OSDN Git Service

FutureにNSManagedObjectContextを監視して値を設定する関数を追加
[kcd/KCD.git] / KCD / Future.swift
1 //
2 //  Future.swift
3 //  KCD
4 //
5 //  Created by Hori,Masaki on 2018/01/13.
6 //  Copyright © 2018年 Hori,Masaki. All rights reserved.
7 //
8
9 import Cocoa
10
11
12 enum Result<T> {
13     
14     case value(T)
15     case none
16     case error(Error)
17 }
18
19 let watingQueue = DispatchQueue(label: "Future", attributes: .concurrent)
20 class Future<T> {
21     
22     private let queue: DispatchQueue
23     private let semaphore = DispatchSemaphore(value: 1)
24     
25     private var result: Result<T> = .none
26     
27     init(queue: DispatchQueue = watingQueue) {
28         
29         self.queue = queue
30         
31         self.semaphore.wait()
32     }
33     deinit {
34         semaphore.signal()
35     }
36     
37     @discardableResult
38     func onSuccess(block: @escaping (T) -> Void) -> Future<T> {
39         
40         queue.async {
41             
42             self.semaphore.wait()
43             if case .value(let value) = self.result { block(value) }
44             self.semaphore.signal()
45         }
46         
47         return self
48     }
49     
50     @discardableResult
51     func onFailure(block: @escaping (Error) -> Void) -> Future<T> {
52         
53         queue.async {
54             
55             self.semaphore.wait()
56             if case .error(let error) = self.result { block(error) }
57             self.semaphore.signal()
58         }
59         
60         return self
61     }
62     
63     func success(_ value: T) {
64         
65         switch result {
66             
67         case .value, .error:
68             Logger.shared.log("multi call success(_:)")
69             fatalError()
70             
71         case .none:
72             result = .value(value)
73             semaphore.signal()
74         }
75     }
76     
77     func failure(_ error: Error) {
78         
79         switch result {
80             
81         case .value, .error:
82             Logger.shared.log("multi call failure(_:)")
83             fatalError()
84             
85         case .none:
86             result = .error(error)
87             semaphore.signal()
88         }
89     }
90 }
91
92 extension Future {
93     
94     
95     /// Notificationを待って値を設定する
96     ///
97     /// - Parameters:
98     ///   - center: NotificationCenter. default is NotificationCenter.default
99     ///   - name: Notification.Name
100     ///   - object: 監視対象
101     ///   - block:
102     ///     Parameters: Notification
103     ///     Returns: `Result<T>` : 成功時は `.value<T>`, エラー時は `.error<Error>`, 監視を継続するときは `.none`を返す
104     func waitingNotification(_ center: NotificationCenter = .default, name: Notification.Name, object: Any?, block: @escaping (Notification) -> Result<T>) {
105         
106         weak var token: NSObjectProtocol?
107         token = center
108             .addObserver(forName: name,
109                          object: object,
110                          queue: nil) { notification in
111                             
112                             
113                             switch block(notification) {
114                                 
115                             case .value(let val): self.success(val)
116                                 
117                             case .none: return
118                                 
119                             case .error(let error): self.failure(error)
120                             }
121                             
122                             token.map(NotificationCenter.default.removeObserver)
123         }
124     }
125     
126     /// 初回起動時などにデータがない時などにCoreDataを監視する
127     ///
128     /// - Parameters:
129     ///   - coreData: 監視対象
130     ///   - block:
131     ///     Parameters: Notification (NSManagedObjectContextObjectsDidChange)
132     ///     Returns: `Result<T>` : 成功時は `.value<T>`, エラー時は `.error<Error>`, 監視を継続するときは `.none`を返す
133     func waitingCoreData(_ coreData: CoreDataProvider = ServerDataStore.default, block: @escaping (Notification) -> Result<T>) {
134         
135         waitingNotification(name: .NSManagedObjectContextObjectsDidChange, object: coreData.context, block: block)
136     }
137 }