From babcd1158014eb64feb5c2bd3eb88a05ba68f001 Mon Sep 17 00:00:00 2001 From: Basem Emara Date: Mon, 28 Oct 2019 21:39:51 -0400 Subject: [PATCH 1/7] Add pluggable scene delegate --- README.md | 39 ++++++-- .../ApplicationPluggableDelegate.swift | 46 +++++---- .../ExtensionPluggableDelegate.swift | 18 ++-- .../Application/ScenePluggableDelegate.swift | 96 +++++++++++++++++++ 4 files changed, 157 insertions(+), 42 deletions(-) create mode 100644 Sources/ZamzamCore/Application/ScenePluggableDelegate.swift diff --git a/README.md b/README.md index 19c9c1a5..e9c0ee4b 100644 --- a/README.md +++ b/README.md @@ -622,21 +622,16 @@ someStruct.isRunningOnSimulator -> false ```swift // Subclass and install to pass lifecycle events to loaded plugins @UIApplicationMain -class AppDelegate: ApplicationPluginDelegate { +class AppDelegate: ApplicationPluggableDelegate { - private(set) lazy var plugins: [ApplicationPlugin] = [ + override func plugins() -> [ApplicationPlugin] {[ LoggerPlugin(), NotificationPlugin() - ] - - override init() { - super.init() - install(plugins) - } + ]} } ``` ```swift -// Each application plugin has access to the AppDelegate lifecycle events +// Each application plugin has access to the `AppDelegate` lifecycle events final class LoggerPlugin: ApplicationPlugin { private let log = Logger() @@ -659,6 +654,32 @@ final class LoggerPlugin: ApplicationPlugin { } } ``` + +> Split up `SceneDelegate` into plugins: +```swift +// Subclass and install to pass lifecycle events to loaded plugins +class SceneDelegate: ScenePluggableDelegate { + + override func plugins() -> [ScenePlugin] {[ + LoggerPlugin(), + NotificationPlugin() + ]} +} +``` +```swift +// Each application plugin has access to the `SceneDelegate` lifecycle events +final class LoggerPlugin: ScenePlugin { + private let log = Logger() + + func sceneWillEnterForeground() { + log.info("Scene will enter foreground.") + } + + func sceneDidEnterBackground() { + log.info("Scene did enter background.") + } +} +```
diff --git a/Sources/ZamzamCore/Application/ApplicationPluggableDelegate.swift b/Sources/ZamzamCore/Application/ApplicationPluggableDelegate.swift index 2adbf6ef..8dd18873 100644 --- a/Sources/ZamzamCore/Application/ApplicationPluggableDelegate.swift +++ b/Sources/ZamzamCore/Application/ApplicationPluggableDelegate.swift @@ -17,7 +17,7 @@ import UIKit /// @UIApplicationMain /// class AppDelegate: ApplicationPluggableDelegate { /// -/// override func application() -> [ApplicationPlugin] {[ +/// override func plugins() -> [ApplicationPlugin] {[ /// LoggerPlugin(), /// NotificationPlugin() /// ]} @@ -50,17 +50,17 @@ open class ApplicationPluggableDelegate: UIResponder, UIApplicationDelegate { public var window: UIWindow? /// List of application plugins for binding to `AppDelegate` events - public private(set) lazy var plugins: [ApplicationPlugin] = { application() }() + public private(set) lazy var pluginInstances: [ApplicationPlugin] = { plugins() }() public override init() { super.init() // Load lazy property early - _ = plugins + _ = pluginInstances } /// List of application plugins for binding to `AppDelegate` events - open func application() -> [ApplicationPlugin] {[]} // Override + open func plugins() -> [ApplicationPlugin] {[]} // Override } extension ApplicationPluggableDelegate { @@ -68,7 +68,7 @@ extension ApplicationPluggableDelegate { open func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { // Ensure all delegates called even if condition fails early //swiftlint:disable reduce_boolean - plugins.reduce(true) { + pluginInstances.reduce(true) { $0 && $1.application(application, willFinishLaunchingWithOptions: launchOptions) } } @@ -76,7 +76,7 @@ extension ApplicationPluggableDelegate { open func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Ensure all delegates called even if condition fails early //swiftlint:disable reduce_boolean - plugins.reduce(true) { + pluginInstances.reduce(true) { $0 && $1.application(application, didFinishLaunchingWithOptions: launchOptions) } } @@ -85,41 +85,49 @@ extension ApplicationPluggableDelegate { extension ApplicationPluggableDelegate { open func applicationWillEnterForeground(_ application: UIApplication) { - plugins.forEach { $0.applicationWillEnterForeground(application) } + pluginInstances + .compactMap { $0 as? ScenePlugin } + .forEach { $0.sceneWillEnterForeground() } } open func applicationDidEnterBackground(_ application: UIApplication) { - plugins.forEach { $0.applicationDidEnterBackground(application) } + pluginInstances + .compactMap { $0 as? ScenePlugin } + .forEach { $0.sceneDidEnterBackground() } } open func applicationDidBecomeActive(_ application: UIApplication) { - plugins.forEach { $0.applicationDidBecomeActive(application) } + pluginInstances + .compactMap { $0 as? ScenePlugin } + .forEach { $0.sceneDidBecomeActive() } } open func applicationWillResignActive(_ application: UIApplication) { - plugins.forEach { $0.applicationWillResignActive(application) } + pluginInstances + .compactMap { $0 as? ScenePlugin } + .forEach { $0.sceneWillResignActive() } } } extension ApplicationPluggableDelegate { open func applicationProtectedDataWillBecomeUnavailable(_ application: UIApplication) { - plugins.forEach { $0.applicationProtectedDataWillBecomeUnavailable(application) } + pluginInstances.forEach { $0.applicationProtectedDataWillBecomeUnavailable(application) } } open func applicationProtectedDataDidBecomeAvailable(_ application: UIApplication) { - plugins.forEach { $0.applicationProtectedDataDidBecomeAvailable(application) } + pluginInstances.forEach { $0.applicationProtectedDataDidBecomeAvailable(application) } } } extension ApplicationPluggableDelegate { open func applicationWillTerminate(_ application: UIApplication) { - plugins.forEach { $0.applicationWillTerminate(application) } + pluginInstances.forEach { $0.applicationWillTerminate(application) } } open func applicationDidReceiveMemoryWarning(_ application: UIApplication) { - plugins.forEach { $0.applicationDidReceiveMemoryWarning(application) } + pluginInstances.forEach { $0.applicationDidReceiveMemoryWarning(application) } } } @@ -128,11 +136,6 @@ public protocol ApplicationPlugin { func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool - func applicationWillEnterForeground(_ application: UIApplication) - func applicationDidEnterBackground(_ application: UIApplication) - func applicationDidBecomeActive(_ application: UIApplication) - func applicationWillResignActive(_ application: UIApplication) - func applicationProtectedDataWillBecomeUnavailable(_ application: UIApplication) func applicationProtectedDataDidBecomeAvailable(_ application: UIApplication) @@ -146,11 +149,6 @@ public extension ApplicationPlugin { func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { return true } func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { return true } - func applicationWillEnterForeground(_ application: UIApplication) {} - func applicationDidEnterBackground(_ application: UIApplication) {} - func applicationDidBecomeActive(_ application: UIApplication) {} - func applicationWillResignActive(_ application: UIApplication) {} - func applicationProtectedDataWillBecomeUnavailable(_ application: UIApplication) {} func applicationProtectedDataDidBecomeAvailable(_ application: UIApplication) {} diff --git a/Sources/ZamzamCore/Application/ExtensionPluggableDelegate.swift b/Sources/ZamzamCore/Application/ExtensionPluggableDelegate.swift index 506efd95..d5cecaf1 100644 --- a/Sources/ZamzamCore/Application/ExtensionPluggableDelegate.swift +++ b/Sources/ZamzamCore/Application/ExtensionPluggableDelegate.swift @@ -15,7 +15,7 @@ import WatchKit /// /// class ExtensionDelegate: ExtensionPluggableDelegate { /// -/// override func application() -> [ExtensionPlugin] {[ +/// override func plugins() -> [ExtensionPlugin] {[ /// LoggerPlugin(), /// LocationPlugin() /// ]} @@ -49,42 +49,42 @@ import WatchKit open class ExtensionPluggableDelegate: NSObject, WKExtensionDelegate { /// List of application plugins for binding to `ExtensionDelegate` events - public private(set) lazy var plugins: [ExtensionPlugin] = { application() }() + public private(set) lazy var pluginInstances: [ExtensionPlugin] = { plugins() }() public override init() { super.init() // Load lazy property early - _ = plugins + _ = pluginInstances } /// List of application plugins for binding to `ExtensionDelegate` events - open func application() -> [ExtensionPlugin] {[]} // Override + open func plugins() -> [ExtensionPlugin] {[]} // Override } public extension ExtensionPluggableDelegate { func applicationDidFinishLaunching() { - plugins.forEach { $0.applicationDidFinishLaunching(.shared()) } + pluginInstances.forEach { $0.applicationDidFinishLaunching(.shared()) } } } public extension ExtensionPluggableDelegate { func applicationDidBecomeActive() { - plugins.forEach { $0.applicationDidBecomeActive(.shared()) } + pluginInstances.forEach { $0.applicationDidBecomeActive(.shared()) } } func applicationWillResignActive() { - plugins.forEach { $0.applicationWillResignActive(.shared()) } + pluginInstances.forEach { $0.applicationWillResignActive(.shared()) } } func applicationWillEnterForeground() { - plugins.forEach { $0.applicationWillEnterForeground(.shared()) } + pluginInstances.forEach { $0.applicationWillEnterForeground(.shared()) } } func applicationDidEnterBackground() { - plugins.forEach { $0.applicationDidEnterBackground(.shared()) } + pluginInstances.forEach { $0.applicationDidEnterBackground(.shared()) } } } diff --git a/Sources/ZamzamCore/Application/ScenePluggableDelegate.swift b/Sources/ZamzamCore/Application/ScenePluggableDelegate.swift new file mode 100644 index 00000000..42222b2d --- /dev/null +++ b/Sources/ZamzamCore/Application/ScenePluggableDelegate.swift @@ -0,0 +1,96 @@ +// +// File.swift +// +// +// Created by Basem Emara on 2019-10-28. +// + +#if os(iOS) +import UIKit + +/// Subclassed by the `SceneDelegate` to pass lifecycle events to loaded plugins. +/// +/// The scene plugins will be processed in sequence after calling `plugins() -> [ScenePlugin]`. +/// +/// class SceneDelegate: ScenePluggableDelegate { +/// +/// override func plugins() -> [ScenePlugin] {[ +/// LoggerPlugin(), +/// NotificationPlugin() +/// ]} +/// } +/// +/// Each scene plugin has access to the `SceneDelegate` lifecycle events: +/// +/// final class LoggerPlugin: ScenePlugin { +/// private let log = Logger() +/// +/// func sceneWillEnterForeground() { +/// log.info("Scene will enter foreground.") +/// } +/// +/// func sceneDidEnterBackground() { +/// log.info("Scene did enter background.") +/// } +/// } +@available(iOS 13.0, *) +open class ScenePluggableDelegate: UIResponder, UIWindowSceneDelegate { + public var window: UIWindow? + + /// List of scene plugins for binding to `SceneDelegate` events + public private(set) lazy var pluginInstances: [ScenePlugin] = { plugins() }() + + public override init() { + super.init() + + // Load lazy property early + _ = pluginInstances + } + + /// List of scene plugins for binding to `SceneDelegate` events + open func plugins() -> [ScenePlugin] {[]} // Override +} + +@available(iOS 13.0, *) +public extension ScenePluggableDelegate { + + func sceneWillEnterForeground(_ scene: UIScene) { + pluginInstances.forEach { $0.sceneWillEnterForeground() } + } + + func sceneDidEnterBackground(_ scene: UIScene) { + pluginInstances.forEach { $0.sceneDidEnterBackground() } + } + + func sceneDidBecomeActive(_ scene: UIScene) { + pluginInstances.forEach { $0.sceneDidBecomeActive() } + } + + func sceneWillResignActive(_ scene: UIScene) { + pluginInstances.forEach { $0.sceneWillResignActive() } + } + + func sceneDidDisconnect(_ scene: UIScene) { + pluginInstances.forEach { $0.sceneDidDisconnect() } + } +} + +/// Conforming to an scene module and added to `SceneDelegate.plugins()` will trigger events. +public protocol ScenePlugin { + func sceneWillEnterForeground() + func sceneDidEnterBackground() + func sceneDidBecomeActive() + func sceneWillResignActive() + func sceneDidDisconnect() +} + +// MARK: - Optionals + +public extension ScenePlugin { + func sceneWillEnterForeground() {} + func sceneDidEnterBackground() {} + func sceneDidBecomeActive() {} + func sceneWillResignActive() {} + func sceneDidDisconnect() {} +} +#endif From 558a1a3bcf0242b75e614aa3628174ee97e222b2 Mon Sep 17 00:00:00 2001 From: Basem Emara Date: Mon, 28 Oct 2019 21:41:45 -0400 Subject: [PATCH 2/7] Add console logger --- Sources/ZamzamCore/Logging/LoggerAPI.swift | 96 +++++++++++++++++++ Sources/ZamzamCore/Logging/LoggerWorker.swift | 40 ++++++++ .../Logging/Stores/LoggerConsoleStore.swift | 42 ++++++++ 3 files changed, 178 insertions(+) create mode 100644 Sources/ZamzamCore/Logging/LoggerAPI.swift create mode 100644 Sources/ZamzamCore/Logging/LoggerWorker.swift create mode 100644 Sources/ZamzamCore/Logging/Stores/LoggerConsoleStore.swift diff --git a/Sources/ZamzamCore/Logging/LoggerAPI.swift b/Sources/ZamzamCore/Logging/LoggerAPI.swift new file mode 100644 index 00000000..3c153533 --- /dev/null +++ b/Sources/ZamzamCore/Logging/LoggerAPI.swift @@ -0,0 +1,96 @@ +// +// Loggable.swift +// PrayCore +// +// Created by Basem Emara on 2019-06-11. +// Copyright © 2019 Zamzam Inc. All rights reserved. +// + +import Foundation + +public protocol LoggerStore: AppInfo { + + /** + Log something generally unimportant (lowest priority; not written to file) + + - parameter message: Description of the log. + - parameter includeMeta: If true, will append the meta data to the log. + - parameter path: Path of the caller. + - parameter function: Function of the caller. + - parameter line: Line of the caller. + */ + func verbose(_ message: String, path: String, function: String, line: Int, context: [String: Any]?) + + /** + Log something which help during debugging (low priority; not written to file) + + - parameter message: Description of the log. + - parameter includeMeta: If true, will append the meta data to the log. + - parameter path: Path of the caller. + - parameter function: Function of the caller. + - parameter line: Line of the caller. + */ + func debug(_ message: String, path: String, function: String, line: Int, context: [String: Any]?) + + /** + Log something which you are really interested but which is not an issue or error (normal priority) + + - parameter message: Description of the log. + - parameter includeMeta: If true, will append the meta data to the log. + - parameter path: Path of the caller. + - parameter function: Function of the caller. + - parameter line: Line of the caller. + */ + func info(_ message: String, path: String, function: String, line: Int, context: [String: Any]?) + + /** + Log something which may cause big trouble soon (high priority) + + - parameter message: Description of the log. + - parameter includeMeta: If true, will append the meta data to the log. + - parameter path: Path of the caller. + - parameter function: Function of the caller. + - parameter line: Line of the caller. + */ + func warn(_ message: String, path: String, function: String, line: Int, context: [String: Any]?) + + /** + Log something which will keep you awake at night (highest priority) + + - parameter message: Description of the log. + - parameter includeMeta: If true, will append the meta data to the log. + - parameter path: Path of the caller. + - parameter function: Function of the caller. + - parameter line: Line of the caller. + */ + func error(_ message: String, path: String, function: String, line: Int, context: [String: Any]?) +} + +public protocol LoggerWorkerType: LoggerStore {} +public extension LoggerWorkerType { + + /// Log something generally unimportant (lowest priority; not written to file) + func verbose(_ message: String, path: String = #file, function: String = #function, line: Int = #line) { + verbose(message, path: path, function: function, line: line, context: nil) + } + + /// Log something which help during debugging (low priority; not written to file) + func debug(_ message: String, path: String = #file, function: String = #function, line: Int = #line) { + debug(message, path: path, function: function, line: line, context: nil) + } + + /// Log something which you are really interested but which is not an issue or error (normal priority) + func info(_ message: String, path: String = #file, function: String = #function, line: Int = #line) { + info(message, path: path, function: function, line: line, context: nil) + } + + /// Log something which may cause big trouble soon (high priority) + func warn(_ message: String, path: String = #file, function: String = #function, line: Int = #line) { + warn(message, path: path, function: function, line: line, context: nil) + } + + /// Log something which will keep you awake at night (highest priority) + func error(_ message: String, path: String = #file, function: String = #function, line: Int = #line) { + error(message, path: path, function: function, line: line, context: nil) + } +} diff --git a/Sources/ZamzamCore/Logging/LoggerWorker.swift b/Sources/ZamzamCore/Logging/LoggerWorker.swift new file mode 100644 index 00000000..0c5859ce --- /dev/null +++ b/Sources/ZamzamCore/Logging/LoggerWorker.swift @@ -0,0 +1,40 @@ +// +// Logger.swift +// PrayCore +// +// Created by Basem Emara on 2019-06-11. +// Copyright © 2019 Zamzam Inc. All rights reserved. +// + +import Foundation + +public struct LoggerWorker: LoggerWorkerType { + private let stores: [LoggerStore] + + public init(stores: [LoggerStore]) { + self.stores = stores + } +} + +public extension LoggerWorker { + + func verbose(_ message: String, path: String, function: String, line: Int, context: [String: Any]?) { + stores.forEach { $0.verbose(message, path: path, function: function, line: line, context: context) } + } + + func debug(_ message: String, path: String, function: String, line: Int, context: [String: Any]?) { + stores.forEach { $0.debug(message, path: path, function: function, line: line, context: context) } + } + + func info(_ message: String, path: String, function: String, line: Int, context: [String: Any]?) { + stores.forEach { $0.info(message, path: path, function: function, line: line, context: context) } + } + + func warn(_ message: String, path: String, function: String, line: Int, context: [String: Any]?) { + stores.forEach { $0.warn(message, path: path, function: function, line: line, context: context) } + } + + func error(_ message: String, path: String, function: String, line: Int, context: [String: Any]?) { + stores.forEach { $0.error(message, path: path, function: function, line: line, context: context) } + } +} diff --git a/Sources/ZamzamCore/Logging/Stores/LoggerConsoleStore.swift b/Sources/ZamzamCore/Logging/Stores/LoggerConsoleStore.swift new file mode 100644 index 00000000..39e55d12 --- /dev/null +++ b/Sources/ZamzamCore/Logging/Stores/LoggerConsoleStore.swift @@ -0,0 +1,42 @@ +// +// File.swift +// +// +// Created by Basem Emara on 2019-10-28. +// + +import Foundation + +public struct LoggerConsoleStore: LoggerStore { + public init() {} +} + +public extension LoggerConsoleStore { + + func verbose(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { + print("💜 VERBOSE \(output(message, path, function, line, context))") + } + + func debug(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { + print("💚 DEBUG \(output(message, path, function, line, context))") + } + + func info(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { + print("💙 INFO \(output(message, path, function, line, context))") + } + + func warn(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { + print("💛 WARNING \(output(message, path, function, line, context))") + } + + func error(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { + print("❤️ ERROR \(output(message, path, function, line, context))") + } +} + +private extension LoggerConsoleStore { + + func output(_ message: String, _ path: String, _ function: String, _ line: Int, _ context: [String: Any]?) -> String { + "\(URL(fileURLWithPath: path).deletingPathExtension().lastPathComponent).\(function):\(line) - \(message)" + } +} From 15361a53f1be005656886e2d64bd264e0cbcb70e Mon Sep 17 00:00:00 2001 From: Basem Emara Date: Thu, 31 Oct 2019 23:45:54 -0400 Subject: [PATCH 3/7] Access control and comments on pluggable scene --- .../Application/ScenePluggableDelegate.swift | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/Sources/ZamzamCore/Application/ScenePluggableDelegate.swift b/Sources/ZamzamCore/Application/ScenePluggableDelegate.swift index 42222b2d..b8f623a2 100644 --- a/Sources/ZamzamCore/Application/ScenePluggableDelegate.swift +++ b/Sources/ZamzamCore/Application/ScenePluggableDelegate.swift @@ -52,35 +52,45 @@ open class ScenePluggableDelegate: UIResponder, UIWindowSceneDelegate { } @available(iOS 13.0, *) -public extension ScenePluggableDelegate { +extension ScenePluggableDelegate { - func sceneWillEnterForeground(_ scene: UIScene) { + open func sceneWillEnterForeground(_ scene: UIScene) { pluginInstances.forEach { $0.sceneWillEnterForeground() } } - func sceneDidEnterBackground(_ scene: UIScene) { + open func sceneDidEnterBackground(_ scene: UIScene) { pluginInstances.forEach { $0.sceneDidEnterBackground() } } - func sceneDidBecomeActive(_ scene: UIScene) { + open func sceneDidBecomeActive(_ scene: UIScene) { pluginInstances.forEach { $0.sceneDidBecomeActive() } } - func sceneWillResignActive(_ scene: UIScene) { + open func sceneWillResignActive(_ scene: UIScene) { pluginInstances.forEach { $0.sceneWillResignActive() } } - func sceneDidDisconnect(_ scene: UIScene) { + open func sceneDidDisconnect(_ scene: UIScene) { pluginInstances.forEach { $0.sceneDidDisconnect() } } } /// Conforming to an scene module and added to `SceneDelegate.plugins()` will trigger events. public protocol ScenePlugin { + + /// Tells the delegate that the scene is about to begin running in the foreground and become visible to the user. func sceneWillEnterForeground() + + /// Tells the delegate that the scene is running in the background and is no longer onscreen. func sceneDidEnterBackground() + + /// Tells the delegate that the scene became active and is now responding to user events. func sceneDidBecomeActive() + + /// Tells the delegate that the scene is about to resign the active state and stop responding to user events. func sceneWillResignActive() + + /// Tells the delegate that UIKit removed a scene from your app. func sceneDidDisconnect() } From 318511e8d68ba4f3263802f0611bbc020e78a1f5 Mon Sep 17 00:00:00 2001 From: Basem Emara Date: Fri, 1 Nov 2019 12:23:30 -0400 Subject: [PATCH 4/7] Update logger API and add os_log store --- .../___FILEBASENAME___NetworkStore.swift | 15 +++--- README.md | 6 +-- .../Logging/{LoggerAPI.swift => LogAPI.swift} | 32 ++++++++--- .../{LoggerWorker.swift => LogWorker.swift} | 14 ++--- .../Logging/Stores/LogConsoleStore.swift | 53 ++++++++++++++++++ .../Logging/Stores/LogOSStore.swift | 54 +++++++++++++++++++ .../Logging/Stores/LoggerConsoleStore.swift | 42 --------------- .../UNNotificationAttachment.swift | 2 +- .../UNUserNotificationCenter.swift | 8 +-- 9 files changed, 156 insertions(+), 70 deletions(-) rename Sources/ZamzamCore/Logging/{LoggerAPI.swift => LogAPI.swift} (85%) rename Sources/ZamzamCore/Logging/{LoggerWorker.swift => LogWorker.swift} (74%) create mode 100644 Sources/ZamzamCore/Logging/Stores/LogConsoleStore.swift create mode 100644 Sources/ZamzamCore/Logging/Stores/LogOSStore.swift delete mode 100644 Sources/ZamzamCore/Logging/Stores/LoggerConsoleStore.swift diff --git a/Assets/Xcode Templates/ZamzamKit/Clean Worker.xctemplate/___FILEBASENAME___NetworkStore.swift b/Assets/Xcode Templates/ZamzamKit/Clean Worker.xctemplate/___FILEBASENAME___NetworkStore.swift index ad843467..efedd7df 100644 --- a/Assets/Xcode Templates/ZamzamKit/Clean Worker.xctemplate/___FILEBASENAME___NetworkStore.swift +++ b/Assets/Xcode Templates/ZamzamKit/Clean Worker.xctemplate/___FILEBASENAME___NetworkStore.swift @@ -1,11 +1,12 @@ //___FILEHEADER___ -import ZamzamKit +import ZamzamCore -public struct ___VARIABLE_productName:identifier___NetworkStore: ___VARIABLE_productName:identifier___Store, Loggable { +public struct ___VARIABLE_productName:identifier___NetworkStore: ___VARIABLE_productName:identifier___Store { + private let log: LogWorkerType - public init() { - + public init(log: LogWorkerType) { + self.log = log } } @@ -16,7 +17,7 @@ public extension ___VARIABLE_productName:identifier___NetworkStore { // Handle errors if applicable guard let value = $0.value, $0.isSuccess else { let error = DataError(from: $0.error) - self.Log(error: "An error occured while fetching ___VARIABLE_productName:identifier___: \($0.error?.serverDescription ?? "unknown")") + self.log.error("An error occured while fetching ___VARIABLE_productName:identifier___: \($0.error?.serverDescription ?? "unknown")") return completion(.failure(error)) } @@ -34,7 +35,7 @@ public extension ___VARIABLE_productName:identifier___NetworkStore { ) guard let data = payload.data, payload.status == .success else { - self.Log(error: "An error occured while fetching ___VARIABLE_productName:identifier___, data nil or server status error: \(String(describing: payload.errors)).") + self.log.error("An error occured while fetching ___VARIABLE_productName:identifier___, data nil or server status error: \(String(describing: payload.errors)).") return DispatchQueue.main.async { completion(.failure(.unknownReason(nil))) } } @@ -42,7 +43,7 @@ public extension ___VARIABLE_productName:identifier___NetworkStore { completion(.success(data.objects)) } } catch { - self.Log(error: "An error occured while parsing ___VARIABLE_productName:identifier___: \(error).") + self.log.error("An error occured while parsing ___VARIABLE_productName:identifier___: \(error).") return DispatchQueue.main.async { completion(.failure(.parseFailure(error))) } } } diff --git a/README.md b/README.md index e9c0ee4b..68a5d407 100644 --- a/README.md +++ b/README.md @@ -1020,8 +1020,8 @@ UNUserNotificationCenter.current().register( authorizations: [.alert, .badge, .sound], completion: { granted in granted - ? log(debug: "Authorization for notification succeeded.") - : log(warn: "Authorization for notification not given.") + ? log.debug("Authorization for notification succeeded.") + : log.warn("Authorization for notification not given.") } ) ``` @@ -1106,7 +1106,7 @@ UNUserNotificationCenter.current().add( ```swift UNNotificationAttachment.download(from: urlString) { guard $0.isSuccess, let attachment = $0.value else { - return log(error: "Could not download the remote resource (\(urlString)): \($0.error.debugDescription).") + return log.error("Could not download the remote resource (\(urlString)): \($0.error.debugDescription).") } UNUserNotificationCenter.current().add( diff --git a/Sources/ZamzamCore/Logging/LoggerAPI.swift b/Sources/ZamzamCore/Logging/LogAPI.swift similarity index 85% rename from Sources/ZamzamCore/Logging/LoggerAPI.swift rename to Sources/ZamzamCore/Logging/LogAPI.swift index 3c153533..3e9ccee9 100644 --- a/Sources/ZamzamCore/Logging/LoggerAPI.swift +++ b/Sources/ZamzamCore/Logging/LogAPI.swift @@ -1,6 +1,6 @@ // // Loggable.swift -// PrayCore +// ZamzamCore // // Created by Basem Emara on 2019-06-11. // Copyright © 2019 Zamzam Inc. All rights reserved. @@ -8,7 +8,10 @@ import Foundation -public protocol LoggerStore: AppInfo { +// Namespace +public enum LogAPI {} + +public protocol LogStore: AppInfo { /** Log something generally unimportant (lowest priority; not written to file) @@ -52,7 +55,7 @@ public protocol LoggerStore: AppInfo { - parameter function: Function of the caller. - parameter line: Line of the caller. */ - func warn(_ message: String, path: String, function: String, line: Int, context: [String: Any]?) + func warning(_ message: String, path: String, function: String, line: Int, context: [String: Any]?) /** Log something which will keep you awake at night (highest priority) @@ -66,8 +69,8 @@ public protocol LoggerStore: AppInfo { func error(_ message: String, path: String, function: String, line: Int, context: [String: Any]?) } -public protocol LoggerWorkerType: LoggerStore {} -public extension LoggerWorkerType { +public protocol LogWorkerType: LogStore {} +public extension LogWorkerType { /// Log something generally unimportant (lowest priority; not written to file) func verbose(_ message: String, path: String = #file, function: String = #function, line: Int = #line) { @@ -86,7 +89,7 @@ public extension LoggerWorkerType { /// Log something which may cause big trouble soon (high priority) func warn(_ message: String, path: String = #file, function: String = #function, line: Int = #line) { - warn(message, path: path, function: function, line: line, context: nil) + warning(message, path: path, function: function, line: line, context: nil) } /// Log something which will keep you awake at night (highest priority) @@ -94,3 +97,20 @@ public extension LoggerWorkerType { error(message, path: path, function: function, line: line, context: nil) } } + +// MARK: - Types + +public extension LogAPI { + + enum Level: Int, Comparable { + case verbose + case debug + case info + case warning + case error + + public static func < (lhs: Level, rhs: Level) -> Bool { + lhs.rawValue < rhs.rawValue + } + } +} diff --git a/Sources/ZamzamCore/Logging/LoggerWorker.swift b/Sources/ZamzamCore/Logging/LogWorker.swift similarity index 74% rename from Sources/ZamzamCore/Logging/LoggerWorker.swift rename to Sources/ZamzamCore/Logging/LogWorker.swift index 0c5859ce..db61034c 100644 --- a/Sources/ZamzamCore/Logging/LoggerWorker.swift +++ b/Sources/ZamzamCore/Logging/LogWorker.swift @@ -1,6 +1,6 @@ // // Logger.swift -// PrayCore +// ZamzamCore // // Created by Basem Emara on 2019-06-11. // Copyright © 2019 Zamzam Inc. All rights reserved. @@ -8,15 +8,15 @@ import Foundation -public struct LoggerWorker: LoggerWorkerType { - private let stores: [LoggerStore] +public struct LogWorker: LogWorkerType { + private let stores: [LogStore] - public init(stores: [LoggerStore]) { + public init(stores: [LogStore]) { self.stores = stores } } -public extension LoggerWorker { +public extension LogWorker { func verbose(_ message: String, path: String, function: String, line: Int, context: [String: Any]?) { stores.forEach { $0.verbose(message, path: path, function: function, line: line, context: context) } @@ -30,8 +30,8 @@ public extension LoggerWorker { stores.forEach { $0.info(message, path: path, function: function, line: line, context: context) } } - func warn(_ message: String, path: String, function: String, line: Int, context: [String: Any]?) { - stores.forEach { $0.warn(message, path: path, function: function, line: line, context: context) } + func warning(_ message: String, path: String, function: String, line: Int, context: [String: Any]?) { + stores.forEach { $0.warning(message, path: path, function: function, line: line, context: context) } } func error(_ message: String, path: String, function: String, line: Int, context: [String: Any]?) { diff --git a/Sources/ZamzamCore/Logging/Stores/LogConsoleStore.swift b/Sources/ZamzamCore/Logging/Stores/LogConsoleStore.swift new file mode 100644 index 00000000..96ea1a41 --- /dev/null +++ b/Sources/ZamzamCore/Logging/Stores/LogConsoleStore.swift @@ -0,0 +1,53 @@ +// +// File.swift +// +// +// Created by Basem Emara on 2019-10-28. +// + +import Foundation + +/// Sends a message to the IDE console. +public struct LogConsoleStore: LogStore { + private let minLevel: LogAPI.Level + private let queue = DispatchQueue(label: "io.zamzam.LogConsoleStore", qos: .utility) + + public init(minLevel: LogAPI.Level) { + self.minLevel = minLevel + } +} + +public extension LogConsoleStore { + + func verbose(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { + guard minLevel <= .verbose else { return } + queue.async { print("💜 VERBOSE \(self.output(message, path, function, line, context))") } + } + + func debug(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { + guard minLevel <= .debug else { return } + queue.async { print("💚 DEBUG \(self.output(message, path, function, line, context))") } + } + + func info(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { + guard minLevel <= .info else { return } + queue.async { print("💙 INFO \(self.output(message, path, function, line, context))") } + } + + func warning(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { + guard minLevel <= .warning else { return } + queue.async { print("💛 WARNING \(self.output(message, path, function, line, context))") } + } + + func error(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { + guard minLevel <= .error else { return } + queue.async { print("❤️ ERROR \(self.output(message, path, function, line, context))") } + } +} + +private extension LogConsoleStore { + + func output(_ message: String, _ path: String, _ function: String, _ line: Int, _ context: [String: Any]?) -> String { + "\(URL(fileURLWithPath: path).deletingPathExtension().lastPathComponent).\(function):\(line) - \(message)" + } +} diff --git a/Sources/ZamzamCore/Logging/Stores/LogOSStore.swift b/Sources/ZamzamCore/Logging/Stores/LogOSStore.swift new file mode 100644 index 00000000..c59bf7c8 --- /dev/null +++ b/Sources/ZamzamCore/Logging/Stores/LogOSStore.swift @@ -0,0 +1,54 @@ +// +// File.swift +// +// +// Created by Basem Emara on 2019-11-01. +// + +import Foundation +import os + +/// Sends a message to the logging system, optionally specifying a custom log object, log level, and any message format arguments. +public struct LogOSStore: LogStore { + private let minLevel: LogAPI.Level + private let subsystem: String + private let category: String + private let log: OSLog + + private let queue = DispatchQueue(label: "io.zamzam.LogOSStore", qos: .utility) + + public init(minLevel: LogAPI.Level, subsystem: String, category: String) { + self.minLevel = minLevel + self.subsystem = subsystem + self.category = category + self.log = OSLog(subsystem: subsystem, category: category) + } +} + +public extension LogOSStore { + + func verbose(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { + guard minLevel <= .verbose else { return } + queue.async { os_log("%@", log: self.log, type: .debug, message) } + } + + func debug(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { + guard minLevel <= .debug else { return } + queue.async { os_log("%@", log: self.log, type: .debug, message) } + } + + func info(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { + guard minLevel <= .info else { return } + queue.async { os_log("%@", log: self.log, type: .info, message) } + } + + func warning(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { + guard minLevel <= .warning else { return } + queue.async { os_log("%@", log: self.log, type: .default, message) } + } + + func error(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { + guard minLevel <= .error else { return } + queue.async { os_log("%@", log: self.log, type: .error, message) } + } +} diff --git a/Sources/ZamzamCore/Logging/Stores/LoggerConsoleStore.swift b/Sources/ZamzamCore/Logging/Stores/LoggerConsoleStore.swift deleted file mode 100644 index 39e55d12..00000000 --- a/Sources/ZamzamCore/Logging/Stores/LoggerConsoleStore.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// File.swift -// -// -// Created by Basem Emara on 2019-10-28. -// - -import Foundation - -public struct LoggerConsoleStore: LoggerStore { - public init() {} -} - -public extension LoggerConsoleStore { - - func verbose(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { - print("💜 VERBOSE \(output(message, path, function, line, context))") - } - - func debug(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { - print("💚 DEBUG \(output(message, path, function, line, context))") - } - - func info(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { - print("💙 INFO \(output(message, path, function, line, context))") - } - - func warn(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { - print("💛 WARNING \(output(message, path, function, line, context))") - } - - func error(_ message: String, path: String = #file, function: String = #function, line: Int = #line, context: [String: Any]? = nil) { - print("❤️ ERROR \(output(message, path, function, line, context))") - } -} - -private extension LoggerConsoleStore { - - func output(_ message: String, _ path: String, _ function: String, _ line: Int, _ context: [String: Any]?) -> String { - "\(URL(fileURLWithPath: path).deletingPathExtension().lastPathComponent).\(function):\(line) - \(message)" - } -} diff --git a/Sources/ZamzamNotification/UNNotificationAttachment.swift b/Sources/ZamzamNotification/UNNotificationAttachment.swift index df94af3d..0f58f497 100644 --- a/Sources/ZamzamNotification/UNNotificationAttachment.swift +++ b/Sources/ZamzamNotification/UNNotificationAttachment.swift @@ -16,7 +16,7 @@ public extension UNNotificationAttachment { /// /// UNNotificationAttachment.download(from: urlString) { /// guard $0.isSuccess, let attachment = $0.value else { - /// return log(error: "Could not download the remote resource (\(urlString)): \($0.error.debugDescription).") + /// return log.error("Could not download the remote resource (\(urlString)): \($0.error.debugDescription).") /// } /// /// UNUserNotificationCenter.current().add( diff --git a/Sources/ZamzamNotification/UNUserNotificationCenter.swift b/Sources/ZamzamNotification/UNUserNotificationCenter.swift index c0b15a2e..dd8a490c 100644 --- a/Sources/ZamzamNotification/UNUserNotificationCenter.swift +++ b/Sources/ZamzamNotification/UNUserNotificationCenter.swift @@ -45,8 +45,8 @@ public extension UNUserNotificationCenter { /// authorizations: [.alert, .badge, .sound], /// completion: { granted in /// granted - /// ? log(debug: "Authorization for notification succeeded.") - /// : log(warn: "Authorization for notification not given.") + /// ? log.debug("Authorization for notification succeeded.") + /// : log.warn("Authorization for notification not given.") /// } /// ) /// @@ -92,8 +92,8 @@ public extension UNUserNotificationCenter { /// authorizations: [.alert, .badge, .sound], /// completion: { granted in /// granted - /// ? log(debug: "Authorization for notification succeeded.") - /// : log(warn: "Authorization for notification not given.") + /// ? log.debug("Authorization for notification succeeded.") + /// : log.warn("Authorization for notification not given.") /// } /// ) /// From 2d42ee9d49faf8bd824cd9786b98204d8c656b24 Mon Sep 17 00:00:00 2001 From: Basem Emara Date: Fri, 1 Nov 2019 12:30:10 -0400 Subject: [PATCH 5/7] Updated readme --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index 68a5d407..91413114 100644 --- a/README.md +++ b/README.md @@ -782,6 +782,27 @@ myLabel3.text = .localized(.next) ```
+
+Logger + +> Create loggers that conform to `LogStore` and add to `LogWorker`. Then use the worker to log levels. Included log stores are `LogConsoleStore` and `LogOSStore`: +```swift +let log: LogWorkerType = LogWorker( + stores: [ + LogConsoleStore(minLevel: .debug), + LogOSStore( + minLevel: .warning, + subsystem: "io.zamzam.Basem-Emara", + category: "Application" + ), + MyCustomLogger() + ] +) + +log.error("There was an error.") +``` +
+
SystemConfiguration From 9c936cf0329d4e7869548563e19b2d0d37454b79 Mon Sep 17 00:00:00 2001 From: Basem Emara Date: Sat, 2 Nov 2019 10:34:16 -0400 Subject: [PATCH 6/7] Update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 91413114..4b0e78bd 100644 --- a/README.md +++ b/README.md @@ -785,7 +785,7 @@ myLabel3.text = .localized(.next)
Logger -> Create loggers that conform to `LogStore` and add to `LogWorker`. Then use the worker to log levels. Included log stores are `LogConsoleStore` and `LogOSStore`: +> Create loggers that conform to `LogStore` and add to `LogWorker` (console and `os_log` are included): ```swift let log: LogWorkerType = LogWorker( stores: [ From 73dc053b4c575e5fdd41f01d42029952b9181019 Mon Sep 17 00:00:00 2001 From: Basem Emara Date: Sat, 2 Nov 2019 10:34:39 -0400 Subject: [PATCH 7/7] Expose more scene delegate events --- .../ApplicationPluggableDelegate.swift | 2 +- .../Application/ScenePluggableDelegate.swift | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Sources/ZamzamCore/Application/ApplicationPluggableDelegate.swift b/Sources/ZamzamCore/Application/ApplicationPluggableDelegate.swift index 8dd18873..fd6dcba1 100644 --- a/Sources/ZamzamCore/Application/ApplicationPluggableDelegate.swift +++ b/Sources/ZamzamCore/Application/ApplicationPluggableDelegate.swift @@ -46,7 +46,7 @@ import UIKit /// log.warn("App will terminate.") /// } /// } -open class ApplicationPluggableDelegate: UIResponder, UIApplicationDelegate { +open class ApplicationPluggableDelegate: UIResponder, UIApplicationDelegate, WindowDelegate { public var window: UIWindow? /// List of application plugins for binding to `AppDelegate` events diff --git a/Sources/ZamzamCore/Application/ScenePluggableDelegate.swift b/Sources/ZamzamCore/Application/ScenePluggableDelegate.swift index b8f623a2..5e80b57c 100644 --- a/Sources/ZamzamCore/Application/ScenePluggableDelegate.swift +++ b/Sources/ZamzamCore/Application/ScenePluggableDelegate.swift @@ -34,7 +34,7 @@ import UIKit /// } /// } @available(iOS 13.0, *) -open class ScenePluggableDelegate: UIResponder, UIWindowSceneDelegate { +open class ScenePluggableDelegate: UIResponder, UIWindowSceneDelegate, WindowDelegate { public var window: UIWindow? /// List of scene plugins for binding to `SceneDelegate` events @@ -54,6 +54,10 @@ open class ScenePluggableDelegate: UIResponder, UIWindowSceneDelegate { @available(iOS 13.0, *) extension ScenePluggableDelegate { + open func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + pluginInstances.forEach { $0.scene(scene, willConnectTo: session, options: connectionOptions) } + } + open func sceneWillEnterForeground(_ scene: UIScene) { pluginInstances.forEach { $0.sceneWillEnterForeground() } } @@ -92,6 +96,10 @@ public protocol ScenePlugin { /// Tells the delegate that UIKit removed a scene from your app. func sceneDidDisconnect() + + /// Tells the delegate about the addition of a scene to the app. + @available(iOS 13.0, *) + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) } // MARK: - Optionals @@ -102,5 +110,12 @@ public extension ScenePlugin { func sceneDidBecomeActive() {} func sceneWillResignActive() {} func sceneDidDisconnect() {} + + @available(iOS 13.0, *) + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {} +} + +public protocol WindowDelegate: class { + var window: UIWindow? { get set } } #endif