diff --git a/src/iOS.Core/Services/WatchDeviceService.cs b/src/iOS.Core/Services/WatchDeviceService.cs index 89fda8da9..250d4788c 100644 --- a/src/iOS.Core/Services/WatchDeviceService.cs +++ b/src/iOS.Core/Services/WatchDeviceService.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Bit.App.Services; using Bit.Core.Abstractions; using Bit.Core.Models; +using Bit.Core.Utilities; using Newtonsoft.Json; using WatchConnectivity; @@ -11,12 +12,16 @@ namespace Bit.iOS.Core.Services { public class WatchDeviceService : BaseWatchDeviceService { + const string ACTION_MESSAGE_KEY = "actionMessage"; + const string TRIGGER_SYNC_ACTION_KEY = "triggerSync"; + public WatchDeviceService(ICipherService cipherService, IEnvironmentService environmentService, IStateService stateService, IVaultTimeoutService vaultTimeoutService) : base(cipherService, environmentService, stateService, vaultTimeoutService) { + WCSessionManager.SharedManager.OnMessagedReceived += OnMessagedReceived; } public override bool IsConnected => WCSessionManager.SharedManager.IsSessionActivated; @@ -44,5 +49,17 @@ namespace Bit.iOS.Core.Services { WCSessionManager.SharedManager.StartSession(); } + + private void OnMessagedReceived(WCSession session, Dictionary data) + { + if (data != null + && + data.TryGetValue(ACTION_MESSAGE_KEY, out var action) + && + action as string == TRIGGER_SYNC_ACTION_KEY) + { + SyncDataToWatchAsync().FireAndForget(); + } + } } } diff --git a/src/iOS.Core/Utilities/DictionaryExtensions.cs b/src/iOS.Core/Utilities/DictionaryExtensions.cs index 38379209a..de3387237 100644 --- a/src/iOS.Core/Utilities/DictionaryExtensions.cs +++ b/src/iOS.Core/Utilities/DictionaryExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Bit.Core.Models.Domain; using Foundation; using Newtonsoft.Json; @@ -21,6 +22,25 @@ namespace Bit.iOS.Core.Utilities var NSKeys = dict.Keys.Select(x => keyConverter(x)).ToArray(); return NSDictionary.FromObjectsAndKeys(NSValues, NSKeys, NSKeys.Count()); } + + public static Dictionary ToDictionary(this NSDictionary nsDict) + { + return nsDict.ToDictionary(v => v?.ToString() as object); + } + + public static Dictionary ToDictionary(this NSDictionary nsDict, Func valueTransformer) + { + return nsDict.ToDictionary(k => k.ToString(), v => valueTransformer(v)); + } + + public static Dictionary ToDictionary(this NSDictionary nsDict, Func keyConverter, Func valueConverter) + where KFrom : NSObject + where VFrom : NSObject + { + var keys = nsDict.Keys.Select(k => keyConverter(k)).ToArray(); + var values = nsDict.Values.Select(v => valueConverter(v)).ToArray(); + return keys.Zip(values, (k, v) => new { Key = k, Value = v }) + .ToDictionary(x => x.Key, x => x.Value); + } } } - diff --git a/src/iOS.Core/Utilities/WCSessionManager.cs b/src/iOS.Core/Utilities/WCSessionManager.cs index 34cb8b254..7802019f3 100644 --- a/src/iOS.Core/Utilities/WCSessionManager.cs +++ b/src/iOS.Core/Utilities/WCSessionManager.cs @@ -1,10 +1,14 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading.Tasks; +using Bit.Core.Models.Domain; +using Bit.Core.Services; using Bit.iOS.Core.Utilities; using Foundation; using Newtonsoft.Json; +using ObjCRuntime; namespace WatchConnectivity { @@ -15,19 +19,18 @@ namespace WatchConnectivity private static readonly WCSessionManager sharedManager = new WCSessionManager(); private static WCSession session = WCSession.IsSupported ? WCSession.DefaultSession : null; - public static string Device = "Phone"; - - public event WCSessionReceiveDataHandler ApplicationContextUpdated; - public event WCSessionReceiveDataHandler MessagedReceived; - public delegate void WCSessionReceiveDataHandler(WCSession session, Dictionary applicationContext); + public event WCSessionReceiveDataHandler OnApplicationContextUpdated; + public event WCSessionReceiveDataHandler OnMessagedReceived; + public delegate void WCSessionReceiveDataHandler(WCSession session, Dictionary data); + WCSessionUserInfoTransfer _transf; private WCSession validSession { get { - Console.WriteLine($"Paired status:{(session.Paired ? '✓' : '✗')}\n"); - Console.WriteLine($"Watch App Installed status:{(session.WatchAppInstalled ? '✓' : '✗')}\n"); + Debug.WriteLine($"Paired status:{(session.Paired ? '✓' : '✗')}\n"); + Debug.WriteLine($"Watch App Installed status:{(session.WatchAppInstalled ? '✓' : '✗')}\n"); return (session.Paired && session.WatchAppInstalled) ? session : null; } } @@ -62,26 +65,15 @@ namespace WatchConnectivity { session.Delegate = this; session.ActivateSession(); - Console.WriteLine($"Started Watch Connectivity Session on {Device}"); + Debug.WriteLine($"Started Watch Connectivity Session"); } } public override void SessionReachabilityDidChange(WCSession session) { - Console.WriteLine($"Watch connectivity Reachable:{(session.Reachable ? '✓' : '✗')} from {Device}"); - // handle session reachability change - if (session.Reachable) - { - // great! continue on with Interactive Messaging - } - else - { - // 😥 prompt the user to unlock their iOS device - } + Debug.WriteLine($"Watch connectivity Reachable:{(session.Reachable ? '✓' : '✗')}"); } - #region Application Context Methods - public void SendBackgroundHighPriorityMessage(Dictionary applicationContext) { // Application context doesnt need the watch to be reachable, it will be received when opened @@ -90,27 +82,24 @@ namespace WatchConnectivity return; } - Xamarin.Forms.Device.BeginInvokeOnMainThread(() => + try { - try + var sendSuccessfully = validSession.UpdateApplicationContext(applicationContext.ToNSDictionary(), out var error); + if (sendSuccessfully) { - var sendSuccessfully = validSession.UpdateApplicationContext(applicationContext.ToNSDictionary(), out var error); - if (sendSuccessfully) - { - Console.WriteLine($"Sent App Context from {Device} \nPayLoad: {applicationContext.ToNSDictionary().ToString()} \n"); - } - else - { - Console.WriteLine($"Error Updating Application Context: {error.LocalizedDescription}"); - } + Debug.WriteLine($"Sent App Context \nPayLoad: {applicationContext.ToNSDictionary().ToString()} \n"); } - catch (Exception ex) + else { - Console.WriteLine($"Exception Updating Application Context: {ex.Message}"); + Debug.WriteLine($"Error Updating Application Context: {error.LocalizedDescription}"); } - }); + } + catch (Exception ex) + { + LoggerHelper.LogEvenIfCantBeResolved(ex); + } } - WCSessionUserInfoTransfer _transf; + public void SendBackgroundFifoHighPriorityMessage(Dictionary message) { if(validSession is null || validSession.ActivationState != WCSessionActivationState.Activated) @@ -120,11 +109,10 @@ namespace WatchConnectivity _transf?.Cancel(); - Console.WriteLine("Started transferring user info"); + Debug.WriteLine("Started transferring user info"); _transf = session.TransferUserInfo(message.ToNSDictionary()); - Task.Run(async () => { try @@ -133,53 +121,41 @@ namespace WatchConnectivity { await Task.Delay(1000); } - Console.WriteLine("Finished transferring user info"); + Debug.WriteLine("Finished transferring user info"); } catch (Exception ex) { - Console.WriteLine("Error transferring user info " + ex); + LoggerHelper.LogEvenIfCantBeResolved(ex); } }); - - //session.SendMessage(dic, - // (dd) => - // { - // Console.WriteLine(dd?.ToString()); - // }, - // error => - // { - // Console.WriteLine(error?.ToString()); - // } - //); } public override void DidReceiveApplicationContext(WCSession session, NSDictionary applicationContext) { - Console.WriteLine($"Receiving Message on {Device}"); - if (ApplicationContextUpdated != null) + Debug.WriteLine($"Receiving Message"); + if (OnApplicationContextUpdated != null) { var keys = applicationContext.Keys.Select(k => k.ToString()).ToArray(); var values = applicationContext.Values.Select(v => JsonConvert.DeserializeObject(v.ToString())).ToArray(); var dictionary = keys.Zip(values, (k, v) => new { Key = k, Value = v }) .ToDictionary(x => x.Key, x => x.Value); - ApplicationContextUpdated(session, dictionary); + OnApplicationContextUpdated(session, dictionary); } } - public override void DidReceiveMessage(WCSession session, NSDictionary message) { - Console.WriteLine($"Receiving Message on {Device}"); + Debug.WriteLine($"Receiving Message"); - var keys = message.Keys.Select(k => k.ToString()).ToArray(); - var values = message.Values.Select(v => v?.ToString() as object).ToArray(); - var dictionary = keys.Zip(values, (k, v) => new { Key = k, Value = v }) - .ToDictionary(x => x.Key, x => x.Value); - - MessagedReceived?.Invoke(session, dictionary); + OnMessagedReceived?.Invoke(session, message.ToDictionary()); } - #endregion + public override void DidReceiveMessage(WCSession session, NSDictionary message, WCSessionReplyHandler replyHandler) + { + Debug.WriteLine($"Receiving Message"); + + OnMessagedReceived?.Invoke(session, message.ToDictionary()); + } } } diff --git a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Helpers/KeychainHelper.swift b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Helpers/KeychainHelper.swift index e23a787a9..9102cb045 100644 --- a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Helpers/KeychainHelper.swift +++ b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Helpers/KeychainHelper.swift @@ -7,6 +7,37 @@ final class KeychainHelper { private init() {} + func hasDeviceOwnerAuth() -> Bool { + let query: [String:Any] = [ + kSecClass as String : kSecClassGenericPassword, + kSecAttrAccount as String : UUID().uuidString, + kSecAttrAccessible as String: kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, + kSecValueData as String: "hasPass".data(using: String.Encoding.utf8)!, + kSecReturnAttributes as String : kCFBooleanTrue as Any + ] + + var dataTypeRef: AnyObject? + var status = withUnsafeMutablePointer(to: &dataTypeRef) { + SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) + } + + if status == errSecItemNotFound { + let createStatus = SecItemAdd(query as CFDictionary, nil) + guard createStatus == errSecSuccess else { + return false + } + status = withUnsafeMutablePointer(to: &dataTypeRef) { + SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) + } + } + + guard status == errSecSuccess else { + return false + } + + return true + } + func read(_ key: String, _ type: T.Type) -> T? where T : Codable { guard let data = read(key) else { return nil @@ -16,7 +47,7 @@ final class KeychainHelper { let item = try JSONDecoder().decode(type, from: data) return item } catch { - assertionFailure("Fail to decode item for keychain: \(error)") + Log.e("Fail to decode item for keychain: \(error)") return nil } } @@ -28,7 +59,7 @@ final class KeychainHelper { save(data, key) } catch { - assertionFailure("Fail to encode item for keychain: \(error)") + Log.e("Fail to encode item for keychain: \(error)") } } @@ -57,7 +88,7 @@ final class KeychainHelper { kSecClass: kSecClassGenericPassword, kSecAttrService: genericService, kSecAttrAccount: key, - kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlock + kSecAttrAccessible: kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly ] as CFDictionary let status = SecItemAdd(query, nil) diff --git a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Localization/en.lproj/Localizable.strings b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Localization/en.lproj/Localizable.strings index 8def799f6..948845d62 100644 --- a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Localization/en.lproj/Localizable.strings +++ b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Localization/en.lproj/Localizable.strings @@ -7,3 +7,4 @@ "SetUpBitwardenToViewItemsContainingVerificationCodes"="Set up Bitwarden to view items containing verification codes"; "Search"="Search"; "NoItemsFound"="No items found"; +"SetUpAppleWatchPasscodeInOrderToUseBitwarden"="Set up Apple Watch passcode in order to use Bitwarden"; diff --git a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Services/StateService.swift b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Services/StateService.swift index a670a9a6e..6d5f1c9e7 100644 --- a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Services/StateService.swift +++ b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Services/StateService.swift @@ -6,6 +6,7 @@ class StateService { let HAS_RUN_BEFORE_KEY = "has_run_before_key" let CURRENT_STATE_KEY = "current_state_key" let CURRENT_USER_KEY = "current_user_key" + let LACKED_DEVICE_OWNER_AUTH_LAST_TIME_KEY = "lacked_device_owner_auth_last_time_key" // let TIMEOUT_MINUTES_KEY = "timeout_minutes_key" // let TIMEOUT_ACTION_KEY = "timeout_action_key" @@ -66,6 +67,15 @@ class StateService { setUser(user: nil) } + var lackedDeviceOwnerAuthLastTime : Bool { + get { + return UserDefaults.standard.bool(forKey: LACKED_DEVICE_OWNER_AUTH_LAST_TIME_KEY) + } + set(newValue) { + UserDefaults.standard.set(newValue, forKey: LACKED_DEVICE_OWNER_AUTH_LAST_TIME_KEY) + } + } + // var vaultTimeoutInMinutes: Int? { // guard let timeoutData = KeychainHelper.standard.read(TIMEOUT_MINUTES_KEY), // let strData = String(data: timeoutData, encoding: .utf8), diff --git a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Utilities/BWState.swift b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Utilities/BWState.swift index bc4c22ed1..cfccda06a 100644 --- a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Utilities/BWState.swift +++ b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Utilities/BWState.swift @@ -8,6 +8,7 @@ enum BWState : Int, Codable { case need2FAItem = 4 case syncing = 5 // case needUnlock = 6 + case needDeviceOwnerAuth = 7 var isDestructive: Bool { return self == .needSetup || self == .needLogin || self == .needPremium || self == .need2FAItem diff --git a/src/watchOS/bitwarden/bitwarden WatchKit Extension/Utilities/Queue.swift b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Utilities/Queue.swift new file mode 100644 index 000000000..d628066b8 --- /dev/null +++ b/src/watchOS/bitwarden/bitwarden WatchKit Extension/Utilities/Queue.swift @@ -0,0 +1,35 @@ +import Foundation + +/// This is basic Queue implementation that uses an array internally. +/// For the current use is enough, If intended to use with many items please improve it +/// following https://nitinagam17.medium.com/data-structure-in-swift-queue-part-5-985601071606 Linked list or Two stacks approach +struct ArrayQueue : CustomStringConvertible { + private var elements: [T] = [] + public init() {} + + var isEmpty: Bool { + elements.isEmpty + } + + var peek: T? { + elements.first + } + + var description: String { + if isEmpty { + return "Queue empty" + } + + return "---- Queue -----\n" + + elements.map({"\($0)"}).joined(separator: " -> ") + + "---- Queue end -----" + } + + mutating func enqueue(_ value: T) { + elements.append(value) + } + + mutating func dequeue() -> T? { + isEmpty ? nil : elements.removeFirst() + } +} diff --git a/src/watchOS/bitwarden/bitwarden WatchKit Extension/ViewModels/BWStateViewModel.swift b/src/watchOS/bitwarden/bitwarden WatchKit Extension/ViewModels/BWStateViewModel.swift index ac841086e..c55991a1c 100644 --- a/src/watchOS/bitwarden/bitwarden WatchKit Extension/ViewModels/BWStateViewModel.swift +++ b/src/watchOS/bitwarden/bitwarden WatchKit Extension/ViewModels/BWStateViewModel.swift @@ -19,6 +19,8 @@ class BWStateViewModel : ObservableObject{ isLoading = true case .need2FAItem: text = "Add2FactorAutenticationToAnItemToViewVerificationCodes" + case .needDeviceOwnerAuth: + text = "SetUpAppleWatchPasscodeInOrderToUseBitwarden" default: text = "" } diff --git a/src/watchOS/bitwarden/bitwarden WatchKit Extension/ViewModels/CipherListViewModel.swift b/src/watchOS/bitwarden/bitwarden WatchKit Extension/ViewModels/CipherListViewModel.swift index 13402ecc4..ffebd82d0 100644 --- a/src/watchOS/bitwarden/bitwarden WatchKit Extension/ViewModels/CipherListViewModel.swift +++ b/src/watchOS/bitwarden/bitwarden WatchKit Extension/ViewModels/CipherListViewModel.swift @@ -47,6 +47,10 @@ class CipherListViewModel : ObservableObject { } func checkStateAndFetch(_ state: BWState? = nil) { + guard checkDeviceOwnerAuth() else { + return + } + StateService.shared.checkIntegrity() user = StateService.shared.getUser() @@ -88,4 +92,20 @@ class CipherListViewModel : ObservableObject { return false } + + func checkDeviceOwnerAuth() -> Bool { + guard KeychainHelper.standard.hasDeviceOwnerAuth() else { + currentState = .needDeviceOwnerAuth + showingSheet = true + StateService.shared.lackedDeviceOwnerAuthLastTime = true + return false + } + + if StateService.shared.lackedDeviceOwnerAuthLastTime { + watchConnectivityManager.triggerSync() + StateService.shared.lackedDeviceOwnerAuthLastTime = false + } + + return true + } } diff --git a/src/watchOS/bitwarden/bitwarden WatchKit Extension/WatchConnectivityManager.swift b/src/watchOS/bitwarden/bitwarden WatchKit Extension/WatchConnectivityManager.swift index 03908d36f..405062064 100644 --- a/src/watchOS/bitwarden/bitwarden WatchKit Extension/WatchConnectivityManager.swift +++ b/src/watchOS/bitwarden/bitwarden WatchKit Extension/WatchConnectivityManager.swift @@ -10,9 +10,12 @@ final class WatchConnectivityManager: NSObject, ObservableObject { static let shared = WatchConnectivityManager() let watchConnectivitySubject = CurrentValueSubject(WatchConnectivityMessage(state: nil)) - - private let kMessageKey = "message" - private let kCipherDataKey = "watchDto" + + private let WATCH_DTO_APP_CONTEXT_KEY = "watchDto" + private let TRIGGER_SYNC_ACTION_KEY = "triggerSync" + private let ACTION_MESSAGE_KEY = "actionMessage" + + var messageQueue = ArrayQueue<[String : Any]>() private override init() { super.init() @@ -26,17 +29,22 @@ final class WatchConnectivityManager: NSObject, ObservableObject { var isSessionActivated: Bool { return WCSession.default.isCompanionAppInstalled && WCSession.default.activationState == .activated } + + func triggerSync() { + send([ACTION_MESSAGE_KEY : TRIGGER_SYNC_ACTION_KEY]) + } - func send(_ message: String) { + func send(_ message: [String : Any]) { guard WCSession.default.activationState == .activated else { - return + messageQueue.enqueue(message) + return } guard WCSession.default.isCompanionAppInstalled else { return } - WCSession.default.sendMessage([kMessageKey : message], replyHandler: nil) { error in + WCSession.default.sendMessage(message) { error in Log.e("Cannot send message: \(String(describing: error))") } } @@ -52,19 +60,30 @@ extension WatchConnectivityManager: WCSessionDelegate { func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { + guard !messageQueue.isEmpty, activationState == .activated else { + return + } + + repeat { + send(messageQueue.dequeue()!) + } while !messageQueue.isEmpty } func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) { // in order for the delivery to be faster the time is added to the key to make each application context update have a different key // and update faster let watchDtoKey = applicationContext.keys.first { k in - k.starts(with: kCipherDataKey) + k.starts(with: WATCH_DTO_APP_CONTEXT_KEY) } guard let dtoKey = watchDtoKey, let serializedDto = applicationContext[dtoKey] as? String else { return } + guard KeychainHelper.standard.hasDeviceOwnerAuth() else { + return + } + do { guard let json = try! JSONSerialization.jsonObject(with: serializedDto.data(using: .utf8)!, options: [.fragmentsAllowed]) as? String else { return diff --git a/src/watchOS/bitwarden/bitwarden.xcodeproj/project.pbxproj b/src/watchOS/bitwarden/bitwarden.xcodeproj/project.pbxproj index 3a2395296..ca71d85dc 100644 --- a/src/watchOS/bitwarden/bitwarden.xcodeproj/project.pbxproj +++ b/src/watchOS/bitwarden/bitwarden.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 1B15615B28B7F3D900610B9B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1B15615A28B7F3D900610B9B /* Preview Assets.xcassets */; }; 1B15616C28B81A2200610B9B /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B15616B28B81A2200610B9B /* Cipher.swift */; }; 1B15616E28B81A4300610B9B /* WatchConnectivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B15616D28B81A4300610B9B /* WatchConnectivityManager.swift */; }; + 1B5849A7294D1C020055286B /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B5849A6294D1C020055286B /* Queue.swift */; }; 1B59EC5729007DEE00A8718D /* BitwardenDB.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 1B59EC5529007DEE00A8718D /* BitwardenDB.xcdatamodeld */; }; 1B59EC592900801500A8718D /* StringEncryptionTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B59EC582900801500A8718D /* StringEncryptionTransformer.swift */; }; 1B59EC5C2900BB3400A8718D /* CryptoService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B59EC5B2900BB3400A8718D /* CryptoService.swift */; }; @@ -134,6 +135,8 @@ 1B15615D28B7F3D900610B9B /* PushNotificationPayload.apns */ = {isa = PBXFileReference; lastKnownFileType = text; path = PushNotificationPayload.apns; sourceTree = ""; }; 1B15616B28B81A2200610B9B /* Cipher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cipher.swift; sourceTree = ""; }; 1B15616D28B81A4300610B9B /* WatchConnectivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchConnectivityManager.swift; sourceTree = ""; }; + 1B5849A6294D1C020055286B /* Queue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; + 1B5849A92950BC860055286B /* LocalAuthentication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LocalAuthentication.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS9.1.sdk/System/Library/Frameworks/LocalAuthentication.framework; sourceTree = DEVELOPER_DIR; }; 1B59EC5629007DEE00A8718D /* BitwardenDB.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = BitwardenDB.xcdatamodel; sourceTree = ""; }; 1B59EC582900801500A8718D /* StringEncryptionTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringEncryptionTransformer.swift; sourceTree = ""; }; 1B59EC5B2900BB3400A8718D /* CryptoService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoService.swift; sourceTree = ""; }; @@ -219,6 +222,7 @@ 1BD291BE292D0E6F0004F33F /* JsonDecoderExtensions.swift */, 1BD291C0292E7E690004F33F /* ErrorExtensions.swift */, 1B5F5E39293F9D6F009B5FCC /* ViewExtensions.swift */, + 1B5849A6294D1C020055286B /* Queue.swift */, ); path = Utilities; sourceTree = ""; @@ -230,6 +234,7 @@ 1B15614128B7F3D700610B9B /* bitwarden WatchKit App */, 1B15614C28B7F3D800610B9B /* bitwarden WatchKit Extension */, 1B15613028B7F3D400610B9B /* Products */, + 1B5849A82950BC860055286B /* Frameworks */, ); sourceTree = ""; }; @@ -305,6 +310,14 @@ path = "Preview Content"; sourceTree = ""; }; + 1B5849A82950BC860055286B /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1B5849A92950BC860055286B /* LocalAuthentication.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 1B59EC5429007D7300A8718D /* Entities */ = { isa = PBXGroup; children = ( @@ -583,6 +596,7 @@ 1BD291C1292E7E690004F33F /* ErrorExtensions.swift in Sources */, 1B14DF37291186D900EA43F1 /* EmptyStateViewModifier.swift in Sources */, 1BD291B52924047C0004F33F /* BWStateViewModel.swift in Sources */, + 1B5849A7294D1C020055286B /* Queue.swift in Sources */, 1B15616E28B81A4300610B9B /* WatchConnectivityManager.swift in Sources */, 1B5AFF0329196C81004478F9 /* ColorUtils.swift in Sources */, 1B59EC612900C48E00A8718D /* KeychainHelper.swift in Sources */, diff --git a/src/watchOS/bitwarden/bitwarden/WatchConnectivityManager.swift b/src/watchOS/bitwarden/bitwarden/WatchConnectivityManager.swift index 9858b5aa9..010d99b7f 100644 --- a/src/watchOS/bitwarden/bitwarden/WatchConnectivityManager.swift +++ b/src/watchOS/bitwarden/bitwarden/WatchConnectivityManager.swift @@ -10,8 +10,9 @@ final class WatchConnectivityManager: NSObject, ObservableObject { static let shared = WatchConnectivityManager() @Published var notificationMessage: NotificationMessage? = nil - private let kMessageKey = "message" - private let kCipherDataKey = "cipherData" + private let WATCH_DTO_APP_CONTEXT_KEY = "watchDto" + private let TRIGGER_SYNC_ACTION_KEY = "triggerSync" + private let ACTION_MESSAGE_KEY = "actionMessage" private override init() { super.init() @@ -40,7 +41,7 @@ final class WatchConnectivityManager: NSObject, ObservableObject { return } - WCSession.default.sendMessage([kMessageKey : message], replyHandler: nil) { error in + WCSession.default.sendMessage([ACTION_MESSAGE_KEY : TRIGGER_SYNC_ACTION_KEY], replyHandler: nil) { error in print("Cannot send message: \(String(describing: error))") } } @@ -52,7 +53,7 @@ extension WatchConnectivityManager: WCSessionDelegate { self?.notificationMessage = NotificationMessage(text: "testing this didReceiveMessage") } - if let notificationText = message[kMessageKey] as? String { + if let notificationText = message[ACTION_MESSAGE_KEY] as? String { DispatchQueue.main.async { [weak self] in self?.notificationMessage = NotificationMessage(text: notificationText) } @@ -96,7 +97,7 @@ extension WatchConnectivityManager: WCSessionDelegate { DispatchQueue.main.async { [weak self] in self?.notificationMessage = NotificationMessage(text: "testing this didReceiveApplicationContext") } - if let notificationText = applicationContext[kCipherDataKey] as? String { + if let notificationText = applicationContext[WATCH_DTO_APP_CONTEXT_KEY] as? String { // let decoder = JSONDecoder() // do {