On macOS, I use an external framework (written in C) that must be installed by the user. In Swift, I need to check at runtime if it exists, and I can't use #available() since it is for OS-related features, and I am trying to track down an external framework. Also, NSClassFromString() is not useful since it is not an Objective-C framework.
I have been trying to understand how to replicate the Objective-C equivalent for checking for a weakly-linked symbol such as:
if ( anExternalFunction == NULL ) {
// fail graciously
} else {
// do my thing
}
but in Swift, this does not seem to work: the compiler states that since anExternalFunction is not optional, I will always get != nil, which make "Swift sense" but does not help me one bit.
I have found two solutions, but they stink up my code like you wouldn't believe:
Option 1, create an Objective-C file with a function called isFrameworkAvailable() doing the work, and calling from Swift
Option 2, actually checking for the library with the following Swift code:
let libHandle = dlopen("/Library/Frameworks/TheLib.framework/TheLib", RTLD_NOW)
if (libHandle != nil) {
if dlsym(libHandle, "anExternalFunction") != nil {
return true
}
}
return false
I have been unable to get Option 2 to work nicely with RTLD_DEFAULT since for some reason, it is defined in dlfcn.h (-2) but does not seem to be imported in Swift (like all negative pointers in that header: RTLD_NEXT, RTLD_DEFAULT, RTLD_SELF, RTLD_MAIN_ONLY). I have found this ugliest hack to make it work:
if dlsym(unsafeBitCast(-2, to: UnsafeMutableRawPointer.self), "anExternalFunction") != nil {
// library installed
}
So for option 2, I require the path or a hack, which I find particularly ugly (but it works) and Option 1 is not very "Swifty" and makes little sense to me: what is the proper way of doing this in Swift?
Edits: clarified question, explained Option 2 better.