29 Commits

Author SHA1 Message Date
Fernando Martín Ortiz 465ebe3f64 Merge pull request #25 from davbeck/master
Fix callback handling
2017-11-08 19:18:25 -03:00
David Beck 89deaa7850 Fix callback handling
Callbacks need to be coallesced for multiple responders, and more importantly, called if no service responds.
2017-08-29 14:12:53 -07:00
Fernando Martín Ortiz e03cd1e487 Merge pull request #18 from stephanecopin/public-fix
Fix issue where open url was changed to public url
2017-05-23 16:51:26 -03:00
Stéphane Copin 704d2c7d30 Fix issue where open url was changed to public url 2017-05-18 14:14:16 +01:00
Fernando 97ead7c308 Solved warnings. 2017-05-16 09:52:34 -03:00
Fernando Martín Ortiz 66cbdc3f78 Merge pull request #17 from adamchalmers/master
Changed lifecycle methods from public to open
2017-05-15 15:43:20 -03:00
Adam Chalmers 50a8cb44cf Changed lifecycle methods from public to open
My company is using PluggableApplicationDelegate in our iOS codebase. We'd like to time each ApplicationService's didFinishLaunching and didBecomeActive methods, to find where we can get performance gains. My initial thought was to subclass PluggableApplicationDelegate and add timers to the relevant methods, but because they're public (not open) this isn't possible.
2017-05-15 13:18:17 -05:00
Fernando Martín Ortiz d129ee2680 Merge pull request #15 from stephanecopin/logic-refactor
Fix delegate method handling
2017-05-15 11:51:23 -03:00
Stéphane Copin 4119d0395f Fix logic when calling delegate methods from multiple services, so that they're never short-circuited. 2017-05-15 15:39:06 +01:00
Fernando Martín Ortiz 3ae9dbacc7 Merge pull request #11 from andrewcb/master
Boolean methods made more concise and functional by replacing loops with reduce expressions
2017-04-20 10:56:14 -03:00
acb dbc8e1e745 Boolean methods made more concise and functional by replacing loops with reduce expressions 2017-04-12 12:56:22 +01:00
Fernando Martín Ortiz 8e9091f109 Merge pull request #9 from DKOG/master
Remove `:git =>` from the podfile installation string
2017-04-07 11:49:13 -03:00
Daniel Kloeck 3a045a0904 Remove :git => from the podfile installation string 2017-04-07 10:34:35 +02:00
Fernando Martín Ortiz 4ed80bf050 Merge pull request #7 from harmjanr/master
Changed return value
2017-04-04 17:47:38 -03:00
Harm-Jan Roskam 283cf142f3 Return result instead of true. The whole for loop is useless if true is returned. 2017-04-04 15:05:11 +02:00
Fernando Ortiz e400f10779 Merge branch 'master' of https://github.com/fmo91/PluggableApplicationDelegate 2017-04-04 01:12:13 -03:00
Fernando Ortiz 9b53d401d3 Bugfixing 2017-04-04 01:11:49 -03:00
Fernando Martín Ortiz c20435566e Merge pull request #6 from loopsocial/feature/carthage
Share the main scheme so Carthage can pick it up
2017-04-03 23:46:35 -03:00
Jakub Petrík f12323e1b0 Share main scheme 2017-04-03 13:25:08 -07:00
Fernando Ortiz d5d08bf252 Bugfixing 2017-04-02 18:35:25 -03:00
Fernando Martín Ortiz 8b5145090c Merge pull request #5 from SimonFairbairn/DeprecationWarning
Fixes issue #2
2017-03-30 16:30:36 -03:00
Simon Fairbairn 9cf41812fc Fixes deprecation warning when building for iOS 10 2017-03-30 11:17:12 +02:00
Fernando Martín Ortiz 44d4efd4d5 Update README.md 2017-03-19 10:35:22 -03:00
Fernando Martín Ortiz 3280ff38a3 Merge pull request #1 from MariuszWisniewski/patch-1
Change Obj-C style lazy into new new Swift-style lazy
2017-03-10 10:18:35 -03:00
Mariusz Wisniewski 2d0a9baeec True lazy variable
Change __services and _services into just one lazy variable
2017-03-10 14:15:03 +01:00
Fernando Martín Ortiz f2b0035d85 Update README.md 2017-02-27 09:03:15 -08:00
Fernando Ortiz 411f0dc5e3 Merge branch 'master' of https://github.com/fmo91/PluggableApplicationDelegate 2017-02-26 22:01:21 -03:00
Fernando Martín Ortiz 993d460688 Update README.md 2017-02-26 21:55:59 -03:00
Fernando Martín Ortiz d6d38f69eb Update README.md 2017-02-26 21:54:34 -03:00
5 changed files with 263 additions and 129 deletions
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "650D907715EF6AB8D20EC59213D27791"
BuildableName = "PluggableApplicationDelegate.framework"
BlueprintName = "PluggableApplicationDelegate"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "650D907715EF6AB8D20EC59213D27791"
BuildableName = "PluggableApplicationDelegate.framework"
BlueprintName = "PluggableApplicationDelegate"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "650D907715EF6AB8D20EC59213D27791"
BuildableName = "PluggableApplicationDelegate.framework"
BlueprintName = "PluggableApplicationDelegate"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
+1 -1
View File
@@ -8,7 +8,7 @@
Pod::Spec.new do |s|
s.name = 'PluggableApplicationDelegate'
s.version = '0.1.0'
s.version = '0.2.0'
s.summary = 'Services oriented AppDelegate in Swift 3.'
s.description = <<-DESC
PluggableApplicationDelegate is a way of decoupling AppDelegate, by splitting it into small modules called ApplicationService.
@@ -18,112 +18,132 @@ open class PluggableApplicationDelegate: UIResponder, UIApplicationDelegate {
public var window: UIWindow?
open var services: [ApplicationService] { return [] }
private var _services: [ApplicationService]?
public var __services: [ApplicationService] {
if _services == nil {
_services = services
private lazy var __services: [ApplicationService] = {
return self.services
}()
@discardableResult
private func apply<T, S>(_ work: (ApplicationService, @escaping (T) -> Void) -> S?, completionHandler: @escaping ([T]) -> Swift.Void) -> [S] {
let dispatchGroup = DispatchGroup()
var results: [T] = []
var returns: [S] = []
for service in __services {
dispatchGroup.enter()
let returned = work(service, { result in
results.append(result)
dispatchGroup.leave()
})
if let returned = returned {
returns.append(returned)
} else { // delegate doesn't impliment method
dispatchGroup.leave()
}
if returned == nil {
}
}
return _services!
dispatchGroup.notify(queue: .main) {
completionHandler(results)
}
return returns
}
@available(iOS 2.0, *)
public func applicationDidFinishLaunching(_ application: UIApplication) {
for service in __services {
service.applicationDidFinishLaunching?(application)
}
open func applicationDidFinishLaunching(_ application: UIApplication) {
__services.forEach { $0.applicationDidFinishLaunching?(application) }
}
@available(iOS 6.0, *)
public func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
open func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
var result = false
for service in __services {
if let serviceResult = service.application?(application, willFinishLaunchingWithOptions: launchOptions) {
if serviceResult == false {
return false
}
if service.application?(application, willFinishLaunchingWithOptions: launchOptions) ?? false {
result = true
}
}
return true
return result
}
@available(iOS 3.0, *)
public func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
open func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
var result = false
for service in __services {
if let serviceResult = service.application?(application, didFinishLaunchingWithOptions: launchOptions) {
if serviceResult == false {
return false
}
if service.application?(application, didFinishLaunchingWithOptions: launchOptions) ?? false {
result = true
}
}
return true
return result
}
@available(iOS 2.0, *)
public func applicationDidBecomeActive(_ application: UIApplication) {
open func applicationDidBecomeActive(_ application: UIApplication) {
for service in __services {
service.applicationDidBecomeActive?(application)
}
}
@available(iOS 2.0, *)
public func applicationWillResignActive(_ application: UIApplication) {
open func applicationWillResignActive(_ application: UIApplication) {
for service in __services {
service.applicationWillResignActive?(application)
}
}
@available(iOS, introduced: 2.0, deprecated: 9.0, message: "Please use application:openURL:options:")
public func application(_ application: UIApplication, handleOpen url: URL) -> Bool {
open func application(_ application: UIApplication, handleOpen url: URL) -> Bool {
var result = false
for service in __services {
if service.application?(application, handleOpen: url) == false {
return false
if service.application?(application, handleOpen: url) ?? false {
result = true
}
}
return true
return result
}
@available(iOS, introduced: 4.2, deprecated: 9.0, message: "Please use application:openURL:options:")
public func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
open func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
var result = false
for service in __services {
if service.application?(application, open: url, sourceApplication: sourceApplication, annotation: annotation) == false {
return false
if service.application?(application, open: url, sourceApplication: sourceApplication, annotation: annotation) ?? false {
result = true
}
}
return true
return result
}
@available(iOS 9.0, *)
public func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
open func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
var result = false
for service in __services {
if service.application?(app, open: url, options: options) == false {
return false
if service.application?(app, open: url, options: options) ?? false {
result = true
}
}
return true
return result
}
@available(iOS 2.0, *)
public func applicationDidReceiveMemoryWarning(_ application: UIApplication) {
open func applicationDidReceiveMemoryWarning(_ application: UIApplication) {
for service in __services {
service.applicationDidReceiveMemoryWarning?(application)
}
}
@available(iOS 2.0, *)
public func applicationWillTerminate(_ application: UIApplication) {
open func applicationWillTerminate(_ application: UIApplication) {
for service in __services {
service.applicationWillTerminate?(application)
}
}
@available(iOS 2.0, *)
public func applicationSignificantTimeChange(_ application: UIApplication) {
open func applicationSignificantTimeChange(_ application: UIApplication) {
for service in __services {
service.applicationSignificantTimeChange?(application)
}
@@ -131,26 +151,26 @@ open class PluggableApplicationDelegate: UIResponder, UIApplicationDelegate {
@available(iOS 2.0, *)
public func application(_ application: UIApplication, willChangeStatusBarOrientation newStatusBarOrientation: UIInterfaceOrientation, duration: TimeInterval) {
open func application(_ application: UIApplication, willChangeStatusBarOrientation newStatusBarOrientation: UIInterfaceOrientation, duration: TimeInterval) {
for service in __services {
service.application?(application, willChangeStatusBarOrientation: newStatusBarOrientation, duration: duration)
}
}
@available(iOS 2.0, *)
public func application(_ application: UIApplication, didChangeStatusBarOrientation oldStatusBarOrientation: UIInterfaceOrientation) {
open func application(_ application: UIApplication, didChangeStatusBarOrientation oldStatusBarOrientation: UIInterfaceOrientation) {
for service in __services {
service.application?(application, didChangeStatusBarOrientation: oldStatusBarOrientation)
}
}
// public func application(_ application: UIApplication, willChangeStatusBarFrame newStatusBarFrame: CGRect) {
// open func application(_ application: UIApplication, willChangeStatusBarFrame newStatusBarFrame: CGRect) {
// for service in __services {
// service.application?(application, willChangeStatusBarFrame: newStatusBarFrame)
// }
// }
//
// public func application(_ application: UIApplication, didChangeStatusBarFrame oldStatusBarFrame: CGRect) {
// open func application(_ application: UIApplication, didChangeStatusBarFrame oldStatusBarFrame: CGRect) {
// for service in __services {
// service.application?(application, didChangeStatusBarFrame: oldStatusBarFrame)
// }
@@ -158,8 +178,8 @@ open class PluggableApplicationDelegate: UIResponder, UIApplicationDelegate {
// This callback will be made upon calling -[UIApplication registerUserNotificationSettings:]. The settings the user has granted to the application will be passed in as the second argument.
@available(iOS 8.0, *)
public func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
@available(iOS, introduced: 8.0, deprecated: 10.0, message: "Use UserNotification UNNotification Settings instead")
open func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
for service in __services {
service.application?(application, didRegister: notificationSettings)
}
@@ -167,7 +187,7 @@ open class PluggableApplicationDelegate: UIResponder, UIApplicationDelegate {
@available(iOS 3.0, *)
public func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
open func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
for service in __services {
service.application?(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
}
@@ -175,7 +195,7 @@ open class PluggableApplicationDelegate: UIResponder, UIApplicationDelegate {
@available(iOS 3.0, *)
public func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
open func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
for service in __services {
service.application?(application, didFailToRegisterForRemoteNotificationsWithError: error)
}
@@ -183,7 +203,7 @@ open class PluggableApplicationDelegate: UIResponder, UIApplicationDelegate {
@available(iOS, introduced: 3.0, deprecated: 10.0, message: "Use UserNotifications Framework's -[UNUserNotificationCenterDelegate willPresentNotification:withCompletionHandler:] or -[UNUserNotificationCenterDelegate didReceiveNotificationResponse:withCompletionHandler:] for user visible notifications and -[UIApplicationDelegate application:didReceiveRemoteNotification:fetchCompletionHandler:] for silent remote notifications")
public func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
open func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
for service in __services {
service.application?(application, didReceiveRemoteNotification: userInfo)
}
@@ -191,7 +211,7 @@ open class PluggableApplicationDelegate: UIResponder, UIApplicationDelegate {
@available(iOS, introduced: 4.0, deprecated: 10.0, message: "Use UserNotifications Framework's -[UNUserNotificationCenterDelegate willPresentNotification:withCompletionHandler:] or -[UNUserNotificationCenterDelegate didReceiveNotificationResponse:withCompletionHandler:]")
public func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
open func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
for service in __services {
service.application?(application, didReceive: notification)
}
@@ -202,20 +222,22 @@ open class PluggableApplicationDelegate: UIResponder, UIApplicationDelegate {
// A nil action identifier indicates the default action.
// You should call the completion handler as soon as you've finished handling the action.
@available(iOS, introduced: 8.0, deprecated: 10.0, message: "Use UserNotifications Framework's -[UNUserNotificationCenterDelegate didReceiveNotificationResponse:withCompletionHandler:]")
public func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, completionHandler: @escaping () -> Swift.Void) {
for service in __services {
service.application?(application, handleActionWithIdentifier: identifier, for: notification, completionHandler: completionHandler)
}
open func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, completionHandler: @escaping () -> Swift.Void) {
apply({ (service, completion) -> Void? in
service.application?(application, handleActionWithIdentifier: identifier, for: notification, completionHandler: completion)
}, completionHandler: { _ in
completionHandler()
})
}
@available(iOS, introduced: 9.0, deprecated: 10.0, message: "Use UserNotifications Framework's -[UNUserNotificationCenterDelegate didReceiveNotificationResponse:withCompletionHandler:]")
public func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], withResponseInfo responseInfo: [AnyHashable : Any], completionHandler: @escaping () -> Swift.Void) {
for service in __services {
open func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], withResponseInfo responseInfo: [AnyHashable : Any], completionHandler: @escaping () -> Swift.Void) {
apply({ (service, completionHandler) -> Void? in
service.application?(application, handleActionWithIdentifier: identifier, forRemoteNotification: userInfo, withResponseInfo: responseInfo, completionHandler: completionHandler)
}
}, completionHandler: { _ in
completionHandler()
})
}
@@ -223,18 +245,22 @@ open class PluggableApplicationDelegate: UIResponder, UIApplicationDelegate {
// A nil action identifier indicates the default action.
// You should call the completion handler as soon as you've finished handling the action.
@available(iOS, introduced: 8.0, deprecated: 10.0, message: "Use UserNotifications Framework's -[UNUserNotificationCenterDelegate didReceiveNotificationResponse:withCompletionHandler:]")
public func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], completionHandler: @escaping () -> Swift.Void) {
for service in __services {
open func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], completionHandler: @escaping () -> Swift.Void) {
apply({ (service, completionHandler) -> Void? in
service.application?(application, handleActionWithIdentifier: identifier, forRemoteNotification: userInfo, completionHandler: completionHandler)
}
}, completionHandler: { _ in
completionHandler()
})
}
@available(iOS, introduced: 9.0, deprecated: 10.0, message: "Use UserNotifications Framework's -[UNUserNotificationCenterDelegate didReceiveNotificationResponse:withCompletionHandler:]")
public func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, withResponseInfo responseInfo: [AnyHashable : Any], completionHandler: @escaping () -> Swift.Void) {
for service in __services {
open func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, withResponseInfo responseInfo: [AnyHashable : Any], completionHandler: @escaping () -> Swift.Void) {
apply({ (service, completionHandler) -> Void? in
service.application?(application, handleActionWithIdentifier: identifier, for: notification, withResponseInfo: responseInfo, completionHandler: completionHandler)
}
}, completionHandler: { _ in
completionHandler()
})
}
@@ -242,29 +268,39 @@ open class PluggableApplicationDelegate: UIResponder, UIApplicationDelegate {
This method will be invoked even if the application was launched or resumed because of the remote notification. The respective delegate methods will be invoked first. Note that this behavior is in contrast to application:didReceiveRemoteNotification:, which is not called in those cases, and which will not be invoked if this method is implemented. !*/
@available(iOS 7.0, *)
public func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Swift.Void) {
for service in __services {
open func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Swift.Void) {
apply({ (service, completionHandler) -> Void? in
service.application?(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)
}
}, completionHandler: { results in
let result = results.min(by: { $0.rawValue < $1.rawValue }) ?? .noData
completionHandler(result)
})
}
/// Applications with the "fetch" background mode may be given opportunities to fetch updated content in the background or when it is convenient for the system. This method will be called in these situations. You should call the fetchCompletionHandler as soon as you're finished performing that operation, so the system can accurately estimate its power and data cost.
@available(iOS 7.0, *)
public func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Swift.Void) {
for service in __services {
open func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Swift.Void) {
apply({ (service, completionHandler) -> Void? in
service.application?(application, performFetchWithCompletionHandler: completionHandler)
}
}, completionHandler: { results in
let result = results.min(by: { $0.rawValue < $1.rawValue }) ?? .noData
completionHandler(result)
})
}
// Called when the user activates your application by selecting a shortcut on the home screen,
// except when -application:willFinishLaunchingWithOptions: or -application:didFinishLaunchingWithOptions returns NO.
@available(iOS 9.0, *)
public func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Swift.Void) {
for service in __services {
open func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Swift.Void) {
apply({ (service, completionHandler) -> Void? in
service.application?(application, performActionFor: shortcutItem, completionHandler: completionHandler)
}
}, completionHandler: { results in
// if any service handled the shortcut, return true
let result = results.reduce(false, { $0 || $1 })
completionHandler(result)
})
}
@@ -274,23 +310,37 @@ open class PluggableApplicationDelegate: UIResponder, UIApplicationDelegate {
// callbacks. If such a session has already been created (if the app is being resumed, for instance), then the delegate will start receiving
// callbacks without any action by the application. You should call the completionHandler as soon as you're finished handling the callbacks.
@available(iOS 7.0, *)
public func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Swift.Void) {
for service in __services {
open func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Swift.Void) {
apply({ (service, completionHandler) -> Void? in
service.application?(application, handleEventsForBackgroundURLSession: identifier, completionHandler: completionHandler)
}
}, completionHandler: { _ in
completionHandler()
})
}
@available(iOS 8.2, *)
public func application(_ application: UIApplication, handleWatchKitExtensionRequest userInfo: [AnyHashable : Any]?, reply: @escaping ([AnyHashable : Any]?) -> Swift.Void) {
open func application(_ application: UIApplication, handleWatchKitExtensionRequest userInfo: [AnyHashable : Any]?, reply: @escaping ([AnyHashable : Any]?) -> Swift.Void) {
for service in __services {
service.application?(application, handleWatchKitExtensionRequest: userInfo, reply: reply)
}
apply({ (service, reply) -> Void? in
service.application?(application, handleWatchKitExtensionRequest: userInfo, reply: reply)
}, completionHandler: { results in
let result = results.reduce([:], { initial, next in
var initial = initial
for (key, value) in next ?? [:] {
initial[key] = value
}
return initial
})
reply(result)
})
}
@available(iOS 9.0, *)
public func applicationShouldRequestHealthAuthorization(_ application: UIApplication) {
open func applicationShouldRequestHealthAuthorization(_ application: UIApplication) {
for service in __services {
service.applicationShouldRequestHealthAuthorization?(application)
}
@@ -298,14 +348,14 @@ open class PluggableApplicationDelegate: UIResponder, UIApplicationDelegate {
@available(iOS 4.0, *)
public func applicationDidEnterBackground(_ application: UIApplication) {
open func applicationDidEnterBackground(_ application: UIApplication) {
for service in __services {
service.applicationDidEnterBackground?(application)
}
}
@available(iOS 4.0, *)
public func applicationWillEnterForeground(_ application: UIApplication) {
open func applicationWillEnterForeground(_ application: UIApplication) {
for service in __services {
service.applicationWillEnterForeground?(application)
}
@@ -313,14 +363,14 @@ open class PluggableApplicationDelegate: UIResponder, UIApplicationDelegate {
@available(iOS 4.0, *)
public func applicationProtectedDataWillBecomeUnavailable(_ application: UIApplication) {
open func applicationProtectedDataWillBecomeUnavailable(_ application: UIApplication) {
for service in __services {
service.applicationProtectedDataWillBecomeUnavailable?(application)
}
}
@available(iOS 4.0, *)
public func applicationProtectedDataDidBecomeAvailable(_ application: UIApplication) {
open func applicationProtectedDataDidBecomeAvailable(_ application: UIApplication) {
for service in __services {
service.applicationProtectedDataDidBecomeAvailable?(application)
}
@@ -331,19 +381,19 @@ open class PluggableApplicationDelegate: UIResponder, UIApplicationDelegate {
// Constants representing common extension point identifiers are provided further down.
// If unimplemented, the default behavior is to allow the extension point identifier.
@available(iOS 8.0, *)
public func application(_ application: UIApplication, shouldAllowExtensionPointIdentifier extensionPointIdentifier: UIApplicationExtensionPointIdentifier) -> Bool {
open func application(_ application: UIApplication, shouldAllowExtensionPointIdentifier extensionPointIdentifier: UIApplicationExtensionPointIdentifier) -> Bool {
var result = false
for service in __services {
if service.application?(application, shouldAllowExtensionPointIdentifier: extensionPointIdentifier) == false {
return false
if service.application?(application, shouldAllowExtensionPointIdentifier: extensionPointIdentifier) ?? true {
result = true
}
}
return true
return result
}
@available(iOS 6.0, *)
public func application(_ application: UIApplication, viewControllerWithRestorationIdentifierPath identifierComponents: [Any], coder: NSCoder) -> UIViewController? {
open func application(_ application: UIApplication, viewControllerWithRestorationIdentifierPath identifierComponents: [Any], coder: NSCoder) -> UIViewController? {
for service in __services {
if let viewController = service.application?(application, viewControllerWithRestorationIdentifierPath: identifierComponents, coder: coder) {
return viewController
@@ -354,36 +404,36 @@ open class PluggableApplicationDelegate: UIResponder, UIApplicationDelegate {
}
@available(iOS 6.0, *)
public func application(_ application: UIApplication, shouldSaveApplicationState coder: NSCoder) -> Bool {
open func application(_ application: UIApplication, shouldSaveApplicationState coder: NSCoder) -> Bool {
var result = false
for service in __services {
if service.application?(application, shouldSaveApplicationState: coder) == true {
return true
if service.application?(application, shouldSaveApplicationState: coder) ?? false {
result = true
}
}
return false
return result
}
@available(iOS 6.0, *)
public func application(_ application: UIApplication, shouldRestoreApplicationState coder: NSCoder) -> Bool {
open func application(_ application: UIApplication, shouldRestoreApplicationState coder: NSCoder) -> Bool {
var result = false
for service in __services {
if service.application?(application, shouldRestoreApplicationState: coder) == true {
return true
if service.application?(application, shouldRestoreApplicationState: coder) ?? false {
result = true
}
}
return false
return result
}
@available(iOS 6.0, *)
public func application(_ application: UIApplication, willEncodeRestorableStateWith coder: NSCoder) {
open func application(_ application: UIApplication, willEncodeRestorableStateWith coder: NSCoder) {
for service in __services {
service.application?(application, willEncodeRestorableStateWith: coder)
}
}
@available(iOS 6.0, *)
public func application(_ application: UIApplication, didDecodeRestorableStateWith coder: NSCoder) {
open func application(_ application: UIApplication, didDecodeRestorableStateWith coder: NSCoder) {
for service in __services {
service.application?(application, didDecodeRestorableStateWith: coder)
}
@@ -395,14 +445,14 @@ open class PluggableApplicationDelegate: UIResponder, UIApplicationDelegate {
// For each application:willContinueUserActivityWithType: invocation, you are guaranteed to get exactly one invocation of application:continueUserActivity: on success,
// or application:didFailToContinueUserActivityWithType:error: if an error was encountered.
@available(iOS 8.0, *)
public func application(_ application: UIApplication, willContinueUserActivityWithType userActivityType: String) -> Bool {
open func application(_ application: UIApplication, willContinueUserActivityWithType userActivityType: String) -> Bool {
var result = false
for service in __services {
if service.application?(application, willContinueUserActivityWithType: userActivityType) == true {
return true
if service.application?(application, willContinueUserActivityWithType: userActivityType) ?? false {
result = true
}
}
return false
return result
}
@@ -411,36 +461,33 @@ open class PluggableApplicationDelegate: UIResponder, UIApplicationDelegate {
// invoked with the user activity. Invoking the restorationHandler is optional. It may be copied and invoked later, and it will bounce to the main thread to complete its work and call
// restoreUserActivityState on all objects.
@available(iOS 8.0, *)
public func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Swift.Void) -> Bool {
for service in __services {
if service.application?(application, continue: userActivity, restorationHandler: restorationHandler) == true {
return true
}
}
open func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Swift.Void) -> Bool {
let returns = apply({ (service, restorationHandler) -> Bool? in
service.application?(application, continue: userActivity, restorationHandler: restorationHandler)
}, completionHandler: { results in
let result = results.reduce([], { $0 + ($1 ?? []) })
restorationHandler(result)
})
return false
return returns.reduce(false, { $0 || $1 })
}
// If the user activity cannot be fetched after willContinueUserActivityWithType is called, this will be called on the main thread when implemented.
@available(iOS 8.0, *)
public func application(_ application: UIApplication, didFailToContinueUserActivityWithType userActivityType: String, error: Error) {
open func application(_ application: UIApplication, didFailToContinueUserActivityWithType userActivityType: String, error: Error) {
for service in __services {
service.application?(application, didFailToContinueUserActivityWithType: userActivityType, error: error)
}
return
}
// This is called on the main thread when a user activity managed by UIKit has been updated. You can use this as a last chance to add additional data to the userActivity.
@available(iOS 8.0, *)
public func application(_ application: UIApplication, didUpdate userActivity: NSUserActivity) {
open func application(_ application: UIApplication, didUpdate userActivity: NSUserActivity) {
for service in __services {
service.application?(application, didUpdate: userActivity)
}
return
}
@@ -448,11 +495,9 @@ open class PluggableApplicationDelegate: UIResponder, UIApplicationDelegate {
// You should use the CKShareMetadata object's shareURL and containerIdentifier to schedule a CKAcceptSharesOperation, then start using
// the resulting CKShare and its associated record(s), which will appear in the CKContainer's shared database in a zone matching that of the record's owner.
@available(iOS 10.0, *)
public func application(_ application: UIApplication, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShareMetadata) {
open func application(_ application: UIApplication, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShareMetadata) {
for service in __services {
service.application?(application, userDidAcceptCloudKitShareWith: cloudKitShareMetadata)
}
return
}
}
+5 -3
View File
@@ -6,7 +6,7 @@
[![Platform](https://img.shields.io/cocoapods/p/PluggableApplicationDelegate.svg?style=flat)](http://cocoapods.org/pods/PluggableApplicationDelegate)
## Introduction
`AppDelegate` is a traditional example of bad code. Lots of line codes that makes so much different things are put together in methods that are called within the application life cycle. But all of those concerns are over.
`AppDelegate` is a traditional example of bad code. Lots of line of code that makes so much different things are put together in methods that are called within the application life cycle. But all of those concerns are over.
Using `PluggableApplicationDelegate` you decouple AppDelegate from the services that you plug to it. Each `ApplicationService` has its own life cycle that is shared with `AppDelegate`.
## At a glance
@@ -54,7 +54,7 @@ Yes. That's all. Simple.
## How does this work?
You may want to read my Medium post about Pluggable App Delegate.
You may want to read my [Medium post about Pluggable App Delegate](https://medium.com/ios-os-x-development/pluggableapplicationdelegate-e50b2c5d97dd#.sz50l4d0l).
Basically, you do an inversion of control. Instead of let AppDelegate instantiate your dependencies, perform actions at every step of its life cycle, you create objects that share the AppDelegate life cycle and plug them into your AppDelegate.
Those objects are observers of the AppDelegate. Your AppDelegate has the only responsibility of notify them about its life cycle events.
@@ -64,13 +64,15 @@ To run the example project, clone the repo, and run `pod install` from the Examp
## Requirements
PluggableApplicationDelegate requires Swift 3.0 or above.
## Installation
PluggableApplicationDelegate is available through [CocoaPods](http://cocoapods.org). To install
it, simply add the following line to your Podfile:
```ruby
pod "PluggableApplicationDelegate"
pod 'PluggableApplicationDelegate'
```
## Author