Browse Source

common jsWorker

Ivan Valkou 5 năm trước cách đây
mục cha
commit
2a93bcd702

+ 20 - 28
FreeAPS.xcodeproj/project.pbxproj

@@ -35,12 +35,10 @@
 		3811DE4F25C9D4B800A708ED /* AuthotizedRootDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DE4A25C9D4B800A708ED /* AuthotizedRootDataFlow.swift */; };
 		3811DE5025C9D4B800A708ED /* AuthotizedRootProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DE4B25C9D4B800A708ED /* AuthotizedRootProvider.swift */; };
 		3811DE5A25C9D4D500A708ED /* CheckBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DE5225C9D4D500A708ED /* CheckBox.swift */; };
-		3811DE5B25C9D4D500A708ED /* Persisted.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DE5325C9D4D500A708ED /* Persisted.swift */; };
 		3811DE5C25C9D4D500A708ED /* Formatters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DE5425C9D4D500A708ED /* Formatters.swift */; };
 		3811DE5D25C9D4D500A708ED /* Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DE5525C9D4D500A708ED /* Publisher.swift */; };
 		3811DE5E25C9D4D500A708ED /* FlowStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DE5625C9D4D500A708ED /* FlowStack.swift */; };
 		3811DE5F25C9D4D500A708ED /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DE5725C9D4D500A708ED /* ProgressBar.swift */; };
-		3811DE6025C9D4D500A708ED /* Injected.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DE5825C9D4D500A708ED /* Injected.swift */; };
 		3811DE6125C9D4D500A708ED /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DE5925C9D4D500A708ED /* ViewModifiers.swift */; };
 		3811DE6A25C9D62600A708ED /* OnboardingBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DE6425C9D62600A708ED /* OnboardingBuilder.swift */; };
 		3811DE6B25C9D62600A708ED /* OnboardingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DE6525C9D62600A708ED /* OnboardingProvider.swift */; };
@@ -68,9 +66,6 @@
 		3811DEB025C9D88300A708ED /* BaseKeychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DE9E25C9D88300A708ED /* BaseKeychain.swift */; };
 		3811DEB125C9D88300A708ED /* Keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DE9F25C9D88300A708ED /* Keychain.swift */; };
 		3811DEB225C9D88300A708ED /* KeychainItemAccessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DEA025C9D88300A708ED /* KeychainItemAccessibility.swift */; };
-		3811DEB325C9D88300A708ED /* ImageFileStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DEA225C9D88300A708ED /* ImageFileStorage.swift */; };
-		3811DEB425C9D88300A708ED /* FileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DEA325C9D88300A708ED /* FileManager.swift */; };
-		3811DEB525C9D88300A708ED /* FileStorageError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DEA425C9D88300A708ED /* FileStorageError.swift */; };
 		3811DEB625C9D88300A708ED /* UnlockManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DEA625C9D88300A708ED /* UnlockManager.swift */; };
 		3811DEB725C9D88300A708ED /* AuthorizationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DEA825C9D88300A708ED /* AuthorizationManager.swift */; };
 		3811DEBB25C9D8DD00A708ED /* Moya in Frameworks */ = {isa = PBXBuildFile; productRef = 3811DEBA25C9D8DD00A708ED /* Moya */; };
@@ -78,6 +73,9 @@
 		3811DEC325C9D99900A708ED /* UIContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DEBF25C9D99900A708ED /* UIContainer.swift */; };
 		3811DEC425C9D99900A708ED /* NetworkContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DEC025C9D99900A708ED /* NetworkContainer.swift */; };
 		3811DEC525C9D99900A708ED /* StorageContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DEC125C9D99900A708ED /* StorageContainer.swift */; };
+		3811DEE825CA063400A708ED /* Injected.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DEE425CA063400A708ED /* Injected.swift */; };
+		3811DEEA25CA063400A708ED /* SyncAccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DEE625CA063400A708ED /* SyncAccess.swift */; };
+		3811DEEB25CA063400A708ED /* PersistedProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DEE725CA063400A708ED /* PersistedProperty.swift */; };
 		384E803425C385E60086DB71 /* JavaScriptWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 384E803325C385E60086DB71 /* JavaScriptWorker.swift */; };
 		384E803825C388640086DB71 /* Script.swift in Sources */ = {isa = PBXBuildFile; fileRef = 384E803725C388640086DB71 /* Script.swift */; };
 		388E595C25AD948C0019842D /* FreeAPSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388E595B25AD948C0019842D /* FreeAPSApp.swift */; };
