mirror of
https://github.com/bitwarden/mobile.git
synced 2024-06-27 10:44:49 +02:00
e72932cbaa
* EC-835 Added in the Watch app keychain accessible when passcode set this device only and when the passcode is set to signal the iPhone to trigger a sync on opening the watch app * EC-835 Embed LocalAuthentication framework into the watch app to fix no such module when importing it * EC-835 Changed approach to check if Watch has passcode enabled by using Keychain accessible kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly instead of LAContext * EC-835 Fix weird error saying unassigned local variable on the CI compiler. It seems it doesn't realize of the full condition
136 lines
5.1 KiB
Swift
136 lines
5.1 KiB
Swift
import Combine
|
|
import Foundation
|
|
import WatchConnectivity
|
|
|
|
struct WatchConnectivityMessage {
|
|
var state: BWState?
|
|
}
|
|
|
|
final class WatchConnectivityManager: NSObject, ObservableObject {
|
|
static let shared = WatchConnectivityManager()
|
|
|
|
let watchConnectivitySubject = CurrentValueSubject<WatchConnectivityMessage, Error>(WatchConnectivityMessage(state: nil))
|
|
|
|
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()
|
|
|
|
if WCSession.isSupported() {
|
|
WCSession.default.delegate = self
|
|
WCSession.default.activate()
|
|
}
|
|
}
|
|
|
|
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 : Any]) {
|
|
guard WCSession.default.activationState == .activated else {
|
|
messageQueue.enqueue(message)
|
|
return
|
|
}
|
|
|
|
guard WCSession.default.isCompanionAppInstalled else {
|
|
return
|
|
}
|
|
|
|
WCSession.default.sendMessage(message) { error in
|
|
Log.e("Cannot send message: \(String(describing: error))")
|
|
}
|
|
}
|
|
}
|
|
|
|
extension WatchConnectivityManager: WCSessionDelegate {
|
|
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
|
|
}
|
|
|
|
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
|
|
}
|
|
|
|
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: 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
|
|
}
|
|
|
|
let decoder = JSONDecoder()
|
|
decoder.keyDecodingStrategy = .upperToLowerCamelCase
|
|
let watchDTO = try decoder.decode(WatchDTO.self, from: json.data(using: .utf8)!)
|
|
|
|
let previousUserId = StateService.shared.getUser()?.id
|
|
|
|
if previousUserId != watchDTO.userData?.id {
|
|
self.watchConnectivitySubject.send(WatchConnectivityMessage(state: .syncing))
|
|
}
|
|
|
|
StateService.shared.currentState = watchDTO.state
|
|
StateService.shared.setUser(user: watchDTO.userData)
|
|
// StateService.shared.setVaultTimeout(watchDTO.settingsData?.vaultTimeoutInMinutes, watchDTO.settingsData?.vaultTimeoutAction ?? .lock)
|
|
EnvironmentService.shared.baseUrl = watchDTO.environmentData?.base
|
|
EnvironmentService.shared.setIconsUrl(url: watchDTO.environmentData?.icons)
|
|
|
|
if watchDTO.state.isDestructive {
|
|
CipherService.shared.deleteAll(nil) {
|
|
self.watchConnectivitySubject.send(WatchConnectivityMessage(state: nil))
|
|
}
|
|
}
|
|
|
|
if watchDTO.state == .valid, var ciphers = watchDTO.ciphers {
|
|
// we need to track the to which user the ciphers belong to, so we add the user here to all ciphers
|
|
// note: it's not being sent directly from the phone to increase performance on the communication
|
|
ciphers.indices.forEach { i in
|
|
ciphers[i].userId = watchDTO.userData!.id
|
|
}
|
|
|
|
CipherService.shared.saveCiphers(ciphers) {
|
|
if let previousUserId = previousUserId,
|
|
let currentUserid = watchDTO.userData?.id,
|
|
previousUserId != currentUserid {
|
|
CipherService.shared.deleteAll(previousUserId) {}
|
|
}
|
|
self.watchConnectivitySubject.send(WatchConnectivityMessage(state: nil))
|
|
}
|
|
}
|
|
}
|
|
catch {
|
|
Log.e(error)
|
|
}
|
|
}
|
|
}
|