You don't need the hashbang and sh, just use /usr/bin/swift to execute Swift code from the script field. There seem to be some issue in that XCode does not recognize that the code is Swift, so it will highlight it as a shell script. It also gives me the following warning: using sysroot for 'iPhoneOS' but targeting 'MacOSX'. No idea how to solve that, but the good news is that it works.
Ah gotcha. I guess the reason it worked for me is that I ran the script form an Aggregate target, instead of the main iOS target. I also tried executing a standalone Swift script from shell in Run Script, but that provided a whole bunch of errors also pointing to the same issue.
However, digging deeper it looks like using xcrun would provide the answer. When setting Shell to /usr/bin/xcrun --sdk macosx swift the script is run without warnings, even when run during an iOS build for a physical device.
If your script exits with a non-zero exit code and prints to stderr using the format described in the below quote, Xcode will interpret and display it as an error/warning.
This is from a SO answer (perhaps someone knows where the official documentation for this is?):
In shell build phases you can write to stderr using the following format:
It's the same format gcc uses to show errors. The filename:linenumber part can be omitted. Depending on the mode (error, warn, note), Xcode will show your message with a red or yellow badge.
If you include an absolute file path and a line number (if the error occurred in a file), double clicking the error in the build log lets Xcode open the file and jumps to the line, even if it is not part of the project. Very handy.
That is correct if I remember correctly (I used this functionality successfully in a Swift (build phase run) script that processed Swift source files (a bit like gyb but in Swift) before compilation some years ago).
Thanks @Jens, I am using a swift script file, so what command should I invoke ? (Pardon my ignorance I am new to scripting and scripting in Swift).
Fatal error works and does throw an error (with a red exclamation mark) in Xcode. Just wanted to know if that is how error handling is done for swift scripts or there is some other preferred way.
For example the following run script works for me (resulting in Xcode showing an error which points to the first line and column of my command line app's main.swift):
import Foundation
struct StderrOutputStream: TextOutputStream {
mutating func write(_ string: String) { fputs(string, stderr) }
}
var standardError = StderrOutputStream()
print("/Users/me/path_to_my_projects_main.swift:1:1: error: My error message", to: &standardError)
exit(1)
(Note that line and column number is given there, which the quote in my previous reply failed to mention.)
Sure, there's no reason to not use fatalError in cases where it works.
Using print to stderr with custom file path etc is necessary if your script is meant to signal an error in another file than the script itself (ie a file that the script is processing).
There's a bit of official documentation in Xcode's Help (Help > Xcode Help) under "Write a build phase script", but it just mentions "error:" and "warning:" (the minimum needed to get diagnostics to appear in the Issue Navigator) and nothing about the file/line syntax. Can you file a bug at https://bugreport.apple.com to document that properly?
This is very interesting just printing it with a prefix of error: and warning: causes it to be caught. Thank you so much guys, learnt quite a lot about scripting.
It would be really cool if swift.org blog could have an article on how swift could be used in scripts.
Yeah, the xcrun --sdk macosx part (to prevent compilation for iOS) is presumably because Xcode is providing project environment variables to the script. Those end up misdirecting the compiler that gets launched within the script phase. It should not be necessary when your project is targeting the host platform (macOS) anyway.