Swift CLI app 'main' attribute cannot be used in a module that contains top-level code - swift

I am trying to call functions from EventKit in a simple command line tool app. However, some of the functions are async, which results in 'async' call in a function that does not support concurrency. I found this SO question but neither of the given solutions work.
Putting code inside Task {} never runs, and declaring a struct with a static func main() async function wont compile with the error 'main' attribute cannot be used in a module that contains top-level code. I am trying to run a single file test.swift with the following content.
import Foundation
import EventKit
#main
struct Test {
static func main() async {
var reminders = EKEventStore.init()
print("run")
if EKEventStore.authorizationStatus(for: EKEntityType.reminder) != EKAuthorizationStatus.authorized {
do {
var ok = try await reminders.requestAccess(to: EKEntityType.reminder)
print(ok)
} catch {
print("error")
return
}
}
print("authroized")
}
}

At least for me, I had this issue when using #main in main.swift. I renamed main.swift to some random name and the error went away.

This is a bug in Swift. Running with -parse-as-library as an argument for the compiler makes it work.

Related

Swift error "Hashbang line is allowed only in the main file"

I wish to create an interpreted Swift command-line utility. Thus, in Xcode 13.1, I create a new project, and use the template "Command Line Tool". This results in an empty project with a single file, called main.swift.
I want to distribute this command-line utility as a single file, so having the "main.swift" name isn't useful. I rename it to MyCommandlineProject.swift, then add the hashbang line on top, and add code that marks it as being the main file. The contents now look as follows:
#!/usr/bin/swift
import Foundation
#main
struct CLI {
static func main() {
print("Hello, World!")
}
}
Despite the #main annotation, I still get the following compiler error:
Hashbang line is allowed only in the main file
Why?
If you are just writing a script you don't need any of that #main stuff. #main is really only for when you have multiple files that you need to compile into an executable.
For a script, like you are writing, you can just add top level functions and they will get called just as if you were writing in a playground.
#!/usr/bin/swift
import Foundation
print("Hello, World!")
You can execute the script with:
chmod 744 scriptName.swift
./scriptName.swift
Or
swift scriptName.swift
Now if you have multiple files, let's call them First.swift:
import Foundation
#main enum First {
static func main() {
print("First.main")
Second.execute()
}
}
And Second.swift:
import Foundation
enum Second {
static func execute() { print("Second.execute") }
}
You have to prefix your entry point with #main like we did for First. You also have to give it a static main() method.
First you have to compile:
swiftc -o awesome First.swift Second.swift
Then to run:
./awesome

Swift Privileged Helper (XPC Listener) Crashing with Illegal Instruction Error

I’ve created a Swift macOS app which uses SMJobBless to create a helper with escalated privileges. This works fine—the helper gets installed to /Library/Privileged Helper Tools and an accompanying LaunchDaemon gets created in /Library/LaunchDaemons. However, the helper is unable to start successfully. Instead, it crashes with an “Illegal instruction: 4” message.
I’ve prepared the helper to respond to XML connections by implementing the NSXPCListenerDelegate protocol. Here‘s my Helper main.swift code:
import Foundation
class HelperDelegate: NSObject, NSXPCListenerDelegate {
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
newConnection.exportedInterface = NSXPCInterface(with: HelperToolProtocol.self)
newConnection.exportedObject = HelperTool()
newConnection.resume()
return true
}
}
let delegate = HelperDelegate()
let listener = NSXPCListener.service()
listener.delegate = delegate
listener.resume()
The crash occurs on the last line, listener.resume().
I tried to launch the helper app manually from the command line (which is identical to what the LaunchDaemon does) and, again, it crashes with the above error message printed to stdout. I don’t have any more ideas on how to test this for the root cause. My implementation is more than rudimentary, following Apple’s guidlines for implementing XM services. Also, the various posts on SO regarding XML services haven’t helped me in resolving this issue. Has anyone of you tried to create a privileged helper in Swift successfully? BTW, the app is not sandboxed.
For the sake of completeness, here’s the code for the HelperTool class referenced in my HelperDelegate class above:
import Foundation
class HelperTool: NSObject, HelperToolProtocol {
func getVersion(withReply reply: (NSData?) -> ()) {
let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString" as String) as? String ?? "<unknown version>"
let build = Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as? String ?? "<unknown build>"
if let d = "v\(version) (\(build))".data(using: .utf8, allowLossyConversion: false) {
reply(d as NSData)
}
}
}
And finally the HelperToolProtocol:
import Foundation
#objc(HelperToolProtocol) protocol HelperToolProtocol {
func getVersion(withReply: (NSData?) -> ())
}
Thanks for any help!
After days of testing I finally found a solution which makes my XPC helper launch correctly and respond to any messages. The problem lies in the last three lines of the main.swift module which currently read
let listener = NSXPCListener.service()
listener.delegate = delegate
listener.resume()
which, as put in the question, make the helper crash immediately upon the very last line.
I took these lines directly from Apple’s Creating XPC Services documentation. Here’s the documentation for the NSXPCListener resume() function:
If called on the service() object, this method never returns. Therefore, you should call it as the last step inside the XPC service's main function after setting up any desired initial state and configuring the listener itself.
The solution is to not call the NSXPCListener.service() singleton object but rather instantiate a new NSXPCListener object using the init(machServiceName:)initializer passing the same Mach service name that is being used on the main app’s XPC connection. As resume() in this case would resume immediately—thus terminating the helper—you have to put it on the current run loop to have it run indeterminately. Here’s the new, working code:
let listener = NSXPCListener(machServiceName: "Privilege-Escalation-Sample.Helper")
listener.delegate = delegate
listener.resume()
RunLoop.current.run()

