Files
WerkoutIOS/iphone/Werkout_watch Watch App/WatchMainViewModel+WCSessionDelegate.swift

120 lines
3.7 KiB
Swift

//
// WatchMainViewModel+WCSessionDelegate.swift
// Werkout_watch Watch App
//
// Created by Trey Tartt on 6/19/24.
//
import Foundation
import WatchConnectivity
import SwiftUI
import HealthKit
import os
import SharedCore
extension WatchMainViewModel: WCSessionDelegate {
func session(_ session: WCSession, didReceiveMessageData messageData: Data) {
dataToAction(messageData: messageData)
}
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
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) {
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 session.isWatchAppInstalled else {
return
}
#else
guard session.isCompanionAppInstalled else {
return
}
#endif
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")
}
}
}
extension WatchMainViewModel {
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String: Any]) {
if let messageData = applicationContext["package"] as? Data {
dataToAction(messageData: messageData)
}
}
public func session(_ session: WCSession, didReceiveUserInfo userInfo: [String: Any] = [:]) {
if let messageData = userInfo["package"] as? Data {
dataToAction(messageData: messageData)
}
}
}