Identical code compiling on macOS but not on Linux

Good afternoon,

I'm working on a project that depends on a Swift 4.1.1 ready framework. When compiling my project on my Mac (with Swift 4.1.1) all the dependencies compile and are linked perfectly. However, when testing this in Linux (also with Swift 4.1.1) it seems to complain about casting errors. More precisely:

/home/tiferrei/Desktop/UCLRooms/.build/checkouts/RequestKit.git-7566548506740707509/RequestKit/JSONPostRouter.swift:27:61: error: '[String : AnyObject]?' is not convertible to 'AnyObject?'; did you mean to use 'as!' to force downcast?
                        userInfo[RequestKitErrorKey] = json as AnyObject?
                                                       ~~~~~^~~~~~~~~~~~~
                                                            as!
/home/tiferrei/Desktop/UCLRooms/.build/checkouts/RequestKit.git-7566548506740707509/RequestKit/JSONPostRouter.swift:29:56: error: cannot convert value of type 'String' to type 'AnyObject?' in coercion
                        userInfo[RequestKitErrorKey] = string as AnyObject?
                                                       ^~~~~~
/home/tiferrei/Desktop/UCLRooms/.build/checkouts/RequestKit.git-7566548506740707509/RequestKit/JSONPostRouter.swift:72:61: error: '[String : AnyObject]?' is not convertible to 'AnyObject?'; did you mean to use 'as!' to force downcast?
                        userInfo[RequestKitErrorKey] = json as AnyObject?
                                                       ~~~~~^~~~~~~~~~~~~
                                                            as!
/home/tiferrei/Desktop/UCLRooms/.build/checkouts/RequestKit.git-7566548506740707509/RequestKit/JSONPostRouter.swift:74:56: error: cannot convert value of type 'String' to type 'AnyObject?' in coercion
                        userInfo[RequestKitErrorKey] = string as AnyObject?
                                                       ^~~~~~
/home/tiferrei/Desktop/UCLRooms/.build/checkouts/RequestKit.git-7566548506740707509/RequestKit/Router.swift:75:62: error: cannot convert value of type 'String' to type 'AnyObject?' in coercion
            parameters[configuration.accessTokenFieldName] = accessToken as AnyObject?
                                                             ^~~~~~~~~~~
/home/tiferrei/Desktop/UCLRooms/.build/checkouts/RequestKit.git-7566548506740707509/RequestKit/Router.swift:167:61: error: '[String : AnyObject]?' is not convertible to 'AnyObject?'; did you mean to use 'as!' to force downcast?
                        userInfo[RequestKitErrorKey] = json as AnyObject?
                                                       ~~~~~^~~~~~~~~~~~~
                                                            as!
/home/tiferrei/Desktop/UCLRooms/.build/checkouts/RequestKit.git-7566548506740707509/RequestKit/Router.swift:202:61: error: '[String : AnyObject]?' is not convertible to 'AnyObject?'; did you mean to use 'as!' to force downcast?
                        userInfo[RequestKitErrorKey] = json as AnyObject?
                                                       ~~~~~^~~~~~~~~~~~~
                                                            as!

None of these errors happens in macOS. I even cloned the dependency separately and compiled it, all fine on macOS.

Is this a known issue? How do we even debug something like this?

Thank you,
Tiago

On macOS, String is convertible to AnyObject due to Foundation's automatic bridging with NSString, which is a reference type. On Linux that bridge doesn't exist and so you get the errors you see. I suggest working with real types rather than trying to cast to AnyObject in order to get (I'm guessing) dynamic messaging.

Have you tried using [String: Any] instead of [String: AnyObject]?

1 Like

I was able to fix those errors using Any instead of AnyObject, thanks!

Now I have other weird errors like:

/home/tiferrei/Desktop/UCLRooms/.build/checkouts/UCLKit.git-1403556243857012072/Source/RoomBookings.swift:138:2: error: only classes that inherit from NSObject can be declared @objc
@objc public final class Location: NSObject, Codable {

That again, only happen in Linux.

Well, the @objc stuff needs to be guarded to be Apple-platforms only I guess. You can't use them in your Linux code.

Unfortunately there are still many Swift third-party libraries out there that have no real reason to be Apple-OS-specific but are because of things like this; since the Linux experience for Swift is still much weaker, folks just don't test their code there or consider which parts of the language they need to avoid if they want it to run there.

Sending pull requests to those projects that make them build on Linux when possible would be a great help moving forward, and hopefully those project owners would accept them. (Of course, then the projects have to make sure they're not using any of the bits of Foundation that aren't implemented yet on Linux...)

1 Like

Interesting. So is there a way I can have the class be Obj-C compatible and Linux compatible at the same time? Or would I have to sacrifice Obj-C for Linux support?

Maybe the real question is: Why do you use/need Objective-C here and could you do the same in Swift?

I'd like the framework to be available for people that still use Obj-C, although that's not a requirement.

AFAIK, you'd have to duplicate your types to make one Obj-C visible and the other Linux buildable. There's no way to put the Obj-C references behind platform checks. Personally I find no value in Obj-C compatibility and it can really restrict your usage of Swift. I usually only see it in Swift ports of libraries which had a previous Obj-C version, though not always even then.

1 Like

That would look horrible, alright Obj-C is out then. Thanks!

1 Like

Well, you still need @objc if you want to load your code at runtime on macOS. That part is sadly missing from the Swift story so far.

Alright here's an interesting one: I've managed to make the package Linux compatible, so now when I build my project everything works as expected, sweet. However, if I try to git clone just the dependency, and I try to swift build it, it fails because it doesn't have a Linux Main file.

Am I the only one confused by this?

I will note that you do not need @objc on a class declaration, ever, if you subclass another Objective-C-exposed class (unless you're trying to set the class's Objective-C runtime name). The only times you should be writing @objc explicitly is on protocols and on methods that you specifically want to expose to Objective-C, and both of those wouldn't work on Linux anyway.

1 Like

Any and AnyObject should be avoided in favor of generics where possible. Requires a bit more work on your part, but always worth it in the long run.

2 Likes

So now I can compile and use the dependency in a project but I can't compile it directly on Linux because of error: missingLinuxMain. Is there any way I can set it up to just treat everything as both Linux and macOS?

IIRC, the missing LinuxMain error is related to the swift file required for testing on linux.

Look that up. I'd help you more but atm I haven't slept for like 21 hours so I'm really conserving energy here. After I finish this presentation I'm gonna give, I'll come back and try to help some more, if you're still trying to make that work.

1 Like

See swift - Build error: missingLinuxMain - Stack Overflow

If you have test modules, you need a LinuxMain.swift file (for now).

1 Like

Thank you @felix91gr and @svanimpe, you're help and time is greatly appreciated, I was able to make it work on Linux by adding the LinuxMain file and following @svanimpe's link.
I did however hit another roadblock: URLSession seems to be missing? My code compiles and Unit tests pass on Xcode, but not on the CLI, on neither Linux nor macOS. On Linux I was kind of expecting it as shown in this link: ubuntu - Swift 3 preview 2 linux error: use of unresolved identifier - Stack Overflow However, I don't see why this also applies to builds on macOS, has URLSession not been implemented at all in Open Source Foundation?
The specific error I'm referring to is:

/Users/tiferrei/Developer/RequestKit/Tests/RequestKitTests/TestInterface.swift:18:53: error: use of unresolved identifier 'URLSession'
    func postJSON(_ session: RequestKitURLSession = URLSession.shared, completion: @escaping (_ response: Response<[String: AnyObject]>) -> Void) -> URLSessionDataTaskProtocol? {
1 Like