Async communication of swift and Javascriptcore

I want to use the Async functionality offered by WKWebView outside web view.
The JS Context option does not provide the Async functionality.
In WKWebView, I write my logic as follows.
func swiftFunc1() {
webView.evaluateJavaScript("jsFunc1(), completionHandler: nil)
}
In javascript code I post a message to swift
function jsFunc1() {
window.webkit.messageHandlers.myMsg.postMessage("call swiftFunc2");
}
The swift code can then call appropriate JS callback as a part of handling message.
But this is dependent upon the webview being the foreground view. If I want to use the JS logic independent of the webview, JSContext is the option. I tried following
func swiftFunc1() {
myCtxt = JSContext()
exportedToJS = exportToJS() //confirms to JSExport and uses #objc
myCtxt.setObject(exportedToJS.self, forKeyedSubscript: "swiftIface")
myFunc = myCtxt.objectForKeyedSubscript("jsFunc1")
myFunc.callWithArguments(nil)
}
Now in javascript code I cannot post a message to swift. If I try to call a swift function as follows, code gets stuck forever.
function jsFunc1() {
swiftIface.swiftFunc2() // This creates a deadklock
}
How can I achieve either of the following without "returning" from the called Javascript function jsFunc1()?
Either post a message to swift so that it can take appropriate action
Or call a swift function so that the appropriate action is taken
Do I understand you right, if you do not want your javscript to terminate after execution?
If I understood you wrong, maybe following helps (I am not at home at Mac to test, but maybe it works if you modify your code as follows).
Option 1: Blocks
The swiftFunc1 could look like this:
func swiftFunc1() {
myCtxt = JSContext()
myCtxt.setObject(unsafeBitCast(swiftFunc2, AnyObject.self), forKeyedSubscript: "swiftFunc2")
exportedToJS = exportToJS() //confirms to JSExport and uses #objc
myCtxt.evaluateScript("swiftFunc2()")
}
Your swiftFunc2 would look like this:
let swiftFunc2: #convention(block) Void -> Void = {
// do something here
}
Your JS code would look like this:
function jsFunc1() {
swiftFunc2();
}
Option 2: JSExport
Your have an exported class which is accessible for all javascript:
import Foundation
import JavaScriptCore
#objc class JavascriptHandler: NSObject, JavascriptHandlerExport {
let context: JSContext = JSContext()
init () {
context.setObject(self, forKeyedSubscript: "MyJSHandler") // set the object name for self accessible in javascript code
}
func swiftFunc1() {
context.evaluateScript("MyJSHandler.swiftFunc2();")
}
func swiftFunc2 () {
// do something here
}
}
Your protocol for the exported class.Here you have to declare all properties and methods you want to use with Javascript.
import Foundation
import JavaScriptCore
#objc protocol JavascriptHandlerExport: JSExport {
func swiftFunc2 ( ) -> Void
}
With this it should be possible for you to call a function from javascript and still let it continue.
You can now access the functions of the class JavascriptHandler from Javascript like this in this example:
MyJSHandler.swiftFunc2();
If you want to seperate the class where your WebView is from the one where your JS logic lies that should also not be a problem. You should also be able to combine the block syntax with the JSExport method.
Let me know if it's not working/behaving as wanted.

How do I execute code once and only once in Swift?

