Swift 同时支持应用程序和扩展程序的库

Swift library that supports both app and extension

我已经为我的 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

并在我的扩展目标中为 Active Compilation Conditions 设置 IS_EXTENSION,并在我的应用程序扩展中为 Preprocessor Macros 设置 IS_EXTENSION=1,但是库仍然给我警告这些行。

第一个解决方案

我建议您使用 UIApplication 注入到您的库中。可以这样实现。

您的 lib 实例可能与此类似:

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 launching 函数中完成:

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

我不建议使用预处理器宏,因为它取决于额外的设置要求,而这些要求无法通过抛出源代码来完成。

第二种解决方案

此外,您目前正在使用应用程序和扩展程序目标。您可以创建这些框架:

  1. DJSwiftCommonHelpers。在此框架中将包含可在应用程序和扩展目标上运行的源代码。
  2. DJSwiftAppHelpers。该框架将包含源代码,其中将包含 UIKit 特定案例,例如 UIApplication shared instance.
  3. DJSwiftExtensionHelpers。在此框架中将包含仅特定于扩展的源代码(如果有的话)。

DJApp 将嵌入 DJSwiftCommonHelpersDJSwiftAppHelpers 框架。

DJNotificationExtension 将嵌入 DJSwiftCommonHelpersDJSwiftExtensionHelpers 框架。

您的问题有几个答案。一般来说,答案是肯定的和否定的。

首先:

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

这种方法或多或少是可行的,可能会奏效,但肯定不适用于 Carthage(我看到你在 https://github.com/ddaddy/DJSwiftHelpers 中使用的是这种方法),如果可以的话,可能会使用 Pods 或 SwiftPM确保标志在编译时正确设置。

原因很简单,你居然自己回答了——是编译时标志。

Carthage 编译框架并构建二进制文件作为 carthage build/update 过程的一部分,因此您最终得到的是已经存在的二进制文件。这个标志已经被解决,并且可能是错误的,因为没有设置。 因此,对于迦太基库,应用程序或扩展目标中没有构建时间标志 'work'。

至于迦太基 - 我可以建议在 DJSwiftHelpers.xcodeproj 中增加 targets/schemes。对于具有多平台支持的迦太基库,如 iOS、tvOS 和 macOS,处理多平台支持的方式相同。添加一个方案 DJSwiftHelpers-extensions,确保它使用正确的构建标志构建二进制文件,carthage 将生成两个不同的二进制文件。 Link 一个是扩展,其次是应用程序,它可能会起作用。

其他可能性:

  1. 按照 Ramis 回答中的建议使用 UIApplication 注入

  2. 使用注入,但实际上您可以定义第二个库仅用于应用程序目标,包含一个 ObjC class 和重写的“+加载”方法,例如:

#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 - 如果我没记错的话,库代码将与应用程序一起编译,因此它可能会“看到”那里设置的编译时标志。