Error Casting Fails When Dependency Integrated As Static Library

So this is a weird one.

I'm building a network SDK in Xcode 11.1, using CocoaPods and a few dependencies, like Alamofire. In writing my tests I need to cast from the Error value coming out of my network calls to AFError, returned by Alamofire. However, no attempt at such a cast is succeeding, despite having done the same cast hundreds of times in the past, including in Alamofire's tests. In my desperation I remember that I originally had CocoaPods set up to integrate my dependencies statically. I switched them to use dynamic frameworks and suddenly the casts work and my tests pass! Can anyone explain this behavior?

Additionally, Xcode 11's debugger was extremely broken here. Any attempt to print the result of a cast, like error as? AFError would result in a debugger error:

error: Couldn't materialize: can't get size of type
error: errored out in DoExecute, couldn't PrepareToExecuteJITExpression

This debugger error is happening for many Alamofire types, not just AFError, making debugging the library very frustrating. Any suggestions here?

1 Like

If you include a library MyLib which includes your type MyType statically, they become distinct types in each module where they are included. If FrameworkA and FrameworkB both link to your library MyLib statically, they both get a copy and the types FrameworkA.MyLib.MyType and FrameworkB.MyLib.MyType are distinct and are completely "not equal".
So you also can not cast between them as for all intents and purposes the runtime doesn't see them as the same.
The only solution to this is to link your library dynamically, so both frameworks share the type.

That's even more prevalent now with SPM packages, which automatically usually produce static libraries. A lot of times it is important to actually change this to yield a dynamic library, when you are working with an app and a framework in Xcode which should share an SPM package. Otherwise you run into this exact same problem.

As for the Xcode error: That's just the way Swift developers have to debug right now :stuck_out_tongue:

I'm sorry, but that explanation doesn't make any sense to me. First, there's only a single dependency relationship in play here, SDK -> Alamofire, so there's only one version of AFError to cast to. Second, this is a runtime failure, where every attempt at error as? AFError fails, even when the debugger confirms it's actually an AFError. Third, if this is expected behavior, wouldn't there be a warning or other diagnostic from the compiler or linker in that case? It seems unbelievable to me that such silent failures are both expected and undiagnosed.

Could this be related to SR-6004 (even though it says it was resolved)?

Can you try passing -all_load to the linker to see if that makes the problem go away? That flag isn't a great solution for binary-size reasons, but if it works, it at least narrows down what's going on.

Adding -all_load to the "Other Linker Flags" of my framework target and test target seems to have no effect with static dependencies.

FYI I also encountered the very similar issue.

I just witnessed the following dependency graph causing multiple symbols:

1. static <- dynamic <- app
2. static <- app

Current solution is to make "static" back to "dynamic",
and in future I will try making all "dynamic" into "static".