SwiftUI 应用程序生命周期 iOS14 AppDelegate 代码放在哪里?

SwiftUI app life cycle iOS14 where to put AppDelegate code?

现在 AppDelegateSceneDelegate 已从 SwiftUI 中删除,我应该将以前在 SceneDelegateAppDelegate 中的代码放在哪里,Firebase 配置前?

所以我的 AppDelegate:

目前有这段代码

我现在应该把这段代码放在哪里?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    
    FirebaseConfiguration.shared.setLoggerLevel(.min)
    FirebaseApp.configure()
    return true
}

Note the method below will stop cross platform support so should only be used if you are planning on building for iOS only.

It should also be noted that this doesn’t use the SwiftUI lifecycle method, instead it allows you to return to the UIKit lifecycle method.

在 Xcode 12-beta 中创建 SwiftUI 应用程序时,您仍然可以拥有 AppDelegate 和 SceneDelegate。

您只需要确保在创建应用时为生命周期选择了正确的选项。

确保为生命周期选择 UIKit App Delegate,您将获得一个 AppDelegate 和一个 SceneDelegate

这里是 SwiftUI 生命周期的解决方案。使用 Xcode 12b / iOS 14

测试
import SwiftUI
import UIKit

// no changes in your AppDelegate class
class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        print(">> your code here !!")
        return true
    }
}

@main
struct Testing_SwiftUI2App: App {

    // inject into SwiftUI life-cycle via adaptor !!!
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

backup

覆盖 App 中的初始值设定项也有效:

import SwiftUI
import Firebase

@main
struct BookSpineApp: App {
  
  init() {
    FirebaseApp.configure()
  }
  
  var body: some Scene {
    WindowGroup {
      BooksListView()
    }
  }
}

在此处查找更详细的文章:

您还可以将新的 ScenePhase 用于 AppDelegate 和 SceneDelegate 具有的某些代码。喜欢去后台或变得活跃。来自

struct PodcastScene: Scene {
    @Environment(\.scenePhase) private var phase

    var body: some Scene {
        WindowGroup {
            TabView {
                LibraryView()
                DiscoverView()
                SearchView()
            }
        }
        .onChange(of: phase) { newPhase in
            switch newPhase {
            case .active:
                // App became active
            case .inactive:
                // App became inactive
            case .background:
                // App is running in the background
            @unknown default:
                // Fallback for future cases
            }
        }
    }
}

示例信用:https://wwdcbysundell.com/2020/building-entire-apps-with-swiftui/

你根本不应该把那种代码放在应用程序委托中,否则你最终会面临 Massive App Delegate。相反,您应该考虑将代码重构为更有意义的部分,然后将正确的部分放在正确的位置。对于这种情况,您唯一需要做的就是确保代码在应用程序准备就绪后执行这些功能,并且只执行一次。所以 init 方法可能很棒:

@main
struct MyApp: App {
    init() {
        setupFirebase()
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

private extension MyApp {
    func setupFirebase() {
        FirebaseConfiguration.shared.setLoggerLevel(.min)
        FirebaseApp.configure()
    }
}

AppDelegate ?

您可以拥有自己的自定义 class 并将其指定为 delegate。但请注意,它不适用于分配前发生的事件。例如:

class CustomDelegate: NSObject, UIApplicationDelegate {
    static let Shared = CustomDelegate()
}

以后:

UIApplication.shared.delegate = CustomDelegate.Shared

正在观察通知

大多数 AppDelegate 方法实际上是在观察您可以手动观察的通知,而不是定义一个新的 class。例如:

NotificationCenter.default.addObserver(
    self,
    selector: #selector(<#T##@objc method#>),
    name: UIApplication.didBecomeActiveNotification,
    object: nil
)

原生AppDelegate包装

您可以直接将应用程序委托注入 @main 结构:

@UIApplicationDelegateAdaptor(CustomDelegate.self) var appDelegate

注意:使用AppDelegate

请记住,添加 AppDelegate 意味着您将取消默认的多平台支持,您必须手动检查平台。

我看到很多 init 被用作 didFinishLaunching 的解决方案。但是,didFinishLaunchingApp 结构的 init 之后被调用。

解决方案 1

使用在 App 结构中创建的 Viewinit。当调用 App 结构的 body 时,didFinishLaunching 就发生了。

@main
struct MyApp: App {
  @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

  @ViewBuilder
  var body: some Scene {
    WindowGroup {
      MainView(appDelegate: appDelegate)
    }
  }
}

struct MainView: View {
  
  init(appDelegate: AppDelegate) {
    // at this point `didFinishLaunching` is completed
    setup()
  }
}

解决方案 2

我们可以创建一个块来在 didFinishLaunching 被调用时通知我们。这允许在 SwiftUI 世界中保留更多代码(而不是 AppDelegate)。

class AppDelegate: NSObject, UIApplicationDelegate {

  var didFinishLaunching: ((AppDelegate) -> Void)?

  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions
      launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
  ) -> Bool {
    didFinishLaunching?(self)
    return true
  }
}

@main
struct MyApp: App {
  @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

  @ObservedObject private var applicationModel = ApplicationModel()

  // `init` gets called BEFORE `didFinishLaunchingWithOptions`
  init() {

    // Subscribe to get a `didFinishLaunching` call
    appDelegate.didFinishLaunching = { [weak applicationObject] appDelegate in

      // Setup any application code...
      applicationModel?.setup()
    }
  }

  var body: some Scene {
    return WindowGroup {
      if applicationObject.isUserLoggedIn {
        LoggedInView()
      } else {
        LoggedOutView()
      }
    }
  }
}

我还建议使用主 Appinit 方法,因为它似乎可以安全使用(有异议吗?)。

我通常做的,可能对分享有用,是有几个实用程序类型,结合 Builder 模式。

/// An abstraction for a predefined set of functionality,
/// aimed to be ran once, at app startup.
protocol StartupProcess {
    func run()
}

/// A convenience type used for running StartupProcesses.
/// Uses the Builder pattern for some coding eye candy.
final class StartupProcessService {
    init() { }

    /// Executes the passed-in StartupProcess by running it's "run()" method.
    /// - Parameter process: A StartupProcess instance, to be initiated.
    /// - Returns: Returns "self", as a means to chain invocations of StartupProcess instances.
    @discardableResult
    func execute(process: any StartupProcess) -> StartupProcessService {
        process.run()
        return self
    }
}

然后我们有一些流程

struct CrashlyticsProcess: StartupProcess {
    func run() {
        // Do stuff, like SDK initialization, etc.
    }
}

struct FirebaseProcess: StartupProcess {
    func run() {
        // Do stuff, like SDK initialization, etc.
    }
}

struct AppearanceCustomizationProcess: StartupProcess {
    func run() {
        // Do stuff, like SDK initialization, etc.
    }
}

最后,运行 他们

@main
struct TheApp: App {
    init() {
        initiateStartupProcesses()
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

private extension TheApp {
    func initiateStartupProcesses() {
        StartupProcessService()
            .execute(process: ExampleProcess())
            .execute(process: FirebaseProcess())
            .execute(process: AppearanceCustomizationProcess)
    }
}

看起来很不错,超级干净。