Проверка во время выполнения на наличие слабосвязанных символов из стороннего фреймворка в Swift
В macOS я использую внешнюю среду (написанную на C), которая должна быть установлена пользователем. В Swift мне нужно во время выполнения проверять, существует ли он, и я не могу использовать #available(), поскольку он предназначен для функций, связанных с ОС, и я пытаюсь отследить внешнюю среду. Кроме того, NSClassFromString() бесполезен, так как это не среда Objective-C.
Я пытался понять, как скопировать эквивалент Objective C для проверки слабо связанного символа, такого как:
if ( anExternalFunction == NULL ) {
// fail graciously
} else {
// do my thing
}
но в Swift это, похоже, не работает: компилятор утверждает, что поскольку функция anExternalFunction не является обязательной, я всегда получаю!= nil, что имеет смысл "Swift", но не помогает мне ни на секунду.
Я нашел два решения, но они воняют мой код, как вы не поверите:
Вариант 1, создать файл Objective-C с функцией isFrameworkAvailable(), выполняющей работу и вызывающей из Swift
Вариант 2, на самом деле проверка библиотеки с помощью следующего кода Swift:
let libHandle = dlopen("/Library/Frameworks/TheLib.framework/TheLib", RTLD_NOW)
if (libHandle != nil) {
if dlsym(libHandle, "anExternalFunction") != nil {
return true
}
}
return false
Мне не удалось заставить вариант 2 хорошо работать с RTLD_DEFAULT, поскольку по какой-то причине он определен в dlfcn.h (-2), но, похоже, не импортируется в Swift (как все отрицательные указатели в этом заголовке: RTLD_NEXT, RTLD_DEFAULT RTLD_SELF, RTLD_MAIN_ONLY). Я нашел этот самый уродливый хак, чтобы заставить его работать:
if dlsym(unsafeBitCast(-2, to: UnsafeMutableRawPointer.self), "anExternalFunction") != nil {
// library installed
}
Так что для варианта 2 мне нужен путь или хак, который я нахожу особенно уродливым (но он работает), а вариант 1 не очень "быстр" и не имеет для меня никакого смысла: как правильно делать это в Swift?
Редактирует: уточнил вопрос, пояснил Вариант 2 лучше.
0 ответов
Я нашел два способа сделать это:
- 1.
if let _ = dlopen("/Library/Frameworks/MyLibrary.framework/MyLibrary", RTLD_NOW) {
print("Can open my library")
}
- 2.
#if canImport(MyLibrary)
print("Can import my library")
#endif
Второй вариант кажется явно лучше с точки зрения удобочитаемости и безопасности типов, но я не уверен, каковы компромиссы между ними.