     handleWatchKitExtensionRequest userInfo: [AnyHashable : Any]?,
     reply: @escaping ([AnyHashable : Any]?) -> Void
   ) {
-    if var model = model {
-      model.watchKitExtensionRequest = .progressing(
-        Session.Model.WatchKitExtensionRequest(
-          userInfo: userInfo,
-          reply: reply
-        )
+    var edit = model
+    edit.watchKitExtensionRequest = .progressing(
+      Session.Model.WatchKitExtensionRequest(
+        userInfo: userInfo,
+        reply: reply
       )
-      output.on(.next(model))
-    }
+    )
+    output.on(.next(edit))
   }
 
   func applicationShouldRequestHealthAuthorization(_ application: UIApplication) {
-    if var model = model {
-      model.isExperiencingHealthAuthorizationRequest = true
-      output.on(.next(model))
-      model.isExperiencingHealthAuthorizationRequest = false
-      output.on(.next(model))
-    }
+    var edit = model
+    edit.isExperiencingHealthAuthorizationRequest = true
+    output.on(.next(edit))
+    edit.isExperiencingHealthAuthorizationRequest = false
+    output.on(.next(edit))
   }
 
   func applicationDidEnterBackground(_ application: UIApplication) {
-    if var model = model {
-      model.state = .did(.resigned)
-      output.on(.next(model))
-    }
+    var edit = model
+    edit.state = .did(.resigned)
+    output.on(.next(edit))
   }
 
   func applicationWillEnterForeground(_ application: UIApplication) {
-    if var model = model {
-      model.state = .will(.active)
-      output.on(.next(model))
-    }
+    var edit = model
+    edit.state = .will(.active)
+    output.on(.next(edit))
   }
 
   func applicationProtectedDataWillBecomeUnavailable(_ application: UIApplication) {
-    if var model = model {
-      model.isProtectedDataAvailable = .will(false)
-      output.on(.next(model))
-    }
+    var edit = model
+    edit.isProtectedDataAvailable = .will(false)
+    output.on(.next(edit))
   }
 
   func applicationProtectedDataDidBecomeAvailable(_ application: UIApplication) {
-    if var model = model {
-      model.isProtectedDataAvailable = .did(true)
-      output.on(.next(model))
-    }
+    var edit = model
+    edit.isProtectedDataAvailable = .did(true)
+    output.on(.next(edit))
   }
 
   func application(
     _ application: UIApplication,
     supportedInterfaceOrientationsFor window: UIWindow?
   ) -> UIInterfaceOrientationMask {
-    if var model = model, let window = window {
-      model.interfaceOrientations += [.considering(window)]
-      output.on(.next(model))
-      return self.model?.interfaceOrientations
+    if let window = window {
+      var edit = model
+      edit.interfaceOrientations += [.considering(window)]
+      output.on(.next(edit))
+      return self.model.interfaceOrientations
         .flatMap { $0.allowed() }
         .filter { $0.window == window }
         .first

    open url: URL,
    options: [UIApplicationOpenURLOptionsKey : Any] = [:]
  ) -> Bool {
    if var model = model {
      model.URL = .considering(.ios9(url: url, options: options))
      output.on(.next(model))
    }
    if let model = model, case .allowing(let allowed) = model.URL {
      return url == allowed
    } else {
      return false

class Session: NSObject, UIApplicationDelegate {
  
  init(_ input: Model) {
    model = input
    output = BehaviorSubject<Model>(value: input)
  }
  
  static let shared = Session(.empty)
  fileprivate var disposable: Disposable?
  fileprivate let output: BehaviorSubject<Model>
  fileprivate var model: Model {
    didSet {
      if model.isIgnoringUserEvents != oldValue.isIgnoringUserEvents {
        if model.isIgnoringUserEvents {
          UIApplication.shared.beginIgnoringInteractionEvents()
        } else {
          UIApplication.shared.endIgnoringInteractionEvents()
        }
      }
      
      UIApplication.shared.isIdleTimerDisabled = model.isIdleTimerDisabled
      
      if let url = model.openingURL, url != oldValue.openingURL {
        UIApplication.shared.openURL(url)
      }
      
      if let new = model.sendingEvent {
        UIApplication.shared.sendEvent(new)
      }
      
      if let new = model.sendingAction {
        UIApplication.shared.sendAction(
          new.action,
          to: new.target,
          from: new.sender,
          for: new.event
        )
      }
      
      UIApplication.shared.isNetworkActivityIndicatorVisible = model.isNetworkActivityIndicatorVisible
      UIApplication.shared.applicationIconBadgeNumber = model.iconBadgeNumber
      UIApplication.shared.applicationSupportsShakeToEdit = model.supportsShakeToEdit
      
      Session.additions(
        new: model.pendingBackgroundTasks,
        old: oldValue.pendingBackgroundTasks
      ).forEach {
        // need to add these ID to output model's running tasks
        let ID = UIApplication.shared.beginBackgroundTask(
          withName: $0.name,
          expirationHandler: $0.expiration
        )
        var edit = model
        edit.runningBackgroundTasksIdentifiers += [ID]
        output.on(.next(edit))
        // don't have event currently. reconsider output schema.
      }
      
      Session.deletions(
        new: model.runningBackgroundTasksIdentifiers,
        old: oldValue.runningBackgroundTasksIdentifiers
      ).forEach {
        UIApplication.shared.endBackgroundTask($0)
      }
      
      switch model.minimumBackgroundFetchInterval {
      case .minimum:
        UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
      case .never:
        UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalNever)
      case .some(let value):
        UIApplication.shared.setMinimumBackgroundFetchInterval(value)
      }
      
      let deletionsTypesRegistered = Changeset(
        source: oldValue.typesRegisteredForRemoteNotifications,
        target: model.typesRegisteredForRemoteNotifications
      ).edits
      
      if deletionsTypesRegistered.count != 0 {
        UIApplication.shared.unregisterForRemoteNotifications()
        model.typesRegisteredForRemoteNotifications.forEach {
          UIApplication.shared.registerForRemoteNotifications(matching: $0)
        }
      }
      
      if
      let new = model.presentedLocalNotification,
      oldValue.presentedLocalNotification != new {
        UIApplication.shared.presentLocalNotificationNow(new)
      }
      
      Session.additions(
        new: model.scheduledLocalNotifications,
        old: oldValue.scheduledLocalNotifications
      ).forEach {
          UIApplication.shared.scheduleLocalNotification($0)
      }
      
      Session.deletions(
        new: model.scheduledLocalNotifications,
        old: oldValue.scheduledLocalNotifications
      ).forEach {
          UIApplication.shared.cancelLocalNotification($0)
      }
      
      if
        let new = model.registeredUserNotificationSettings,
        oldValue.registeredUserNotificationSettings != new {
        UIApplication.shared.registerUserNotificationSettings(new)
      }
      
      if oldValue.isReceivingRemoteControlEvents != model.isReceivingRemoteControlEvents {
        model.isReceivingRemoteControlEvents
          ? UIApplication.shared.beginReceivingRemoteControlEvents()
          : UIApplication.shared.endReceivingRemoteControlEvents()
      }
      
      if oldValue.newsStandIconImage != model.newsStandIconImage {
        UIApplication.shared.setNewsstandIconImage(model.newsStandIconImage)
      }
      
      UIApplication.shared.shortcutItems = model.shortcutItems.map { $0.value }
      
      let change = Changeset(source: oldValue.shortcutItems, target: model.shortcutItems)
        
      change
      .edits
      .flatMap { completionHandler(type: .deletion, edit: $0) }
      .forEach { $0(false) }

      change
      .edits
      .flatMap { completionHandler(type: .substitution, edit: $0) }
      .forEach { $0(true) }

    }
  }
  
