Stabilize iOS/watchOS/tvOS apps and add cross-platform audit remediation
This commit is contained in:
@@ -9,6 +9,8 @@ import Foundation
|
||||
import WatchConnectivity
|
||||
import SwiftUI
|
||||
import HealthKit
|
||||
import os
|
||||
import SharedCore
|
||||
|
||||
extension WatchMainViewModel: WCSessionDelegate {
|
||||
func session(_ session: WCSession, didReceiveMessageData messageData: Data) {
|
||||
@@ -16,27 +18,87 @@ extension WatchMainViewModel: WCSessionDelegate {
|
||||
}
|
||||
|
||||
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
|
||||
print("activation did complete")
|
||||
if let error {
|
||||
logger.error("Watch session activation failed: \(error.localizedDescription, privacy: .public)")
|
||||
} else {
|
||||
logger.info("Watch session activation state: \(activationState.rawValue, privacy: .public)")
|
||||
if activationState == .activated {
|
||||
DataSender.flushQueuedPayloadsIfPossible(session: session)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DataSender {
|
||||
private static let logger = Logger(subsystem: "com.werkout.watch", category: "session")
|
||||
private static let queue = DispatchQueue(label: "com.werkout.watch.sender")
|
||||
private static var queuedPayloads = BoundedFIFOQueue<Data>(maxCount: 100)
|
||||
|
||||
static func send(_ data: Data) {
|
||||
guard WCSession.default.activationState == .activated else {
|
||||
if let validationError = WatchPayloadValidation.validate(data) {
|
||||
logger.error("Dropped invalid watch payload: \(String(describing: validationError), privacy: .public)")
|
||||
return
|
||||
}
|
||||
|
||||
queue.async {
|
||||
let session = WCSession.default
|
||||
guard session.activationState == .activated else {
|
||||
enqueue(data)
|
||||
session.activate()
|
||||
return
|
||||
}
|
||||
|
||||
deliver(data, using: session)
|
||||
flushQueuedPayloads(using: session)
|
||||
}
|
||||
}
|
||||
|
||||
static func flushQueuedPayloadsIfPossible(session: WCSession = .default) {
|
||||
queue.async {
|
||||
flushQueuedPayloads(using: session)
|
||||
}
|
||||
}
|
||||
|
||||
private static func flushQueuedPayloads(using session: WCSession) {
|
||||
guard session.activationState == .activated else {
|
||||
return
|
||||
}
|
||||
|
||||
guard queuedPayloads.isEmpty == false else {
|
||||
return
|
||||
}
|
||||
|
||||
let payloads = queuedPayloads.dequeueAll()
|
||||
payloads.forEach { deliver($0, using: session) }
|
||||
}
|
||||
|
||||
private static func deliver(_ data: Data, using session: WCSession) {
|
||||
#if os(iOS)
|
||||
guard WCSession.default.isWatchAppInstalled else {
|
||||
guard session.isWatchAppInstalled else {
|
||||
return
|
||||
}
|
||||
#else
|
||||
guard WCSession.default.isCompanionAppInstalled else {
|
||||
guard session.isCompanionAppInstalled else {
|
||||
return
|
||||
}
|
||||
#endif
|
||||
WCSession.default.sendMessageData(data, replyHandler: nil)
|
||||
{ error in
|
||||
print("Cannot send message: \(String(describing: error))")
|
||||
|
||||
if session.isReachable {
|
||||
session.sendMessageData(data, replyHandler: nil) { error in
|
||||
logger.error("Cannot send watch message: \(error.localizedDescription, privacy: .public)")
|
||||
queue.async {
|
||||
enqueue(data)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
session.transferUserInfo(["package": data])
|
||||
}
|
||||
}
|
||||
|
||||
private static func enqueue(_ data: Data) {
|
||||
let droppedCount = queuedPayloads.enqueue(data)
|
||||
if droppedCount > 0 {
|
||||
logger.warning("Dropping oldest queued watch payload to enforce queue cap")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user