ZcashLightClientKit/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift

242 lines
7.9 KiB
Swift
Raw Normal View History

//
// SDKSynchronizer.swift
// ZcashLightClientKit
//
// Created by Francisco Gindre on 11/6/19.
// Copyright © 2019 Electric Coin Company. All rights reserved.
//
import Foundation
import UIKit
/**
Synchronizer implementation for UIKit and iOS 12+
*/
public class SDKSynchronizer: Synchronizer {
public private(set) var status: Status
public private(set) var progress: Float = 0.0
public private(set) var blockProcessor: CompactBlockProcessor?
public private(set) var initializer: Initializer
var taskIdentifier: UIBackgroundTaskIdentifier = .invalid
private var isBackgroundAllowed: Bool {
switch UIApplication.shared.backgroundRefreshStatus {
case .available:
return true
default:
return false
}
}
public init(initializer: Initializer) {
self.status = .disconnected
self.initializer = initializer
}
deinit {
NotificationCenter.default.removeObserver(self)
self.blockProcessor?.stop()
self.blockProcessor = nil
self.taskIdentifier = .invalid
}
public func start() throws {
guard let processor = initializer.blockProcessor() else {
throw SynchronizerError.initFailed
}
guard status == .stopped || status == .disconnected else {
assert(true,"warning: synchronizer started when already started") // TODO: remove this assertion some time in the near future
return
}
subscribeToProcessorNotifications(processor)
registerBackgroundActivity()
try processor.start()
self.blockProcessor = processor
}
public func stop() throws {
guard status != .stopped, status != .disconnected else { return }
guard let processor = self.blockProcessor else { return }
processor.stop(cancelTasks: true)
}
private func subscribeToAppDelegateNotifications() {
// todo: ios 13 platform specific
let center = NotificationCenter.default
center.addObserver(self,
selector: #selector(applicationDidBecomeActive(_:)),
name: UIApplication.didBecomeActiveNotification, object: nil)
center.addObserver(self, selector: #selector(applicationWillTerminate(_:)), name: UIApplication.willTerminateNotification,
object: nil)
center.addObserver(self,
selector: #selector(applicationWillResignActive(_:)),
name: UIApplication.willResignActiveNotification,
object: nil)
center.addObserver(self,
selector: #selector(applicationDidEnterBackground(_:)),
name: UIApplication.didEnterBackgroundNotification,
object: nil)
center.addObserver(self,
selector: #selector(applicationWillEnterForeground(_:)),
name: UIApplication.willEnterForegroundNotification,
object: nil)
}
private func registerBackgroundActivity() {
if self.taskIdentifier == .invalid {
self.taskIdentifier = UIApplication.shared.beginBackgroundTask(expirationHandler: {
self.blockProcessor?.stop(cancelTasks: true)
UIApplication.shared.endBackgroundTask(self.taskIdentifier)
self.taskIdentifier = .invalid
})
}
}
private func subscribeToProcessorNotifications(_ processor: CompactBlockProcessor) {
let center = NotificationCenter.default
center.addObserver(self,
selector: #selector(processorUpdated(_:)),
name: Notification.Name.blockProcessorUpdated,
object: processor)
center.addObserver(self,
selector: #selector(processorStartedDownloading(_:)),
name: Notification.Name.blockProcessorStartedDownloading,
object: processor)
center.addObserver(self,
selector: #selector(processorStartedValidating(_:)),
name: Notification.Name.blockProcessorStartedValidating,
object: processor)
center.addObserver(self,
selector: #selector(processorStartedScanning(_:)),
name: Notification.Name.blockProcessorStartedScanning,
object: processor)
center.addObserver(self,
selector: #selector(processorStopped(_:)),
name: Notification.Name.blockProcessorStopped,
object: processor)
center.addObserver(self, selector: #selector(processorFailed(_:)),
name: Notification.Name.blockProcessorFailed,
object: processor)
center.addObserver(self,
selector: #selector(processorIdle(_:)),
name: Notification.Name.blockProcessorIdle,
object: processor)
center.addObserver(self,
selector: #selector(processorTransitionUnknown(_:)),
name: Notification.Name.blockProcessorUnknownTransition,
object: processor)
}
@objc func processorUpdated(_ notification: Notification) {
guard let progress = notification.userInfo?[CompactBlockProcessorNotificationKey.progress] as? Float else {
return
}
self.progress = progress
}
// MARK: Block Processor notifications
@objc func processorStartedDownloading(_ notification: Notification) {
self.status = .syncing
}
@objc func processorStartedValidating(_ notification: Notification) {
self.status = .syncing
}
@objc func processorStartedScanning(_ notification: Notification) {
self.status = .syncing
}
@objc func processorStopped(_ notification: Notification) {
self.status = .stopped
}
@objc func processorFailed(_ notification: Notification) {
self.status = .disconnected
}
@objc func processorIdle(_ notification: Notification) {
self.status = .disconnected
}
@objc func processorTransitionUnknown(_ notification: Notification) {
self.status = .disconnected
}
// MARK: application notifications
@objc func applicationDidBecomeActive(_ notification: Notification) {
registerBackgroundActivity()
do {
try self.start()
} catch {
self.status = .disconnected
}
}
@objc func applicationDidEnterBackground(_ notification: Notification) {
if !self.isBackgroundAllowed {
do {
try self.stop()
} catch {
self.status = .disconnected
}
}
}
@objc func applicationWillEnterForeground(_ notification: Notification) {
let status = self.status
if status == .stopped || status == .disconnected {
do {
try start()
} catch {
self.status = .disconnected
}
}
}
@objc func applicationWillResignActive(_ notification: Notification) {
do {
try stop()
} catch {
print("stop failed with error: \(error)")
}
}
@objc func applicationWillTerminate(_ notification: Notification) {
do {
try stop()
} catch {}
}
}