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 19c9c1a5..4b0e78bd 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.") + } +} +```
@@ -761,6 +782,27 @@ myLabel3.text = .localized(.next) ```
+
+Logger + +> Create loggers that conform to `LogStore` and add to `LogWorker` (console and `os_log` are included): +```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 @@ -999,8 +1041,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.") } ) ``` @@ -1085,7 +1127,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/Application/ApplicationPluggableDelegate.swift b/Sources/ZamzamCore/Application/ApplicationPluggableDelegate.swift index 2adbf6ef..fd6dcba1 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() /// ]} @@ -46,21 +46,21 @@ 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 - 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..5e80b57c --- /dev/null +++ b/Sources/ZamzamCore/Application/ScenePluggableDelegate.swift @@ -0,0 +1,121 @@ +// +// 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, WindowDelegate { + 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, *) +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() } + } + + open func sceneDidEnterBackground(_ scene: UIScene) { + pluginInstances.forEach { $0.sceneDidEnterBackground() } + } + + open func sceneDidBecomeActive(_ scene: UIScene) { + pluginInstances.forEach { $0.sceneDidBecomeActive() } + } + + open func sceneWillResignActive(_ scene: UIScene) { + pluginInstances.forEach { $0.sceneWillResignActive() } + } + + 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() + + /// 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 + +public extension ScenePlugin { + func sceneWillEnterForeground() {} + func sceneDidEnterBackground() {} + 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 diff --git a/Sources/ZamzamCore/Logging/LogAPI.swift b/Sources/ZamzamCore/Logging/LogAPI.swift new file mode 100644 index 00000000..3e9ccee9 --- /dev/null +++ b/Sources/ZamzamCore/Logging/LogAPI.swift @@ -0,0 +1,116 @@ +// +// Loggable.swift +// ZamzamCore +// +// Created by Basem Emara on 2019-06-11. +// Copyright © 2019 Zamzam Inc. All rights reserved. +// + +import Foundation + +// Namespace +public enum LogAPI {} + +public protocol LogStore: 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 warning(_ 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 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) { + 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) { + warning(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) + } +} + +// 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/LogWorker.swift b/Sources/ZamzamCore/Logging/LogWorker.swift new file mode 100644 index 00000000..db61034c --- /dev/null +++ b/Sources/ZamzamCore/Logging/LogWorker.swift @@ -0,0 +1,40 @@ +// +// Logger.swift +// ZamzamCore +// +// Created by Basem Emara on 2019-06-11. +// Copyright © 2019 Zamzam Inc. All rights reserved. +// + +import Foundation + +public struct LogWorker: LogWorkerType { + private let stores: [LogStore] + + public init(stores: [LogStore]) { + self.stores = stores + } +} + +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) } + } + + 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 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]?) { + stores.forEach { $0.error(message, path: path, function: function, line: line, context: context) } + } +} 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/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.") /// } /// ) ///