@@ -119,12 +117,10 @@
 		3811DE4A25C9D4B800A708ED /* AuthotizedRootDataFlow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthotizedRootDataFlow.swift; sourceTree = "<group>"; };
 		3811DE4B25C9D4B800A708ED /* AuthotizedRootProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthotizedRootProvider.swift; sourceTree = "<group>"; };
 		3811DE5225C9D4D500A708ED /* CheckBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckBox.swift; sourceTree = "<group>"; };
-		3811DE5325C9D4D500A708ED /* Persisted.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persisted.swift; sourceTree = "<group>"; };
 		3811DE5425C9D4D500A708ED /* Formatters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Formatters.swift; sourceTree = "<group>"; };
 		3811DE5525C9D4D500A708ED /* Publisher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Publisher.swift; sourceTree = "<group>"; };
 		3811DE5625C9D4D500A708ED /* FlowStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlowStack.swift; sourceTree = "<group>"; };
 		3811DE5725C9D4D500A708ED /* ProgressBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = "<group>"; };
-		3811DE5825C9D4D500A708ED /* Injected.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Injected.swift; sourceTree = "<group>"; };
 		3811DE5925C9D4D500A708ED /* ViewModifiers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewModifiers.swift; sourceTree = "<group>"; };
 		3811DE6425C9D62600A708ED /* OnboardingBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingBuilder.swift; sourceTree = "<group>"; };
 		3811DE6525C9D62600A708ED /* OnboardingProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingProvider.swift; sourceTree = "<group>"; };
@@ -152,9 +148,6 @@
 		3811DE9E25C9D88300A708ED /* BaseKeychain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseKeychain.swift; sourceTree = "<group>"; };
 		3811DE9F25C9D88300A708ED /* Keychain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Keychain.swift; sourceTree = "<group>"; };
 		3811DEA025C9D88300A708ED /* KeychainItemAccessibility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainItemAccessibility.swift; sourceTree = "<group>"; };
-		3811DEA225C9D88300A708ED /* ImageFileStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageFileStorage.swift; sourceTree = "<group>"; };
-		3811DEA325C9D88300A708ED /* FileManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileManager.swift; sourceTree = "<group>"; };
-		3811DEA425C9D88300A708ED /* FileStorageError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileStorageError.swift; sourceTree = "<group>"; };
 		3811DEA625C9D88300A708ED /* UnlockManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnlockManager.swift; sourceTree = "<group>"; };
 		3811DEA825C9D88300A708ED /* AuthorizationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthorizationManager.swift; sourceTree = "<group>"; };
 		3811DEBE25C9D99900A708ED /* SecurityContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecurityContainer.swift; sourceTree = "<group>"; };
@@ -162,6 +155,9 @@
 		3811DEC025C9D99900A708ED /* NetworkContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkContainer.swift; sourceTree = "<group>"; };
 		3811DEC125C9D99900A708ED /* StorageContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StorageContainer.swift; sourceTree = "<group>"; };
 		3811DEC725C9DA7300A708ED /* FreeAPS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FreeAPS.entitlements; sourceTree = "<group>"; };
+		3811DEE425CA063400A708ED /* Injected.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Injected.swift; sourceTree = "<group>"; };
+		3811DEE625CA063400A708ED /* SyncAccess.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncAccess.swift; sourceTree = "<group>"; };
+		3811DEE725CA063400A708ED /* PersistedProperty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PersistedProperty.swift; sourceTree = "<group>"; };
 		384E803325C385E60086DB71 /* JavaScriptWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JavaScriptWorker.swift; sourceTree = "<group>"; };
 		384E803725C388640086DB71 /* Script.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Script.swift; sourceTree = "<group>"; };
 		388E595825AD948C0019842D /* FreeAPS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FreeAPS.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -416,7 +412,6 @@
 				3811DE9925C9D88300A708ED /* Cache */,
 				3811DE9C25C9D88300A708ED /* KeyValueStorage.swift */,
 				3811DE9D25C9D88300A708ED /* Keychain */,
-				3811DEA125C9D88300A708ED /* ImageFileStorage */,
 			);
 			path = Storage;
 			sourceTree = "<group>";
@@ -440,16 +435,6 @@
 			path = Keychain;
 			sourceTree = "<group>";
 		};
