From cf1cbb7d61713be82ee6d78349ec4b69ca27eb03 Mon Sep 17 00:00:00 2001 From: Djorkaeff Alexandre Date: Fri, 19 Jan 2024 16:51:44 -0300 Subject: [PATCH] Create Dependency Injection module --- .../Extensions/Publisher+Extensions.swift | 28 +++++++++++++ .../DependencyInjection/Dependency.swift | 10 +++++ .../DependencyInjection/Store.swift | 40 +++++++++++++++++++ .../RocketChatAppRouter.swift | 16 ++++---- ios/RocketChat Watch App/Storage.swift | 12 ++++-- ios/RocketChatRN.xcodeproj/project.pbxproj | 12 ++++++ 6 files changed, 107 insertions(+), 11 deletions(-) create mode 100644 ios/RocketChat Watch App/Client/Extensions/Publisher+Extensions.swift create mode 100644 ios/RocketChat Watch App/DependencyInjection/Dependency.swift create mode 100644 ios/RocketChat Watch App/DependencyInjection/Store.swift diff --git a/ios/RocketChat Watch App/Client/Extensions/Publisher+Extensions.swift b/ios/RocketChat Watch App/Client/Extensions/Publisher+Extensions.swift new file mode 100644 index 000000000..675225b18 --- /dev/null +++ b/ios/RocketChat Watch App/Client/Extensions/Publisher+Extensions.swift @@ -0,0 +1,28 @@ +import Combine + +extension Publisher { + func retryWithDelay( + retries: Int, + delay: S.SchedulerTimeType.Stride, + scheduler: S + ) -> AnyPublisher where S: Scheduler { + self + .delayIfFailure(for: delay, scheduler: scheduler) + .retry(retries) + .eraseToAnyPublisher() + } + + private func delayIfFailure( + for delay: S.SchedulerTimeType.Stride, + scheduler: S + ) -> AnyPublisher where S: Scheduler { + self.catch { error in + Future { completion in + scheduler.schedule(after: scheduler.now.advanced(by: delay)) { + completion(.failure(error)) + } + } + } + .eraseToAnyPublisher() + } +} diff --git a/ios/RocketChat Watch App/DependencyInjection/Dependency.swift b/ios/RocketChat Watch App/DependencyInjection/Dependency.swift new file mode 100644 index 000000000..d82196b26 --- /dev/null +++ b/ios/RocketChat Watch App/DependencyInjection/Dependency.swift @@ -0,0 +1,10 @@ +@propertyWrapper +struct Dependency { + var wrappedValue: T { + guard let dependency = Store.resolve(T.self) else { + fatalError("No service of type \(ObjectIdentifier(T.self)) registered!") + } + + return dependency + } +} diff --git a/ios/RocketChat Watch App/DependencyInjection/Store.swift b/ios/RocketChat Watch App/DependencyInjection/Store.swift new file mode 100644 index 000000000..b377f09ac --- /dev/null +++ b/ios/RocketChat Watch App/DependencyInjection/Store.swift @@ -0,0 +1,40 @@ +import Foundation + +protocol StoreInterface { + static func register(_ type: T.Type, factory: @autoclosure @escaping () -> T) + static func resolve(_ type: T.Type) -> T? +} + +final class Store: StoreInterface { + private static var factories: [ObjectIdentifier: () -> Any] = [:] + private static var cache: [ObjectIdentifier: Any] = [:] + + static func register(_ type: T.Type, factory: @autoclosure @escaping () -> T) { + let identifier = ObjectIdentifier(type) + factories[identifier] = factory + } + + static func resolve(_ type: T.Type) -> T? { + let identifier = ObjectIdentifier(type) + + if let dependency = cache[identifier] { + return dependency as? T + } else { + let dependency = factories[identifier]?() as? T + + if let dependency { + cache[identifier] = dependency + } + + return dependency + } + } +} + +private final class WeakRef { + weak var value: T? + + init(value: T) { + self.value = value + } +} diff --git a/ios/RocketChat Watch App/RocketChatAppRouter.swift b/ios/RocketChat Watch App/RocketChatAppRouter.swift index be957c40e..968f1ae9f 100644 --- a/ios/RocketChat Watch App/RocketChatAppRouter.swift +++ b/ios/RocketChat Watch App/RocketChatAppRouter.swift @@ -1,7 +1,11 @@ import Foundation -final class RocketChatAppRouter: ObservableObject { - @Storage("current_server") var currentServer: URL? +protocol RocketChatAppRouting { + func route(to route: Route) +} + +final class RocketChatAppRouter: ObservableObject, RocketChatAppRouting { + @Storage(.currentServer) private var currentServer: URL? @Published var route: Route = .serverList { didSet { @@ -38,9 +42,7 @@ final class RocketChatAppRouter: ObservableObject { } } -extension RocketChatAppRouter { - enum Route { - case roomList(Server) - case serverList - } +enum Route { + case roomList(Server) + case serverList } diff --git a/ios/RocketChat Watch App/Storage.swift b/ios/RocketChat Watch App/Storage.swift index 353b604ba..e79a0277b 100644 --- a/ios/RocketChat Watch App/Storage.swift +++ b/ios/RocketChat Watch App/Storage.swift @@ -1,18 +1,22 @@ import Foundation +enum StorageKey: String { + case currentServer = "current_server" +} + @propertyWrapper struct Storage { - private let key: String + private let key: StorageKey private let defaultValue: T? - init(_ key: String, defaultValue: T? = nil) { + init(_ key: StorageKey, defaultValue: T? = nil) { self.key = key self.defaultValue = defaultValue } var wrappedValue: T? { get { - guard let data = UserDefaults.standard.object(forKey: key) as? Data else { + guard let data = UserDefaults.standard.object(forKey: key.rawValue) as? Data else { return defaultValue } @@ -22,7 +26,7 @@ struct Storage { set { let data = try? JSONEncoder().encode(newValue) - UserDefaults.standard.set(data, forKey: key) + UserDefaults.standard.set(data, forKey: key.rawValue) } } } diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj index 8fd222773..648e3519f 100644 --- a/ios/RocketChatRN.xcodeproj/project.pbxproj +++ b/ios/RocketChatRN.xcodeproj/project.pbxproj @@ -77,6 +77,9 @@ 1E2F61642512955D00871711 /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E2F61632512955D00871711 /* HTTPMethod.swift */; }; 1E2F61662512958900871711 /* Push.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E2F61652512958900871711 /* Push.swift */; }; 1E470E832513A71E00E3DD1D /* RocketChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E470E822513A71E00E3DD1D /* RocketChat.swift */; }; + 1E4AFC152B5AF09800E2AA7D /* Dependency.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E4AFC142B5AF09800E2AA7D /* Dependency.swift */; }; + 1E4AFC172B5AF09C00E2AA7D /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E4AFC162B5AF09C00E2AA7D /* Store.swift */; }; + 1E4AFC1B2B5AFC6A00E2AA7D /* Publisher+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E4AFC1A2B5AFC6A00E2AA7D /* Publisher+Extensions.swift */; }; 1E51D962251263CD00DC95DE /* MessageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E51D961251263CD00DC95DE /* MessageType.swift */; }; 1E51D965251263D600DC95DE /* NotificationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E51D964251263D600DC95DE /* NotificationType.swift */; }; 1E598AE42515057D002BDFBD /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E598AE32515057D002BDFBD /* Date+Extensions.swift */; }; @@ -385,6 +388,9 @@ 1E2F61632512955D00871711 /* HTTPMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = ""; }; 1E2F61652512958900871711 /* Push.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Push.swift; sourceTree = ""; }; 1E470E822513A71E00E3DD1D /* RocketChat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RocketChat.swift; sourceTree = ""; }; + 1E4AFC142B5AF09800E2AA7D /* Dependency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dependency.swift; sourceTree = ""; }; + 1E4AFC162B5AF09C00E2AA7D /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = ""; }; + 1E4AFC1A2B5AFC6A00E2AA7D /* Publisher+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Publisher+Extensions.swift"; sourceTree = ""; }; 1E51D961251263CD00DC95DE /* MessageType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageType.swift; sourceTree = ""; }; 1E51D964251263D600DC95DE /* NotificationType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationType.swift; sourceTree = ""; }; 1E598AE32515057D002BDFBD /* Date+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = ""; }; @@ -651,6 +657,7 @@ children = ( 1E29A3092B585B370093C03C /* Data+Extensions.swift */, 1E29A30B2B585D1D0093C03C /* String+Extensions.swift */, + 1E4AFC1A2B5AFC6A00E2AA7D /* Publisher+Extensions.swift */, ); path = Extensions; sourceTree = ""; @@ -859,6 +866,8 @@ isa = PBXGroup; children = ( 1ED033C72B55CE78004F4930 /* DependencyStore.swift */, + 1E4AFC142B5AF09800E2AA7D /* Dependency.swift */, + 1E4AFC162B5AF09C00E2AA7D /* Store.swift */, ); path = DependencyInjection; sourceTree = ""; @@ -1838,9 +1847,11 @@ 1E29A3242B5874FF0093C03C /* MessageComposerView.swift in Sources */, 1EB375892B55DBFB00AEC3D7 /* Server.swift in Sources */, 1E29A3162B5868DF0093C03C /* MessageListView.swift in Sources */, + 1E4AFC172B5AF09C00E2AA7D /* Store.swift in Sources */, 1E29A2F42B585B070093C03C /* SubscriptionsResponse.swift in Sources */, 1E29A2F92B585B070093C03C /* SubscriptionsRequest.swift in Sources */, 1E29A2F22B585B070093C03C /* HistoryResponse.swift in Sources */, + 1E4AFC152B5AF09800E2AA7D /* Dependency.swift in Sources */, 1ED033BA2B55B5F6004F4930 /* ServerView.swift in Sources */, 1E29A2D02B58582F0093C03C /* RoomView.swift in Sources */, 1E29A2FD2B585B070093C03C /* RoomsRequest.swift in Sources */, @@ -1849,6 +1860,7 @@ 1E9A716F2B59CBCA00477BA2 /* AttachmentView.swift in Sources */, 1E29A3002B585B070093C03C /* JSONAdapter.swift in Sources */, 1E29A3022B585B070093C03C /* DateCodingStrategy.swift in Sources */, + 1E4AFC1B2B5AFC6A00E2AA7D /* Publisher+Extensions.swift in Sources */, 1ED033B62B55B4A5004F4930 /* ServerListView.swift in Sources */, 1E29A3202B5871C80093C03C /* RoomFormatter.swift in Sources */, 1EDFD0FA2B589B8F002FEE5F /* MessagesLoader.swift in Sources */,