Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Framework does not include symbols from extensions to generic structs

I am having trouble linking my framework with code that takes advantage of that framework. Specifically, the linker isn't able to find the symbols for extensions for generics structs.

This is what one of the extensions looks like for Optional:

extension Optional {
    /// Unwrap the value returning 'defaultValue' if the value is currently nil
    func or(defaultValue: T) -> T {
        switch(self) {
            case .None:
                return defaultValue
            case .Some(let value):
                return value
        }
    }
}

This method works great in a playground or in an app if the code is compiled within the main part of the app. However, when I try to compile this into a Framework, apps (and even the tests for the framework) produce the following linker error:

Undefined symbols for architecture i386: "__TFSq2orU__fGSqQ__FQQ", referenced from: __TFC18SwiftPlusPlusTests27Optional_SwiftPlusPlusTests13testOrWithNilfS0_FT_T_ in Optional+SwiftPlusPlusTests.o

Similar methods like the one following, link fine (notice, it is not on a generic)

extension String {
    /// Returns a string by repeating it 'times' times
    func repeat(times: Int) -> String {
        var result = ""
        for i in 0..times {
            result += self
        }
        return result
    }
}

There are two other extensions within my repository on github: SwiftPlusPlus that also do not link (both on generic strucs). You will reproduce the errors if you pull the latest commit, build the framework, and then try to run the unit tests.

So far I have tried to run "strings" on the outputted framework and intermediate files and I do not see the symbols for these extensions but I do see the symbols for the repeat method extension on String. So it doesn't even seem to be compiling them into the library.

Does anyone know why the symbols are not defined in the framework?

Edit

  • Here is a link to my Optional Extension
  • Here is a link to the test file that causes the linker error when trying to compile the test target
like image 875
drewag Avatar asked Jun 12 '14 02:06

drewag


2 Answers

I posted on the Apple Developer forums and an Apple employee responded that this is a known bug.

It looks like the compiler gets the mangled symbol names of methods in generic extensions wrong when they live in a different framework.

like image 126
drewag Avatar answered Nov 10 '22 08:11

drewag


In case you are looking for a temporary fix, you can wrap the extension in a class method:

// In your framework
public class OptionalOperator {
    public class func or<T>(optional:Optional<T>,defaultValue:T) ->T {
        return optional.or(defaultValue)
    }
}

// Outside the framework
var maybeText:String?
let text = OptionalOperator.or(maybeText, defaultValue: "Apple, please fix this")

Of course, this is not ideal and defeats the purpose of extensions. So if you plan on calling this method frequently, we could overload/define an operator.

// In your framework
infix operator ||| {}

public func |||<T>(left:Optional<T>, right:T) -> T {
    return left.or(right)
}

// Outside the framework
var maybeText:String?
let text = maybeText ||| "Apple, please fix this"

In my case, I have multiple applications using the framework, so I'd like to keep the method implementation inside the framework. However, overloading an operator (or just using a global function) would be awkward, so I have to go with the first option until that bug is fixed.

Hope this helps.

UPDATE

Funny thing is that Swift already has an operator for that (??).

var maybeText:String?
let text = maybeText ?? "Nice!"

It's called - Nil Coalescing Operator

like image 30
Max Avatar answered Nov 10 '22 07:11

Max