-		3811DEA125C9D88300A708ED /* ImageFileStorage */ = {
-			isa = PBXGroup;
-			children = (
-				3811DEA225C9D88300A708ED /* ImageFileStorage.swift */,
-				3811DEA325C9D88300A708ED /* FileManager.swift */,
-				3811DEA425C9D88300A708ED /* FileStorageError.swift */,
-			);
-			path = ImageFileStorage;
-			sourceTree = "<group>";
-		};
 		3811DEA525C9D88300A708ED /* UnlockManager */ = {
 			isa = PBXGroup;
 			children = (
@@ -497,6 +482,16 @@
 			path = Application;
 			sourceTree = "<group>";
 		};
+		3811DEE325CA063400A708ED /* PropertyWrappers */ = {
+			isa = PBXGroup;
+			children = (
+				3811DEE425CA063400A708ED /* Injected.swift */,
+				3811DEE625CA063400A708ED /* SyncAccess.swift */,
+				3811DEE725CA063400A708ED /* PersistedProperty.swift */,
+			);
+			path = PropertyWrappers;
+			sourceTree = "<group>";
+		};
 		388E594F25AD948C0019842D = {
 			isa = PBXGroup;
 			children = (
@@ -554,11 +549,10 @@
 		388E5A5A25B6F05F0019842D /* Helpers */ = {
 			isa = PBXGroup;
 			children = (
+				3811DEE325CA063400A708ED /* PropertyWrappers */,
 				3811DE5225C9D4D500A708ED /* CheckBox.swift */,
 				3811DE5625C9D4D500A708ED /* FlowStack.swift */,
 				3811DE5425C9D4D500A708ED /* Formatters.swift */,
-				3811DE5825C9D4D500A708ED /* Injected.swift */,
-				3811DE5325C9D4D500A708ED /* Persisted.swift */,
 				3811DE5725C9D4D500A708ED /* ProgressBar.swift */,
 				3811DE5525C9D4D500A708ED /* Publisher.swift */,
 				3811DE5925C9D4D500A708ED /* ViewModifiers.swift */,
@@ -647,6 +641,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				3811DE2325C9D48300A708ED /* MainDataFlow.swift in Sources */,
+				3811DEEB25CA063400A708ED /* PersistedProperty.swift in Sources */,
 				388E5A6025B6F2310019842D /* Autosens.swift in Sources */,
 				3811DE8B25C9D6DD00A708ED /* RequestPermissionsBuilder.swift in Sources */,
 				3811DE4C25C9D4B800A708ED /* AuthotizedRootBuilder.swift in Sources */,
@@ -657,7 +652,6 @@
 				3895E4C625B9E00D00214B37 /* Profile.swift in Sources */,
 				3811DE6E25C9D62600A708ED /* OnboardingViewModel.swift in Sources */,
 				3811DE6D25C9D62600A708ED /* OnboardingRootView.swift in Sources */,
-				3811DEB525C9D88300A708ED /* FileStorageError.swift in Sources */,
 				3811DE3025C9D49500A708ED /* HomeViewModel.swift in Sources */,
 				3811DE0A25C9D32F00A708ED /* BaseModuleBuilder.swift in Sources */,
 				3811DE1725C9D40400A708ED /* Screen.swift in Sources */,
@@ -666,7 +660,6 @@
 				3811DE0925C9D32F00A708ED /* BaseViewModel.swift in Sources */,
 				3811DEB125C9D88300A708ED /* Keychain.swift in Sources */,
 				3811DE7B25C9D6D300A708ED /* LoginProvider.swift in Sources */,
-				3811DEB425C9D88300A708ED /* FileManager.swift in Sources */,
 				3811DE4125C9D4A100A708ED /* SettingsRootView.swift in Sources */,
 				388E595C25AD948C0019842D /* FreeAPSApp.swift in Sources */,
 				3811DE8925C9D6DD00A708ED /* RequestPermissionsProvider.swift in Sources */,
@@ -679,6 +672,7 @@
 				388E5A5C25B6F0770019842D /* JSON.swift in Sources */,
 				3811DEB625C9D88300A708ED /* UnlockManager.swift in Sources */,
 				3811DE1825C9D40400A708ED /* Router.swift in Sources */,
+				3811DEE825CA063400A708ED /* Injected.swift in Sources */,
 				3811DEAF25C9D88300A708ED /* KeyValueStorage.swift in Sources */,
 				3811DE4E25C9D4B800A708ED /* AuthotizedRootRootView.swift in Sources */,
 				3811DE7D25C9D6D300A708ED /* LoginRootView.swift in Sources */,
@@ -705,17 +699,15 @@
 				3811DE3325C9D49500A708ED /* HomeBuilder.swift in Sources */,
 				3811DEA925C9D88300A708ED /* AppearanceManager.swift in Sources */,
 				3811DE2125C9D48300A708ED /* MainBuilder.swift in Sources */,
-				3811DEB325C9D88300A708ED /* ImageFileStorage.swift in Sources */,
 				3811DE7925C9D6D300A708ED /* LoginViewModel.swift in Sources */,
 				3811DEAB25C9D88300A708ED /* HTTPResponseStatus.swift in Sources */,
 				3811DE5F25C9D4D500A708ED /* ProgressBar.swift in Sources */,
+				3811DEEA25CA063400A708ED /* SyncAccess.swift in Sources */,
 				3811DE4F25C9D4B800A708ED /* AuthotizedRootDataFlow.swift in Sources */,
-				3811DE6025C9D4D500A708ED /* Injected.swift in Sources */,
 				3811DE5025C9D4B800A708ED /* AuthotizedRootProvider.swift in Sources */,
 				3811DE6C25C9D62600A708ED /* OnboardingDataFlow.swift in Sources */,
 				3811DE2425C9D48300A708ED /* MainViewModel.swift in Sources */,
 				3811DE3F25C9D4A100A708ED /* SettingsViewModel.swift in Sources */,
-				3811DE5B25C9D4D500A708ED /* Persisted.swift in Sources */,
 				3811DEB725C9D88300A708ED /* AuthorizationManager.swift in Sources */,
 				3811DEB025C9D88300A708ED /* BaseKeychain.swift in Sources */,
 				3811DE4D25C9D4B800A708ED /* AuthotizedRootViewModel.swift in Sources */,

+ 0 - 11
FreeAPS/Sources/Containers/StorageContainer.swift

@@ -8,16 +8,5 @@ enum StorageContainer {
         }
 
         container.register(Keychain.self) { _ in BaseKeychain() }
-
-        container.register(IsDrinkImageFileStorage.self) { r in BaseImageFileStorage(resolver: r, name: "IsDrink")
-        }
-        container
-            .register(DrinkTypeImageFileStorage.self) { r in BaseImageFileStorage(resolver: r, name: "DrinkType")
-            }
     }
 }
-
-protocol IsDrinkImageFileStorage: ImageFileStorage {}
-protocol DrinkTypeImageFileStorage: ImageFileStorage {}
-extension BaseImageFileStorage: IsDrinkImageFileStorage {}
-extension BaseImageFileStorage: DrinkTypeImageFileStorage {}

+ 0 - 19
FreeAPS/Sources/Helpers/Persisted.swift

@@ -1,19 +0,0 @@
-import Foundation
-
-/// Attention! Do not use this wrapper for mutating structure with `didSet` handler into property owner!
-/// `didSet` will never called if structure mutate into itself (by "mutating functions").
-@propertyWrapper
-struct Persisted<Value: Codable> {
-    var wrappedValue: Value? {
-        set { storage.setValue(newValue, forKey: key) }
-        get { storage.getValue(Value.self, forKey: key) }
-    }
-
-    private let key: String
-    private let storage: KeyValueStorage
-
-    init(key: String, storage: KeyValueStorage = UserDefaults.standard) {
-        self.storage = storage
-        self.key = key
-    }
-}

+ 1 - 2
FreeAPS/Sources/Helpers/Injected.swift

@@ -4,8 +4,7 @@ protocol Injectable {
     func injectServices(_ resolver: Resolver)
 }
 
-@propertyWrapper
-final class Injected<Resolve, Service>: Resolvable {
+@propertyWrapper final class Injected<Resolve, Service>: Resolvable {
     var wrappedValue: Service!
 
     init(as _: Resolve.Type) {}

+ 48 - 0
FreeAPS/Sources/Helpers/PropertyWrappers/PersistedProperty.swift

@@ -0,0 +1,48 @@
+import Foundation
+
+/// Attention! Do not use this wrapper for mutating structure with `didSet` handler into property owner!
+/// `didSet` will never called if structure mutate into itself (by "mutating functions").
+@propertyWrapper struct Persisted<Value: Codable & Equatable> {
+    var wrappedValue: Value {
+        get { getValue() ?? initialValue }
+        set { setValue(newValue) }
+    }
+
+    private func getValue() -> Value? {
+        lock?.lock()
+        defer { lock?.unlock() }
+        return storage.getValue(Value.self, forKey: key)
+    }
+
+    private mutating func setValue(_ value: Value) {
+        lock?.lock()
+        defer { lock?.unlock() }
+        storage.setValue(value, forKey: key)
+    }
+
+    private let key: String
+    private let storage: KeyValueStorage
+    private let lock: NSRecursiveLock?
+    private let initialValue: Value
+    var isInitialValue: Bool {
+        if let value = getValue() {
+            return value == initialValue
+        }
+        return true
+    }
+
+    init(
+        wrappedValue: Value,
+        key: String,
+        storage: KeyValueStorage = UserDefaults.standard,
+        lock: NSRecursiveLock? = nil
+    ) {
+        self.storage = storage
+        self.key = key
+        self.lock = lock
+        initialValue = wrappedValue
+        lock?.lock()
+        defer { lock?.unlock() }
+        setValue(storage.getValue(Value.self, forKey: key) ?? wrappedValue)
+    }
+}

+ 32 - 0
FreeAPS/Sources/Helpers/PropertyWrappers/SyncAccess.swift

@@ -0,0 +1,32 @@
+import Foundation
+
+@propertyWrapper class SyncAccess<T> {
+    var wrappedValue: T {
+        get {
+            lock.lock()
+            defer { lock.unlock() }
+            return value
+        }
+        set {
+            lock.lock()
+            defer { lock.unlock() }
+            value = newValue
+        }
+    }
+
+    var projectedValue: SyncAccess<T> { self }
+
+    private var value: T
+    private let lock: NSRecursiveLock
+
+    init(wrappedValue: T) {
+        value = wrappedValue
+        lock = NSRecursiveLock()
+        lock.name = "SyncAccess::\(Unmanaged.passUnretained(self).toOpaque())"
+    }
+
+    init(wrappedValue: T, lock: NSRecursiveLock) {
+        value = wrappedValue
+        self.lock = lock
+    }
+}

+ 21 - 17
FreeAPS/Sources/OpenAPS/JavaScriptWorker.swift

@@ -8,42 +8,38 @@
 import Foundation
 import JavaScriptCore
 
+private let contextLock = NSRecursiveLock()
+
 final class JavaScriptWorker {
     private let processQueue = DispatchQueue(label: "DispatchQueue.JavaScriptWorker")
     private let virtualMachine: JSVirtualMachine
-    private let context: JSContext
+    @SyncAccess(lock: contextLock) private var commonContext: JSContext? = nil
 
     init() {
         virtualMachine = processQueue.sync { JSVirtualMachine()! }
-        context = JSContext(virtualMachine: virtualMachine)!
+    }
+
+    private func createContext() -> JSContext {
+        let context = JSContext(virtualMachine: virtualMachine)!
         context.exceptionHandler = { _, exception in
             if let error = exception?.toString() {
                 print(error)
             }
         }
+        return context
     }
 
     @discardableResult
     func evaluate(script: Script) -> JSValue! {
-        context.evaluateScript(script.body)
-    }
-
-    @discardableResult
-    func evaluate(string: String) -> JSValue! {
-        context.evaluateScript(string)
+        evaluate(string: script.body)
     }
 
-    subscript(key: String) -> JSValue! {
-        get {
-            context.objectForKeyedSubscript(key)
-        }
-        set(newValue) {
-            context.setObject(newValue, forKeyedSubscript: key as NSString)
-        }
+    private func evaluate(string: String) -> JSValue! {
+        let ctx = commonContext ?? createContext()
+        return ctx.evaluateScript(string)
     }
 
-
-    func json(for string: String) -> JSON {
+    private func json(for string: String) -> JSON {
         evaluate(string: "JSON.stringify(\(string));")!.toString()!
     }
 
@@ -51,4 +47,12 @@ final class JavaScriptWorker {
         let joined = arguments.map(\.string).joined(separator: ",")
         return json(for: "\(function)(\(joined))")
     }
+
+    func inCommonContext<Value>(execute: (JavaScriptWorker) -> Value) -> Value{
+        commonContext = createContext()
+        defer {
+            commonContext = nil
+        }
+        return execute(self)
+    }
 }

+ 139 - 121
FreeAPS/Sources/OpenAPS/OpenAPS.swift

@@ -9,98 +9,113 @@ import Foundation
 import JavaScriptCore
 
 final class OpenAPS {
+    private let jsWorker = JavaScriptWorker()
+    private let processQueue = DispatchQueue(label: "OpenAPS.processQueue", qos: .utility)
+
     func test() {
-        let pumphistory = loadJSON(name: "pumphistory")
-        let profile = loadJSON(name: "profile")
-        let basalProfile = loadJSON(name: "basal_profile")
-        let clock = loadJSON(name: "clock")
-        let carbs = loadJSON(name: "carbhistory")
-        let glucose = loadJSON(name: "glucose")
-        let currentTemp = loadJSON(name: "temp_basal")
-        let reservoir = 100
-        let tsMilliseconds: Double = 1527924300000
-
-        let autosensResult = autosense(
-            pumpHistory: pumphistory,
-            profile: profile,
-            carbs: carbs,
-            glucose: glucose,
-            basalprofile: basalProfile,
-            temptargets: "null"
-        )
-        print("AUTOSENS: \(autosensResult)")
-
-        let iobResult = iob(
-            pumphistory: pumphistory,
-            profile: profile,
-            clock: clock,
-            autosens: autosensResult,
-            pumphistory24: "null"
-        )
-        print("IOB: \(iobResult)")
-
-        let mealResult = meal(
-            pumphistory: pumphistory,
-            profile: profile,
-            basalProfile: basalProfile,
-            clock: clock,
-            carbs: carbs,
-            glucose: glucose
-        )
-
-        print("MEAL: \(mealResult)")
-
-        let glucoseStatus = glucoseGetLast(glucose: glucose)
-        print("GLUCOSE STATUS: \(glucoseStatus)")
-
-        let suggested = determineBasal(
-            glucoseStatus: glucoseStatus,
-            currentTemp: currentTemp,
-            iob: iobResult,
-            profile: profile,
-            aurosens: autosensResult,
-            meal: mealResult,
-            microBolusAllowed: true,
-            reservoir: reservoir,
-            tsMilliseconds: tsMilliseconds
-        )
-        print("SUGGESTED: \(suggested)")
+        processQueue.async {
+            let now = Date()
+            print("START at \(now)")
+            let pumphistory = self.loadJSON(name: "pumphistory")
+            let profile = self.loadJSON(name: "profile")
+            let basalProfile = self.loadJSON(name: "basal_profile")
+            let clock = self.loadJSON(name: "clock")
+            let carbs = self.loadJSON(name: "carbhistory")
+            let glucose = self.loadJSON(name: "glucose")
+            let currentTemp = self.loadJSON(name: "temp_basal")
+            let reservoir = 100
+            let tsMilliseconds: Double = 1527924300000
+
+            let autosensResult = self.autosense(
+                pumpHistory: pumphistory,
+                profile: profile,
+                carbs: carbs,
+                glucose: glucose,
+                basalprofile: basalProfile,
+                temptargets: "null"
+            )
+            print("AUTOSENS: \(autosensResult)")
+
+            let iobResult = self.iob(
+                pumphistory: pumphistory,
+                profile: profile,
+                clock: clock,
+                autosens: autosensResult,
+                pumphistory24: "null"
+            )
+            print("IOB: \(iobResult)")
+
+            let mealResult = self.meal(
+                pumphistory: pumphistory,
+                profile: profile,
+                basalProfile: basalProfile,
+                clock: clock,
+                carbs: carbs,
+                glucose: glucose
+            )
+
+            print("MEAL: \(mealResult)")
+
+            let glucoseStatus = self.glucoseGetLast(glucose: glucose)
+            print("GLUCOSE STATUS: \(glucoseStatus)")
+
+            let suggested = self.determineBasal(
+                glucoseStatus: glucoseStatus,
+                currentTemp: currentTemp,
+                iob: iobResult,
+                profile: profile,
+                aurosens: autosensResult,
+                meal: mealResult,
+                microBolusAllowed: true,
+                reservoir: reservoir,
+                tsMilliseconds: tsMilliseconds
+            )
+            print("SUGGESTED: \(suggested)")
+            let finishDate = Date()
+            print("FINISH at \(finishDate), duration \(finishDate.timeIntervalSince(now)) s")
+        }
     }
 
-    func iob(pumphistory: JSON, profile: JSON, clock: JSON, autosens: JSON, pumphistory24: JSON) -> JSON {
-        let jsWorker = JavaScriptWorker()
-        jsWorker.evaluate(script: Script(name:"iob-bundle"))
-        jsWorker.evaluate(script: Script(name:"prepare-iob"))
-        return jsWorker.call(function: "generate", with: [
-            pumphistory,
-            profile,
-            clock,
-            autosens,
-            pumphistory24
-        ])
+    private func iob(pumphistory: JSON, profile: JSON, clock: JSON, autosens: JSON, pumphistory24: JSON) -> JSON {
+        dispatchPrecondition(condition: .onQueue(processQueue))
+        return jsWorker.inCommonContext { worker in
+            worker.evaluate(script: Script(name:"iob-bundle"))
+            worker.evaluate(script: Script(name:"prepare-iob"))
+            return worker.call(function: "generate", with: [
+                pumphistory,
+                profile,
+                clock,
+                autosens,
+                pumphistory24
+            ])
+        }
     }
 
-    func meal(pumphistory: JSON, profile: JSON, basalProfile: JSON, clock: JSON, carbs: JSON, glucose: JSON) -> JSON {
-        let jsWorker = JavaScriptWorker()
-        jsWorker.evaluate(script: Script(name:"meal-bundle"))
-        jsWorker.evaluate(script: Script(name:"prepare-meal"))
-        return jsWorker.call(function: "generate", with: [
-            pumphistory,
-            profile,
-            basalProfile,
-            clock,
-            carbs,
-            glucose
-        ])
+    private func meal(pumphistory: JSON, profile: JSON, basalProfile: JSON, clock: JSON, carbs: JSON, glucose: JSON) -> JSON {
+        dispatchPrecondition(condition: .onQueue(processQueue))
+        return jsWorker.inCommonContext { worker in
+            worker.evaluate(script: Script(name:"meal-bundle"))
+            worker.evaluate(script: Script(name:"prepare-meal"))
+            return worker.call(function: "generate", with: [
+                pumphistory,
+                profile,
+                basalProfile,
+                clock,
+                carbs,
+                glucose
+            ])
+        }
     }
 
-    func glucoseGetLast(glucose: JSON) -> JSON {
-        let jsWorker = JavaScriptWorker()
-        jsWorker.evaluate(script: Script(name:"glucose-get-last-bundle"))
-        return jsWorker.call(function: "freeaps", with: [glucose])
+    private func glucoseGetLast(glucose: JSON) -> JSON {
+        dispatchPrecondition(condition: .onQueue(processQueue))
+        return jsWorker.inCommonContext { worker in
+            worker.evaluate(script: Script(name:"glucose-get-last-bundle"))
+            return worker.call(function: "freeaps", with: [glucose])
+        }
     }
 
-    func determineBasal(
+    private func determineBasal(
         glucoseStatus: JSON,
         currentTemp: JSON,
         iob: JSON,
@@ -111,31 +126,32 @@ final class OpenAPS {
         reservoir: Int,
         tsMilliseconds: Double
     ) -> JSON {
-        let jsWorker = JavaScriptWorker()
-
-        jsWorker.evaluate(script: Script(name:"basal-set-temp-bundle"))
-        jsWorker.evaluate(script: Script(name:"prepare-determine-basal"))
-        let funcKey = "tempBasalFunctions"
-        jsWorker.evaluate(script: Script(name:"determine-basal-bundle"))
-
-        return jsWorker.call(
-            function: "freeaps",
-            with: [
-                glucoseStatus,
-                currentTemp,
-                iob,
-                profile,
-                aurosens,
-                meal,
-                funcKey,
-                microBolusAllowed,
-                reservoir,
-                tsMilliseconds
-            ]
-        )
+        dispatchPrecondition(condition: .onQueue(processQueue))
+        return jsWorker.inCommonContext { worker in
+            worker.evaluate(script: Script(name:"basal-set-temp-bundle"))
+            worker.evaluate(script: Script(name:"prepare-determine-basal"))
+            let funcKey = "tempBasalFunctions"
+            worker.evaluate(script: Script(name:"determine-basal-bundle"))
+
+            return worker.call(
+                function: "freeaps",
+                with: [
+                    glucoseStatus,
+                    currentTemp,
+                    iob,
+                    profile,
+                    aurosens,
+                    meal,
+                    funcKey,
+                    microBolusAllowed,
+                    reservoir,
+                    tsMilliseconds
+                ]
+            )
+        }
     }
 
-    func autosense(
+    private func autosense(
         pumpHistory: JSON,
         profile: JSON,
         carbs: JSON,
@@ -143,21 +159,23 @@ final class OpenAPS {
         basalprofile: JSON,
         temptargets: JSON
     ) -> JSON {
-        let jsWorker = JavaScriptWorker()
-        jsWorker.evaluate(script: Script(name:"autosens-bundle"))
-        jsWorker.evaluate(script: Script(name:"prepare-autosens"))
-
-        return jsWorker.call(
-            function: "generate",
-            with: [
-                pumpHistory,
-                profile,
-                carbs,
-                glucose,
-                basalprofile,
-                temptargets
-            ]
-        )
+        dispatchPrecondition(condition: .onQueue(processQueue))
+        return jsWorker.inCommonContext { worker in
+            worker.evaluate(script: Script(name:"autosens-bundle"))
+            worker.evaluate(script: Script(name:"prepare-autosens"))
+
+            return worker.call(
+                function: "generate",
+                with: [
+                    pumpHistory,
+                    profile,
+                    carbs,
+                    glucose,
+                    basalprofile,
+                    temptargets
+                ]
+            )
+        }
     }
 
     private func loadJSON(name: String) -> String {

+ 0 - 48
FreeAPS/Sources/Services/Storage/ImageFileStorage/FileManager.swift

@@ -1,48 +0,0 @@
-import Foundation
-
-protocol FileManager {
-    var temporaryDirectory: URL { get }
-
-    func url(
-        for: Foundation.FileManager.SearchPathDirectory,
-        in: Foundation.FileManager.SearchPathDomainMask,
-        appropriateFor: URL?,
-        create: Bool
-    ) throws -> URL
-
-    func urls(
-        for: Foundation.FileManager.SearchPathDirectory,
-        in: Foundation.FileManager.SearchPathDomainMask
-    ) -> [URL]
-
-    func enumerator(
-        at: URL,
-        includingPropertiesForKeys: [URLResourceKey]?,
-        options: Foundation.FileManager.DirectoryEnumerationOptions,
-        errorHandler: ((URL, Error) -> Bool)?
-    ) -> Foundation.FileManager.DirectoryEnumerator?
-
-    func createDirectory(
-        at: URL,
-        withIntermediateDirectories: Bool,
-        attributes: [FileAttributeKey: Any]?
-    ) throws
-
-    func createFile(
-        atPath: String,
-        contents: Data?,
-        attributes: [FileAttributeKey: Any]?
-    ) -> Bool
-
-    func removeItem(at: URL) throws
-
-    func moveItem(at: URL, to: URL) throws
-
-    func fileExists(atPath: String) -> Bool
-
-    func attributesOfItem(atPath: String) throws -> [FileAttributeKey: Any]
-
-    func contents(atPath: String) -> Data?
-}
-
-extension Foundation.FileManager: FileManager {}

+ 0 - 9
FreeAPS/Sources/Services/Storage/ImageFileStorage/FileStorageError.swift

@@ -1,9 +0,0 @@
-import Foundation
-
-enum FileStorageError: Error {
-    case cannotCreateDirectory(url: URL, error: Error)
-    case cannotCreateFile(url: URL)
-    case cannotLoadDataFromDisk(url: URL, error: Error)
-    case cannotConvertData(url: URL)
-    case fileNotExist
-}

+ 0 - 135
FreeAPS/Sources/Services/Storage/ImageFileStorage/ImageFileStorage.swift

@@ -1,135 +0,0 @@
-import Combine
-import CoreImage
-import Swinject
-
-protocol ImageFileStorage: AnyObject {
-    var name: String { get }
-
-    func saveImage(_: CIImage, imageClass: String) -> AnyPublisher<URL, FileStorageError>
-
-    func imageClasses() -> [String]
-    func fileURLs(imageClass: String) -> [URL]
-
-    func moveImage(url: URL, toImageClass: String) -> AnyPublisher<Void, FileStorageError>
-
-    func remove(url: URL)
-}
-
-final class BaseImageFileStorage: ImageFileStorage, Injectable {
-    let name: String
-
-    @Injected() private var fileManager: FileManager!
-
-    private let processQueue = DispatchQueue(label: "BaseImageFileStorage.processQueue")
-    private var lifetime = Set<AnyCancellable>()
-
-    private lazy var directoryURL: URL = {
-        let url = try! fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
-        return url.appendingPathComponent(name, isDirectory: true)
-    }()
-
-    init(
-        resolver: Resolver,
-        name: String
-    ) {
-        self.name = name
-        injectServices(resolver)
-    }
-
-    func saveImage(_ image: CIImage, imageClass: String) -> AnyPublisher<URL, FileStorageError> {
-        Future<URL, FileStorageError> { promise in
-            self.createDirectoryIfNeeded(imageClass: imageClass)
-                .receive(on: self.processQueue)
-                .sink(receiveCompletion: { res in
-                    switch res {
-                    case .finished: break
-                    case let .failure(error):
-                        promise(.failure(error))
-                    }
-                }, receiveValue: { url in
-                    let id = UUID().uuidString
-                    let fileURL = url.appendingPathComponent(id).appendingPathExtension("jpeg")
-
-                    let imageData = CIContext().jpegRepresentation(
-                        of: image,
-                        colorSpace: CGColorSpaceCreateDeviceRGB()
-                    )!
-                    if self.fileManager.createFile(atPath: fileURL.path, contents: imageData, attributes: nil) {
-                        promise(.success(fileURL))
-                    } else {
-                        promise(.failure(.cannotCreateFile(url: fileURL)))
-                    }
-
-                })
-                .store(in: &self.lifetime)
-        }.eraseToAnyPublisher()
-    }
-
-    func imageClasses() -> [String] {
-        var urls = fileManager.enumerator(
-            at: directoryURL,
-            includingPropertiesForKeys: nil,
-            options: [.skipsSubdirectoryDescendants, .skipsPackageDescendants, .skipsHiddenFiles],
-            errorHandler: nil
-        )?.allObjects as? [URL] ?? []
-
-        urls = urls.filter { $0.hasDirectoryPath }
-
-        let classes = urls.map(\.lastPathComponent)
-
-        return classes
-    }
-
-    func fileURLs(imageClass: String) -> [URL] {
-        var urls = fileManager.enumerator(
-            at: directory(imageClass: imageClass),
-            includingPropertiesForKeys: nil,
-            options: [.skipsSubdirectoryDescendants, .skipsPackageDescendants, .skipsHiddenFiles],
-            errorHandler: nil
-        )?.allObjects as? [URL] ?? []
-
-        urls = urls.filter { $0.pathExtension == "jpeg" }
-
-        return urls
-    }
-
-    func moveImage(url: URL, toImageClass imageClass: String) -> AnyPublisher<Void, FileStorageError> {
-        createDirectoryIfNeeded(imageClass: imageClass)
-            .map { (folderURL: URL) -> Void in
-                let toURL = folderURL.appendingPathComponent(url.lastPathComponent)
-                try? self.fileManager.moveItem(at: url, to: toURL)
-            }.eraseToAnyPublisher()
-    }
-
-    func remove(url: URL) {
-        processQueue.async {
-            try? self.fileManager.removeItem(at: url)
-        }
-    }
-
-    private func directory(imageClass: String) -> URL {
-        directoryURL.appendingPathComponent(imageClass)
-    }
-
-    private func createDirectoryIfNeeded(imageClass: String) -> AnyPublisher<URL, FileStorageError> {
-        Future<URL, FileStorageError> { promise in
-            self.processQueue.async {
-                let dirURL = self.directory(imageClass: imageClass)
-                guard !self.fileManager.fileExists(atPath: dirURL.path) else {
-                    promise(.success(dirURL))
-                    return
-                }
-                do {
-                    try self.fileManager.createDirectory(
-                        at: dirURL,
-                        withIntermediateDirectories: true,
-                        attributes: nil
-                    )
-                    promise(.success(dirURL))
-                } catch {
-                    promise(.failure(.cannotCreateDirectory(url: dirURL, error: error)))
-                }
-            }
-        }.eraseToAnyPublisher()
-    }
-}