diff --git a/ios/RocketChat Watch App/Loaders/ServersLoader.swift b/ios/RocketChat Watch App/Loaders/ServersLoader.swift index ea2521ac0..e82088705 100644 --- a/ios/RocketChat Watch App/Loaders/ServersLoader.swift +++ b/ios/RocketChat Watch App/Loaders/ServersLoader.swift @@ -25,51 +25,11 @@ protocol ServersLoading { final class ServersLoader: NSObject { @Dependency private var database: ServersDatabase - private let session: WCSession + private let session: WatchSessionProtocol - init(session: WCSession) { + init(session: WatchSessionProtocol = RetriableWatchSession()) { self.session = session super.init() - session.delegate = self - session.activate() - } - - private func sendMessage(completionHandler: @escaping (Result) -> Void) { - print("sendMessage") - - guard session.activationState == .activated else { - completionHandler(.failure(.unactive)) - return - } - - guard !session.iOSDeviceNeedsUnlockAfterRebootForReachability else { - completionHandler(.failure(.locked)) - return - } - - guard session.isReachable else { - completionHandler(.failure(.unreachable)) - return - } - - session.sendMessage([:]) { dictionary in - do { - let data = try JSONSerialization.data(withJSONObject: dictionary) - let message = try JSONDecoder().decode(WatchMessage.self, from: data) - - completionHandler(.success(message)) - } catch { - completionHandler(.failure(.undecodable(error))) - } - } - } -} - -// MARK: - WCSessionDelegate - -extension ServersLoader: WCSessionDelegate { - func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { - } } @@ -78,7 +38,7 @@ extension ServersLoader: WCSessionDelegate { extension ServersLoader: ServersLoading { func loadServers() -> AnyPublisher { Future { [self] promise in - sendMessage { result in + session.sendMessage { result in switch result { case .success(let message): for server in message.servers { diff --git a/ios/RocketChat Watch App/Loaders/WatchSession.swift b/ios/RocketChat Watch App/Loaders/WatchSession.swift new file mode 100644 index 000000000..52df5bf0f --- /dev/null +++ b/ios/RocketChat Watch App/Loaders/WatchSession.swift @@ -0,0 +1,98 @@ +import WatchConnectivity + +protocol WatchSessionProtocol { + func sendMessage(completionHandler: @escaping (Result) -> Void) +} + +/// Default WatchSession protocol implementation. +final class WatchSession: NSObject, WatchSessionProtocol, WCSessionDelegate { + private let session: WCSession + + init(session: WCSession) { + self.session = session + super.init() + session.delegate = self + session.activate() + } + + func sendMessage(completionHandler: @escaping (Result) -> Void) { + guard session.activationState == .activated else { + completionHandler(.failure(.unactive)) + return + } + + guard !session.iOSDeviceNeedsUnlockAfterRebootForReachability else { + completionHandler(.failure(.locked)) + return + } + + guard session.isReachable else { + completionHandler(.failure(.unreachable)) + return + } + + session.sendMessage([:]) { dictionary in + do { + let data = try JSONSerialization.data(withJSONObject: dictionary) + let message = try JSONDecoder().decode(WatchMessage.self, from: data) + + completionHandler(.success(message)) + } catch { + completionHandler(.failure(.undecodable(error))) + } + } + } + + func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { + + } +} + +/// Retry decorator for WatchSession protocol. +final class RetriableWatchSession: WatchSessionProtocol { + private let session: WatchSessionProtocol + private let retries: Int + + init(session: WatchSessionProtocol = DelayableWatchSession(session: WatchSession(session: .default)), retries: Int = 3) { + self.session = session + self.retries = retries + } + + func sendMessage(completionHandler: @escaping (Result) -> Void) { + session.sendMessage { [weak self] result in + guard let self else { + return + } + + switch result { + case .success(let message): + completionHandler(.success(message)) + case .failure where retries > 0: + session.sendMessage(completionHandler: completionHandler) + case .failure(let error): + completionHandler(.failure(error)) + } + } + } +} + +/// Delay decorator for WatchSession protocol. +final class DelayableWatchSession: WatchSessionProtocol { + private let delay: TimeInterval + private let session: WatchSessionProtocol + + init(delay: TimeInterval = 1, session: WatchSessionProtocol) { + self.delay = delay + self.session = session + } + + func sendMessage(completionHandler: @escaping (Result) -> Void) { + Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { [weak self] _ in + guard let self else { + return + } + + self.session.sendMessage(completionHandler: completionHandler) + } + } +} diff --git a/ios/RocketChat Watch App/RocketChatApp.swift b/ios/RocketChat Watch App/RocketChatApp.swift index 4de9ddd24..d30366135 100644 --- a/ios/RocketChat Watch App/RocketChatApp.swift +++ b/ios/RocketChat Watch App/RocketChatApp.swift @@ -11,7 +11,7 @@ struct RocketChat_Watch_AppApp: App { private func registerDependencies() { Store.register(AppRouting.self, factory: router) Store.register(ServersDatabase.self, factory: DefaultDatabase()) - Store.register(ServersLoading.self, factory: ServersLoader(session: .default)) + Store.register(ServersLoading.self, factory: ServersLoader()) Store.register(MessagesLoading.self, factory: MessagesLoader()) Store.register(RoomsLoading.self, factory: RoomsLoader()) } diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj index 355f1150f..a92283f16 100644 --- a/ios/RocketChatRN.xcodeproj/project.pbxproj +++ b/ios/RocketChatRN.xcodeproj/project.pbxproj @@ -113,6 +113,7 @@ 1E76CBD825152C870067298C /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E680ED82512990700C9257A /* Request.swift */; }; 1E76CBD925152C8C0067298C /* Push.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E2F61652512958900871711 /* Push.swift */; }; 1E76CBDA25152C8E0067298C /* SendMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E598AE825151A63002BDFBD /* SendMessage.swift */; }; + 1E8979472B6063FC001D99F0 /* WatchSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E8979462B6063FC001D99F0 /* WatchSession.swift */; }; 1E9A71692B59B6E100477BA2 /* MessageSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9A71682B59B6E100477BA2 /* MessageSender.swift */; }; 1E9A716F2B59CBCA00477BA2 /* AttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9A716E2B59CBCA00477BA2 /* AttachmentView.swift */; }; 1E9A71712B59CC1300477BA2 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9A71702B59CC1300477BA2 /* Attachment.swift */; }; @@ -404,6 +405,7 @@ 1E6737FF24DC52660009E081 /* NotificationService-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NotificationService-Bridging-Header.h"; sourceTree = ""; }; 1E67380324DC529B0009E081 /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = ""; }; 1E680ED82512990700C9257A /* Request.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = ""; }; + 1E8979462B6063FC001D99F0 /* WatchSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchSession.swift; sourceTree = ""; }; 1E9A71682B59B6E100477BA2 /* MessageSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSender.swift; sourceTree = ""; }; 1E9A716E2B59CBCA00477BA2 /* AttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentView.swift; sourceTree = ""; }; 1E9A71702B59CC1300477BA2 /* Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Attachment.swift; sourceTree = ""; }; @@ -865,6 +867,7 @@ 1EDFD1052B58A66E002FEE5F /* CancelBag.swift */, 1EDFD1072B58AA77002FEE5F /* RoomsLoader.swift */, 1E9A71682B59B6E100477BA2 /* MessageSender.swift */, + 1E8979462B6063FC001D99F0 /* WatchSession.swift */, ); path = Loaders; sourceTree = ""; @@ -1185,7 +1188,6 @@ }; 1ED0388D2B507B4B00C007D4 = { CreatedOnToolsVersion = 15.0; - DevelopmentTeam = S6UPZG7ZR3; ProvisioningStyle = Automatic; }; 1EFEB5942493B6640072EDC0 = { @@ -1876,6 +1878,7 @@ 1ED033CD2B55D671004F4930 /* RocketChatDatabase.swift in Sources */, 1E29A3122B5866090093C03C /* Room.swift in Sources */, 1E29A3032B585B070093C03C /* FailableDecodable.swift in Sources */, + 1E8979472B6063FC001D99F0 /* WatchSession.swift in Sources */, 1E29A2FE2B585B070093C03C /* ReadRequest.swift in Sources */, 1E9A71692B59B6E100477BA2 /* MessageSender.swift in Sources */, 1E29A3072B585B070093C03C /* RocketChatError.swift in Sources */, @@ -2307,7 +2310,7 @@ CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_ASSET_PATHS = "\"RocketChat Watch App/Preview Content\""; - DEVELOPMENT_TEAM = S6UPZG7ZR3; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17;