Swift: Type-cast of external class → Symbol not found

In a Swift test bundle, I use the following line to get a class object of a class, which is defined in a dynamically loaded bundle (

MyPlugin
). The bundle contains Swift and Objective-C code and is loaded successfully before the following command is executed:
let aClass = NSClassFromString("MyPlugin.BackendAPIManager") as! BackendAPIManager.Type

When the test tries to execute the line, I get the following runtime error:

dyld: lazy symbol binding failed: Symbol not found: __TMaC16MyPlugin17BackendAPIManager

When changing the line to

let aClass = NSClassFromString("MyPlugin.BackendAPIManager") as! NSObject.Type

I can use

let apiURL = aClass.perform(#selector(BackendAPIManager.apiUrl(path:)), with: "/something")

which means that the class is correctly loaded through the bundle, but somehow I cannot access the class through its name. The corresponding symbol is not found and I have no clue what could cause this. Any ideas?

The fact that this fails doesn’t surprise me. The code that works does so entirely via the Objective-C runtime. The Objective-C runtime extracts the require info from the bundle as its loaded, meaning that dyld is not involved at all (except insofar as its notifies the Objective-C runtime when a new image is loaded).

However, for

as! BackendAPIManager.Type
to work you need:
  • The plug-in bundle to export the type’s symbol

  • The test bundle to import the type’s symbol

  • The dyld to match them up

This wouldn’t work even in C. For example, if you have a plug-in bundle that exports a function

foo
, you can’t just call
foo
from your test bundle; you’d have to look it up (using something like
CFBundleGetFunctionPointerForName
) and then call it via a function pointer.

In short, plug-in bundles are not frameworks (-:

As to what you should do, that depends on what your goal is here. Your post just covers the loading stuff, but what do you plan to do after that? Depending on your requirements it might make sense to:

  • Add the

    BackendAPIManager
    source files to your test bundle, so you can test them without dealing with the plug-in
  • Move

    BackendAPIManager
    into a framework that’s linked to by both your test and plug-in bundles
  • Call

    BackendAPIManager
    dynamically, possibly by moving your test code into a ‘self test’ function that lives inside (the test build of) the plug-in bundle

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
Swift: Type-cast of external class → Symbol not found
 
 
Q