From 5b63a4085167b923504b0c2a05b5aaf87fc1d44d Mon Sep 17 00:00:00 2001 From: Laurent Date: Wed, 22 Jan 2025 17:04:18 +0100 Subject: [PATCH] Various fixes and improvements --- LeStorage.xcodeproj/project.pbxproj | 4 +++ LeStorage/ApiCallCollection.swift | 1 + LeStorage/Notification+Name.swift | 18 ++++++++++++ LeStorage/Services.swift | 8 +++--- LeStorage/Store.swift | 5 +--- LeStorage/StoreCenter.swift | 30 ++++++++++++++++---- LeStorage/StoredCollection.swift | 15 ---------- LeStorage/Utils/FileManager+Extensions.swift | 11 +++++-- LeStorage/WebSocketManager.swift | 17 +++++++++-- 9 files changed, 75 insertions(+), 34 deletions(-) create mode 100644 LeStorage/Notification+Name.swift diff --git a/LeStorage.xcodeproj/project.pbxproj b/LeStorage.xcodeproj/project.pbxproj index 96cafc7..7df4434 100644 --- a/LeStorage.xcodeproj/project.pbxproj +++ b/LeStorage.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ C4339BFF2CFF86B3004E5F09 /* Dictionary+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4339BFE2CFF86B3004E5F09 /* Dictionary+Extensions.swift */; }; C456EFE22BE52379007388E2 /* StoredSingleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = C456EFE12BE52379007388E2 /* StoredSingleton.swift */; }; C45D35912C0A1DB5000F379F /* FailedAPICall.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45D35902C0A1DB5000F379F /* FailedAPICall.swift */; }; + C462E0DC2D37B61100F3E6E4 /* Notification+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = C462E0DB2D37B61100F3E6E4 /* Notification+Name.swift */; }; C467AAE32CD2467500D76CD2 /* Formatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C467AAE22CD2466400D76CD2 /* Formatter.swift */; }; C488C8802CCBDC210082001F /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C488C87F2CCBDC210082001F /* NetworkMonitor.swift */; }; C49B6E502C2089B6002BDE1B /* ApiCallCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49B6E4F2C2089B6002BDE1B /* ApiCallCollection.swift */; }; @@ -66,6 +67,7 @@ C4339BFE2CFF86B3004E5F09 /* Dictionary+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+Extensions.swift"; sourceTree = ""; }; C456EFE12BE52379007388E2 /* StoredSingleton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoredSingleton.swift; sourceTree = ""; }; C45D35902C0A1DB5000F379F /* FailedAPICall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailedAPICall.swift; sourceTree = ""; }; + C462E0DB2D37B61100F3E6E4 /* Notification+Name.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+Name.swift"; sourceTree = ""; }; C467AAE22CD2466400D76CD2 /* Formatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Formatter.swift; sourceTree = ""; }; C488C87F2CCBDC210082001F /* NetworkMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitor.swift; sourceTree = ""; }; C49B6E4F2C2089B6002BDE1B /* ApiCallCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiCallCollection.swift; sourceTree = ""; }; @@ -150,6 +152,7 @@ C49B6E4F2C2089B6002BDE1B /* ApiCallCollection.swift */, C4A47D6C2B71364600ADC637 /* ModelObject.swift */, C488C87F2CCBDC210082001F /* NetworkMonitor.swift */, + C462E0DB2D37B61100F3E6E4 /* Notification+Name.swift */, C4AC9CE92CF754CC00CC13DF /* Relationship.swift */, C4A47D602B6D3C1300ADC637 /* Services.swift */, C425D4572B6D2519002A7B48 /* Store.swift */, @@ -348,6 +351,7 @@ C4A47D4F2B6D280200ADC637 /* StoredCollection.swift in Sources */, C4A47D9C2B7CFFE000ADC637 /* Settings.swift in Sources */, C4FC2E292C2B2EC30021F3BF /* StoreCenter.swift in Sources */, + C462E0DC2D37B61100F3E6E4 /* Notification+Name.swift in Sources */, C4A47D812B7665AD00ADC637 /* Migration.swift in Sources */, C4A47D9B2B7CFFDA00ADC637 /* ApiCall.swift in Sources */, C4A47D942B7CF7C500ADC637 /* MicroStorage.swift in Sources */, diff --git a/LeStorage/ApiCallCollection.swift b/LeStorage/ApiCallCollection.swift index 403836c..f3b0afe 100644 --- a/LeStorage/ApiCallCollection.swift +++ b/LeStorage/ApiCallCollection.swift @@ -195,6 +195,7 @@ actor ApiCallCollection: SomeCallCollection { StoreCenter.main.updateLocalInstances(success) } } + self._attemptLoops = -1 } catch { Logger.error(error) } diff --git a/LeStorage/Notification+Name.swift b/LeStorage/Notification+Name.swift new file mode 100644 index 0000000..2916147 --- /dev/null +++ b/LeStorage/Notification+Name.swift @@ -0,0 +1,18 @@ +// +// Notification+Name.swift +// LeStorage +// +// Created by Laurent Morvillier on 15/01/2025. +// + + + +extension Notification.Name { + public static let CollectionDidLoad: Notification.Name = Notification.Name.init( + "notification.collectionDidLoad") + public static let CollectionDidChange: Notification.Name = Notification.Name.init( + "notification.collectionDidChange") + public static let LeStorageDidSynchronize: Notification.Name = Notification.Name.init( + "notification.leStorageDidSynchronize") + +} diff --git a/LeStorage/Services.swift b/LeStorage/Services.swift index ce99706..b4f6f56 100644 --- a/LeStorage/Services.swift +++ b/LeStorage/Services.swift @@ -86,7 +86,7 @@ public class Services { let debugURL = request.url?.absoluteString ?? "" // print("Run \(request.httpMethod ?? "") \(debugURL)") let task: (Data, URLResponse) = try await URLSession.shared.data(for: request) - print("\(apiCall.method.rawValue) \(String(describing: T.self)) => \(String(data: task.0, encoding: .utf8) ?? "")") +// print("\(apiCall.method.rawValue) \(String(describing: T.self)) => \(String(data: task.0, encoding: .utf8) ?? "")") if let response = task.1 as? HTTPURLResponse { let statusCode = response.statusCode @@ -142,7 +142,7 @@ public class Services { let debugURL = request.url?.absoluteString ?? "" // print("Run \(request.httpMethod ?? "") \(debugURL)") let task: (Data, URLResponse) = try await URLSession.shared.data(for: request) - print("\(request.httpMethod ?? "") \(debugURL) => \(String(data: task.0, encoding: .utf8) ?? "")") +// print("\(request.httpMethod ?? "") \(debugURL) => \(String(data: task.0, encoding: .utf8) ?? "")") if let response = task.1 as? HTTPURLResponse { let statusCode = response.statusCode @@ -268,7 +268,7 @@ public class Services { let debugURL = request.url?.absoluteString ?? "" // print("Run \(request.httpMethod ?? "") \(debugURL)") let task: (Data, URLResponse) = try await URLSession.shared.data(for: request) - print("sync POST \(String(describing: T.self)) => \(String(data: task.0, encoding: .utf8) ?? "")") +// print("sync POST \(String(describing: T.self)) => \(String(data: task.0, encoding: .utf8) ?? "")") var rescheduleApiCalls: Bool = false var success: [T] = [] @@ -434,7 +434,7 @@ public class Services { // print("Run \(request.httpMethod ?? "") \(debugURL)") let task: (Data, URLResponse) = try await URLSession.shared.data(for: request) - print("\(request.httpMethod ?? "") \(debugURL) => \(String(data: task.0, encoding: .utf8) ?? "")") +// print("\(request.httpMethod ?? "") \(debugURL) => \(String(data: task.0, encoding: .utf8) ?? "")") if let response = task.1 as? HTTPURLResponse { let statusCode = response.statusCode diff --git a/LeStorage/Store.swift b/LeStorage/Store.swift index 3770931..7213295 100644 --- a/LeStorage/Store.swift +++ b/LeStorage/Store.swift @@ -46,9 +46,6 @@ final public class Store { /// The store identifier, used to name the store directory, and to perform filtering requests to the server public fileprivate(set) var identifier: String? = nil - /// Indicates whether the store directory has been created at the init - fileprivate var _created: Bool = false - public init() { self._createDirectory(directory: Store.storageDirectory) } @@ -63,7 +60,7 @@ final public class Store { /// - Parameters: /// - directory: the name of the directory fileprivate func _createDirectory(directory: String) { - self._created = FileManager.default.createDirectoryInDocuments(directoryName: directory) + FileManager.default.createDirectoryInDocuments(directoryName: directory) } /// A method to provide ids corresponding to the django storage diff --git a/LeStorage/StoreCenter.swift b/LeStorage/StoreCenter.swift index 6254da2..0f2fe18 100644 --- a/LeStorage/StoreCenter.swift +++ b/LeStorage/StoreCenter.swift @@ -96,6 +96,17 @@ public class StoreCenter { Logger.log("websocket configured: \(url)") } + public var hasWebSocketManager: Bool { + return self._webSocketManager != nil + } + public var websocketPingStatus: Bool { + return self._webSocketManager?.pingStatus ?? false + } + + public var websocketFailure: Bool { + return self._webSocketManager?.failure ?? true + } + public var apiURL: String? { return self._urlManager?.api } @@ -193,6 +204,9 @@ public class StoreCenter { self.resetApiCalls() self._failedAPICallsCollection?.reset() + self._stores.removeAll() + self._dataAccess?.reset() + self._settingsStorage.update { settings in settings.username = nil settings.userId = nil @@ -516,6 +530,10 @@ public class StoreCenter { StoreCenter.main.log(message: error.localizedDescription) Logger.error(error) } + + NotificationCenter.default.post( + name: NSNotification.Name.LeStorageDidSynchronize, object: self) + } /// Processes data that should be inserted or updated inside the app @@ -721,12 +739,12 @@ public class StoreCenter { } /// Resets all registered collection - public func reset() { - Store.main.reset() - for store in self._stores.values { - store.reset() - } - } +// public func reset() { +// Store.main.reset() +// for store in self._stores.values { +// store.reset() +// } +// } /// Returns whether any collection has pending API calls public func hasPendingAPICalls() async -> Bool { diff --git a/LeStorage/StoredCollection.swift b/LeStorage/StoredCollection.swift index fc6ff73..1376ec9 100644 --- a/LeStorage/StoredCollection.swift +++ b/LeStorage/StoredCollection.swift @@ -28,13 +28,6 @@ protocol SomeSyncedCollection: SomeCollection { func loadCollectionsFromServerIfNoFile() async throws } -extension Notification.Name { - public static let CollectionDidLoad: Notification.Name = Notification.Name.init( - "notification.collectionDidLoad") - public static let CollectionDidChange: Notification.Name = Notification.Name.init( - "notification.collectionDidChange") -} - public class StoredCollection: RandomAccessCollection, SomeCollection, CollectionHolder { @@ -302,14 +295,6 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti if let index = self.items.firstIndex(where: { $0.id == item.id }) { self.items.remove(at: index) } - - // Task { - // do { - // try await StoreCenter.main.deleteApiCallByDataId(type: T.self, id: item.stringId) - // } catch { - // Logger.error(error) - // } - // } } } diff --git a/LeStorage/Utils/FileManager+Extensions.swift b/LeStorage/Utils/FileManager+Extensions.swift index 010cd89..667921f 100644 --- a/LeStorage/Utils/FileManager+Extensions.swift +++ b/LeStorage/Utils/FileManager+Extensions.swift @@ -9,18 +9,23 @@ import Foundation extension FileManager { - func createDirectoryInDocuments(directoryName: String) -> Bool { + @discardableResult func createDirectoryInDocuments(directoryName: String) -> Bool { let documentsDirectory = self.urls(for: .documentDirectory, in: .userDomainMask).first! let directoryURL = documentsDirectory.appendingPathComponent(directoryName) if !self.fileExists(atPath: directoryURL.path) { do { - try self.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) + try self.createDirectory(at: directoryURL, + withIntermediateDirectories: true, + attributes: nil) + Logger.log("directory created : \(directoryURL)") + return true } catch { Logger.error(error) + return false } - return true } else { + Logger.log("directory exists : \(directoryURL)") return false } } diff --git a/LeStorage/WebSocketManager.swift b/LeStorage/WebSocketManager.swift index 0ee81f8..bdc2053 100644 --- a/LeStorage/WebSocketManager.swift +++ b/LeStorage/WebSocketManager.swift @@ -16,6 +16,8 @@ class WebSocketManager: ObservableObject { fileprivate var _url: String fileprivate var _reconnectAttempts = 0 + fileprivate var _failure = false + fileprivate var _pingOk = false init(urlString: String) { self._url = urlString @@ -52,9 +54,11 @@ class WebSocketManager: ObservableObject { _webSocketTask?.receive { result in switch result { case .failure(let error): + self._failure = true print("Error in receiving message: \(error)") self._handleWebSocketError(error) case .success(let message): + self._failure = false switch message { case .string(let deviceId): print("device id = \(StoreCenter.main.deviceId()), origin id: \(deviceId)") @@ -107,11 +111,14 @@ class WebSocketManager: ObservableObject { private func _ping() { self._webSocketTask?.sendPing { error in -// Logger.log("ping sent. Error?: \(error?.localizedDescription ?? "none") ") if let error: NSError = error as NSError?, error.domain == NSPOSIXErrorDomain && error.code == 57 { + Logger.log("ping sent. Error?: \(error.localizedDescription) ") self._setupWebSocket() + self._pingOk = false + } else { + self._pingOk = true } } } @@ -120,5 +127,11 @@ class WebSocketManager: ObservableObject { self._webSocketTask?.cancel(with: .goingAway, reason: nil) self._timer?.invalidate() } - + + var pingStatus: Bool { + return self._pingOk + } + var failure: Bool { + return self._failure + } }