The answers I've seen so far (1, 2, 3) recommend using GCD's dispatch_once thus:
var token: dispatch_once_t = 0
func test() {
dispatch_once(&token) {
print("This is printed only on the first call to test()")
}
print("This is printed for each call to test()")
}
test()
Output:
This is printed only on the first call to test()
This is printed for each call to test()
But wait a minute. token is a variable, so I could easily do this:
var token: dispatch_once_t = 0
func test() {
dispatch_once(&token) {
print("This is printed only on the first call to test()")
}
print("This is printed for each call to test()")
}
test()
token = 0
test()
Output:
This is printed only on the first call to test()
This is printed for each call to test()
This is printed only on the first call to test()
This is printed for each call to test()
So dispatch_once is of no use if we I can change the value of token! And turning token into a constant is not straightforward as it needs to of type UnsafeMutablePointer<dispatch_once_t>.
So should we give up on dispatch_once in Swift? Is there a safer way to execute code just once?
A man went to the doctor, and said "Doctor, it hurts when I stamp on my foot". The doctor replied, "So stop doing it".
If you deliberately alter your dispatch token, then yes - you'll be able to execute the code twice. But if you work around the logic designed to prevent multiple execution in any way, you'll be able to do it. dispatch_once is still the best method to ensure code is only executed once, as it handles all the (very) complex corner cases around initialisation and race conditions that a simple boolean won't cover.
If you're worried that someone might accidentally reset the token, you can wrap it up in a method and make it as obvious as it can be what the consequences are. Something like the following will scope the token to the method, and prevent anyone from changing it without serious effort:
func willRunOnce() -> () {
struct TokenContainer {
static var token : dispatch_once_t = 0
}
dispatch_once(&TokenContainer.token) {
print("This is printed only on the first call")
}
}
Static properties initialized by a closure are run lazily and at most once, so this prints only once, in spite of being called twice:
/*
run like:
swift once.swift
swift once.swift run
to see both cases
*/
class Once {
static let run: Void = {
print("Behold! \(__FUNCTION__) runs!")
return ()
}()
}
if Process.arguments.indexOf("run") != nil {
let _ = Once.run
let _ = Once.run
print("Called twice, but only printed \"Behold\" once, as desired.")
} else {
print("Note how it's run lazily, so you won't see the \"Behold\" text now.")
}
Example runs:
~/W/WhenDoesStaticDefaultRun> swift once.swift
Note how it's run lazily, so you won't see the "Behold" text now.
~/W/WhenDoesStaticDefaultRun> swift once.swift run
Behold! Once runs!
Called twice, but only printed "Behold" once, as desired.
I think the best approach is to just construct resources lazily as needed. Swift makes this easy.
There are several options. As already mentioned, you can initialize a static property within a type using a closure.
However, the simplest option is to define a global variable (or constant) and initialize it with a closure then reference that variable anywhere the initialization code is required to have happened once:
let resourceInit : Void = {
print("doing once...")
// do something once
}()
Another option is to wrap the type within a function so it reads better when calling. For example:
func doOnce() {
struct Resource {
static var resourceInit : Void = {
print("doing something once...")
}()
}
let _ = Resource.resourceInit
}
You can do variations on this as needed. For example, instead of using the type internal to the function, you can use a private global and internal or public function as needed.
However, I think the best approach is just to determine what resources you need to initialize and create them lazily as global or static properties.
For anyone who stumbles on this thread... We ran into a similar situation at Thumbtack and came up with this: https://www.github.com/thumbtack/Swift-RunOnce. Essentially, it lets you write the following
func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated: Bool)
runOnce {
// One-time code
}
}
I also wrote a blog post explaining how the code works, and explaining why we felt it was worth adding to our codebase.
I found this while searching for something similar: Run code once per app install. The above solutions only work within each app run. If you want to run something once across app launches, do this:
func runOnce() {
if UserDefaults.standard.object(forKey: "run_once_key") == nil {
UserDefaults.standard.set(true, forKey: "run_once_key")
/* run once code */
} else {
/* already ran one time */
}
}
If the app is deleted and re-installed, this will reset.
Use NSUbiquitousKeyValueStore for tracking a value across installs and devices as long as user using same appleID.

Array extension called from other module

Array extension methods are unavailable from other modules (for example the XCTest project)
For the sake of simplicity the code below does nothing but it can be used to reproduce the error
import Foundation
extension Array {
mutating func myMethod(toIndex: Int) -> Int! {
// no real code, it's here only to show the problem
return 0
}
}
Calling it from the same module works as expected but from a test class don't
class MyProjectTests: XCTestCase {
func testMoveObjectsFromIndexes1() {
var arr = ["000", "001", "002", "003"]
arr.myMethod(0)
}
}
I think this is correct because the method visibility is restricted to its own module, indeed I obtain the error '[String]' does not have a member named 'myMethod'
I've tried to define the extended method as public as shown below
extension Array {
public mutating func myMethod(toIndex: Int) -> Int! {
// no real code, it's here only to show the problem
return 0
}
}
But I get the compile error 'Extension of generic type 'Array<T>' from a different module cannot provide public declarations'
Until Beta 7 using public solved the problem but under XCode 6.1 (6A1046a) I obtain this error
How can I fix it to run under other modules/projects?
Swift does not allow public extensions currently so you will need to include that extension swift file in your project and put it part of the target.
While not entirely solving the original question, I did find that I could test extension methods in Swift 2.0 (Under XCode 7.0) by importing the module with the #testable directive:
#testable import MyGreatModule