Библиотека Swift, поддерживающая как приложение, так и расширение

#ios #swift #frameworks

#iOS #swift #фреймворки

Вопрос:

Я создал библиотеку для своего приложения iOS, но получаю предупреждение linking against a dylib which is not safe for use in application extensions .

Я знаю, что это потому, что я не включил Allow app extension API only свою библиотеку. Поэтому, когда я включаю этот параметр, моя библиотека выдает ошибки там, где я использовал UIApplication.shared .

Я знаю, что не могу использовать shared в своем расширении, и на самом деле в этом нет необходимости, но эта библиотека используется как моим приложением, так и моим расширением.

Итак, вопрос в том, как я могу скомпилировать библиотеку с защитой UIApplication.shared ?

Я уже использую это:

 #if !IS_EXTENSION
    // Cancel the background task
    UIApplication.shared.endBackgroundTask(bgTask)
#endif
  

и установите IS_EXTENSION для Active Compilation Conditions в моей цели расширения, а также IS_EXTENSION=1 для Preprocessor Macros в моем расширении приложения, однако библиотека по-прежнему выдает мне предупреждения в этих строках.

Ответ №1:

1-е решение

Я рекомендую вам использовать внедрение UIApplication в вашу библиотеку. Это может быть достигнуто следующим образом.

Ваш экземпляр библиотеки может выглядеть примерно так:

 class DarrenLib {

    static var shared = DarrenLib()
    
    // User application instance everywhere where needed in your lib.
    private var application: UIApplication?
    
    func setup(_ app: UIApplication) {
        self.application = app
    }
}
  

В качестве примера инъекцию можно выполнить в функции запуска did finish:

 func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
    
    DarrenLib.shared.setup(application)
    
    return true
}
  

Я бы не рекомендовал использовать макросы препроцессора, потому что это зависит от дополнительных требований к настройке, которые невозможно выполнить с помощью исходного кода.

2-е решение

Также, поскольку вы в настоящее время используете целевые приложения и расширения. Вы могли бы создать эти фреймворки:

  1. Djswiftcommon Helpers. В этой структуре будет содержаться исходный код, который будет работать с приложением и целевым расширением.
  2. DJSwiftAppHelpers. Этот фреймворк будет содержать исходный код, который будет содержать конкретные случаи UIKit, такие как общий экземпляр UIApplication.
  3. DJSwiftExtensionHelpers. В этой структуре будет содержаться исходный код, специфичный только для расширений, если таковые имеются.

DJApp будет встраивать фреймворки djswiftcommon Helpers и DJSwiftAppHelpers.

DJNotificationExtension будет встраивать фреймворки djswiftcommon Helpers и DJSwiftExtensionHelpers.

введите описание изображения здесь

Комментарии:

1. Также мне интересно увидеть разные решения.

2. Спасибо за предложение. Мне не нравится идея подготовки библиотеки. На самом деле библиотека — это набор помощников, которые я использовал на протяжении многих лет и, как правило, импортирую во все свои проекты. Есть странный вариант, который не подходит для расширения.

3. Есть ли у вас аналогичный проект, размещенный на GitHub, с которым я мог бы поэкспериментировать?

4. @Darren добавил 2-е решение, но я не смог достичь одного рамочного решения.

5. Знаете ли вы стороннюю библиотеку на GitHub, которая работает с целевыми приложениями и расширениями и использует общий экземпляр UIApplication. Если да. Вы могли бы посмотреть, как они работают.

Ответ №2:

На ваши вопросы есть несколько ответов. В общем, ответ — и ДА, и НЕТ.

Прежде всего:

 #if !IS_EXTENSION
    // Cancel the background task
    UIApplication.shared.endBackgroundTask(bgTask)
#endif
  

Этот подход более или менее в порядке и, вероятно, будет работать, но точно не с Carthage (это то, что я вижу, вы используете в https://github.com/ddaddy/DJSwiftHelpers ), возможно, с помощью Pods или SwiftPM, если вы можете быть уверены, что флаг правильно установлен во время компиляции.

Причина проста, и вы сами на нее ответили — это флаг времени компиляции.

Carthage компилирует фреймворк и создает двоичный файл как часть carthage build/update процесса, так что в итоге вы получаете уже существующий двоичный файл. Этот флаг уже был разрешен и, вероятно, был false, поскольку не установлен. Таким образом, никакие флаги времени сборки в приложении или цели расширения не будут «работать» для библиотек carthage.

Что касается карфагена — я могу предложить добавить дополнительные цели / схемы DJSwiftHelpers.xcodeproj . Аналогично поддержке мультиплатформенности обрабатывается для библиотек carthage с поддержкой нескольких платформ, таких как iOS, tvOS и macOS. Добавьте схему DJSwiftHelpers-extensions , убедитесь, что она создает двоичный файл с использованием правильных флагов сборки, и carthage создаст два отличительных двоичных файла. Свяжите одно с расширениями, а второе с приложениями, и это может сработать.

Другие возможности:

  1. Используйте внедрение UIApplication, как предложено в ответе Ramis

  2. Используйте инъекцию, но на самом деле вы можете определить вторую библиотеку, которая будет использоваться только для целей applicatyion, содержащую один класс ObjC с переопределенным методом ‘ load’, например:

 #import 'yours helpers library'

@implementation SomeClass

  (void)load {
    // This is called once, when module is being loaded,
    // "Invoked whenever a class or category is added to the Objective-C
    // runtime; implement this method to perform class-specific behavior
    // upon loading."
    [YourHelperLibraryClass setupWithApplication:[UIApplication shared]];
}

@end
  

Это немного запутанно, но позволит не подключаться к делегату приложения. Я бы лично не использовал ее для этого.

  1. Используйте cocoapods или SwiftPM — если я правильно помню, код библиотеки будет скомпилирован вместе с приложением, поэтому возможно, что он «увидит» установленные там флаги времени компиляции.