iOS Swift: 从 iOS 框架向 iOS 应用程序发送日志信息

iOS Swift: Send log information to iOS app from iOS framework

我正在尝试了解如何将日志记录信息从我的 iOS 框架发送到我的 iOS 应用程序。在我的 iOS 应用程序中,我有 Crashlytics 设置,并且每当我从应用程序以及我的 iOS 框架获取一些日志时,我想使用 Crashlytics.sharedInstance().recordError(error)

看了很多回答都提到Crashlytics只能在App中初始化一次,不建议在框架中再次初始化。 (例如 Link 1), Link 2,

有没有人有针对此类要求的可行解决方案?

编辑 1(从我需要从中获取错误的框架中添加更多代码)

public final class MyAppSession {

    public fileprivate(set) var isValid: Bool = false

    let itemKey = MyAppSession.MyAppKeychainAccount
    let keychainAccessGroupName = MyAppConfigured?.keyChainGroup

    /**
     Attempts to restore a locally stored session.
     */
    public static func savedSession() -> MyAppSession? {

        do {

            let itemKey = MyAppSession.MyAppKeychainAccount
            let keychainAccessGroupName = MyAppConfigured?.keyChainGroup

            let queryLoad: [String: AnyObject] = [
                kSecClass as String: kSecClassGenericPassword,
                kSecAttrAccount as String: itemKey as AnyObject,
                kSecReturnData as String: kCFBooleanTrue,
                kSecMatchLimit as String: kSecMatchLimitOne,
                kSecAttrAccessGroup as String: keychainAccessGroupName as AnyObject
            ]

            var result: AnyObject?

            let resultCodeLoad = withUnsafeMutablePointer(to: &result) {
                SecItemCopyMatching(queryLoad as CFDictionary, UnsafeMutablePointer([=14=]))
            }

            if resultCodeLoad == noErr {
                if let result = result as? Data {
                    if let arrayFromData = NSKeyedUnarchiver.unarchiveObject(with: result) as? NSDictionary {
                        // Found successfully
                        return MyAppSession(accessToken: (arrayFromData["accessToken"] as? String)!, refreshToken: (arrayFromData["refreshToken"] as? String)!)
                    }
                }
            } else {
                MyApp.log("No Keychain elements present.", logLevel: .verbose)
                MyAppLogger.sendLogs(message: "Logger Test. No Keychain elements present.")
            }

            let keychainData = try Locksmith.loadDataForUserAccount(userAccount: MyAppSession.MyAppKeychainAccount)
            if let accessToken = keychainData?[MyAppSession.accessTokenKeychainKey] as? String,
                let refreshToken = keychainData?[MyAppSession.refreshTokenKeychainKey] as? String {

                MyApp.log("Restored saved session.", logLevel: .verbose)

                return MyAppSession(accessToken: accessToken, refreshToken: refreshToken)

            }
            else {
                return nil
            }

        }
        catch {
            return nil
        }

    }

    init?(accessToken: String, refreshToken: String) {

        // Save credentials to the keychain.
        do {

            try Locksmith.updateData(
                data: [MyAppSession.accessTokenKeychainKey: accessToken as AnyObject, MyAppSession.refreshTokenKeychainKey: refreshToken as AnyObject],
                forUserAccount: MyAppSession.MyAppKeychainAccount)

            if let configuration = MyAppConfigured {
                if configuration.keyChainGroup == "" {
                    let valueData = [MyAppSession.accessTokenKeychainKey: accessToken as AnyObject, MyAppSession.refreshTokenKeychainKey: refreshToken as AnyObject]

                    let data = NSKeyedArchiver.archivedData(withRootObject: valueData)

                    let queryAdd: [String: AnyObject] = [
                        kSecClass as String: kSecClassGenericPassword,
                        kSecAttrAccount as String: itemKey as AnyObject,
                        kSecValueData as String: data as AnyObject,
                        kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked
                    ]

                    let resultCode = SecItemAdd(queryAdd as CFDictionary, nil)

                    if resultCode != noErr {
                        MyApp.log("Keychain groupname empty: \(resultCode)", logLevel: .verbose)
                    }
                    else {
                        MyApp.log("KeyChain Saving Success", logLevel: .verbose)
                    }

                }

                else {
                    let valueData = [MyAppSession.accessTokenKeychainKey: accessToken as AnyObject, MyAppSession.refreshTokenKeychainKey: refreshToken as AnyObject]

                    let data = NSKeyedArchiver.archivedData(withRootObject: valueData)

                    let queryAdd: [String: AnyObject] = [
                        kSecClass as String: kSecClassGenericPassword,
                        kSecAttrAccount as String: itemKey as AnyObject,
                        kSecValueData as String: data as AnyObject,
                        kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked,
                        kSecAttrAccessGroup as String: keychainAccessGroupName as AnyObject
                    ]

                    let resultCode = SecItemAdd(queryAdd as CFDictionary, nil)

                    if resultCode != noErr {
                        MyApp.log("Error saving to Keychain: \(resultCode)", logLevel: .verbose)
                    }
                    else {
                        MyApp.log("KeyChain Saving Success", logLevel: .verbose)
                    }
                }
            }

            isValid = true

            MyApp.log("Created new session.")

        }
        catch {
            return nil
        }

    }
}

只需在 appdelegate 中创建实例,然后在您的框架中使用它们。不要在框架中实例化 Crashlytics,只在那里使用它们。

一种选择是将错误记录委托给您的应用程序。这使它更加灵活。

例如:

protocol MyFrameworkErrorHandlingDelegate: AnyObject {
    func didReceiveError(_ error: Error)
}

然后在你的应用程序中你可以创建一个默认扩展

extension MyFrameworkErrorHandlingDelegate {
     func didReceiveError(_ error: Error) {
         Crashlytics.sharedInstance().recordError(error)
     }
}

这种方法的问题是决定在哪里使用它或在哪里分配委托,这取决于您的框架做什么以及您的应用程序如何使用框架。

编辑:

免责声明:这不是最有效的方法,但会是最直接的方法。

protocol MyFrameworkErrorHandlingDelegate: AnyObject {
    func didReceiveError(_ error: Error)
}

class MyAppLogger {

    static var delegate: MyFrameworkErrorHandlingDelegate?

    static func log(_ error: Error) {
        delegate?.didReceiveError(error)
    }
}

class AppDelegate: UIApplicationDelegate, MyFrameworkErrorHandlingDelegate

稍后在您的 AppDelegate 中

MyAppLogger.delegate = self