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

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

Related

Mac Command Line Tool Swift Unit Tests

I want to unit test a command line tool.
I go through the following progress
Create a new project using the macOS > Command Line Tool template. Call it "MyProject"
import Foundation
class MyCode {
func hello() {
print("hello")
}
}
let code = MyCode()
code.hello()
Choose File > New > Target.
Select the macOS Unit Testing Bundle template. Call it "MyTests"
import XCTest
#testable import MyProject
class MyTests: XCTestCase {
func testCode() {
let sol = MyCode()
sol.hello()
}
}
Switch to test target.
Build failed.
How can I test this?

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

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.

Why does my Swift code try and call the wrong init method?

I have the following Swift class:
public class Tree {
var obj_ : OpaquePointer
public init(fromCPtr obj:OpaquePointer) {
obj_ = obj
}
convenience init(_ levels:Int32) {
var rv : OpaquePointer?
Tree_Tree_create(levels, &rv)
self.init(fromCPtr:rv!)
}
deinit
{
Tree_Tree_dispose(obj_)
}
}
And the following test code:
import Foundation
import Tree
let t = Tree(4)
print(t.data())
I compile the Tree module with:
swiftc Tree.swift -import-objc-header Tree-Bridging-Header.h -L. -lTree_c -emit-module -emit-module-path build/Tree.swiftmodule -emit-library -module-name Tree -o build/Tree
This works without errors or warnings.
I compile my test code with:
swiftc TestTree.swift -Ibuild/
and get the following error:
TestTree.swift:4:14: error: cannot convert value of type 'Int' to expected argument type 'OpaquePointer'
let t = Tree(4)
Why is Swift selecting the pointer overload? As I understand things, Swift should not select the pointer init method unless I call init(fromCPtr some_pointer).
Using Tree(Int32(4)) in the test makes no difference.
How can I get Swift to select the correct init method?
convenience init(_ levels:Int32) {
This is not a public initializer. Add public to make it visible outside the module. The default access level is internal, which is only visible inside the module.

Instantiating objects(Entry point)

I am new to swift and Xcode. I have got two swift files which contain's two classes A and B. I need to instantiate an object of Class A to start the program.How would I do that in swift?. I am familiar with Java and I am aware of the main program where the execution starts. How would I do the same in swift? I am not trying to create an iOS or Xcode App.
I have played around with playgrounds and everything works if you put it in a single file. I have created a package and added the swift files. The build is successful but there is no output. The code below is the Source code that I would use in a playground to start the program. How would I do it in an xcode project?
var b = Shop()
b.IssueOrder(quantity: 100, code: "R12")
b.IssueOrder(quantity: 156, code: "L09")
b.IssueOrder(quantity: 13, code: "T58")
I was wondering if there are any swift veterans could help me ??
Look at this structure.
I have created User.swift file in source folder in playground and make use of it in playground.
Class in Source folder : User.swift
import Foundation
public class User {
var firstName : String?
var lastName : String?
public init(_ firstName : String?, _ lastName : String?) {
self.firstName = firstName
self.lastName = lastName
}
public func getFullName() -> String?{
return (firstName ?? "" ) + (lastName ?? "")
}
}
Use it in playground :
//: Playground - noun: a place where people can play
import UIKit
var str = "Hello, playground"
var user = User("Sagar", "Gondaliya")
print("My FullName : \(user.getFullName()!)")
Output: My FullName : Sagar Gondaliya
Just make sure that class and its method which you want to access should be marked as public.
Turns out this can easily be done using Swift package manager via command line. There are very little resources out there so I thought I will go ahead and write the answer myself.
Create a new Directory and cd into it.
Create a new package using the following command. This will create the project structure for you. It has a main.swift file which will serve as the entry point for the application
swift package init --type executable
Build the project
swift build
Execute the project by running the below command where Project is the name of the directory you created(becomes the name of the project)
.build/debug/Project
You will see the output of the source code in the main.swift file in the terminal

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