Add watchOS support

This commit is contained in:
2025-04-04 18:23:12 +02:00
parent 96b0466deb
commit 384ee97cb7
20 changed files with 857 additions and 48 deletions
+147
View File
@@ -0,0 +1,147 @@
//
// WCManager.swift
// meterios
//
// (c) 2025 Martin "maride" Dessauer
//
import SwiftUI
import WatchConnectivity
enum WCMeteriosMessageTypes: String {
case ConnectionInfo
case DrinkList
case BookDrink
}
class WCManager: NSObject, WCSessionDelegate {
@AppStorage("meteHostAddr") private var meteHostAddr = ""
@AppStorage("meteUserID") private var meteUserID = -1
private let session: WCSession = WCSession.default
override init() {
super.init()
if (WCSession.isSupported()) {
session.delegate = self
session.activate()
log("Activated session")
}
}
// Functions required by WCSessionDelegate protocol, but not of interest for Meterios
#if os(iOS)
func sessionDidBecomeInactive(_ session: WCSession) {}
func sessionDidDeactivate(_ session: WCSession) {}
#endif // os(iOS)
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: (any Error)?) {}
// session(...) gets called for every incoming message through the WatchConnectivity channel
// As Meterios currently only needs messages from watch to device and responses back to the watch, this function currently only implements device-related responses.
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
// Check for the different possible message types
if message.contains(where: { $0.key == "request" && $0.value as! String == WCMeteriosMessageTypes.ConnectionInfo.rawValue }) {
// Request for ConnectionInfo
replyHandler([
"userID": meteUserID,
"hostAddr": meteHostAddr
])
} else if message.contains(where: { $0.key == "request" && $0.value as! String == WCMeteriosMessageTypes.DrinkList.rawValue }) {
// Request for a list of available drinks
Task {
let drinks = try await BackendConnector().GetDrinks(baseAddr: meteHostAddr)
let availableDrinks = drinks.filter({ $0.active })
// Create a plist-compatible variant of the Drink object containing the required information
var drinkArray: Array<String> = []
for d in availableDrinks {
drinkArray.append(
try! JSONEncoder().encode(d).base64EncodedString()
)
}
replyHandler([
"drinks": drinkArray
])
}
} else if message.contains(where: { $0.key == "request" && $0.value as! String == WCMeteriosMessageTypes.BookDrink.rawValue }) {
// Book the given drink at the backend
Task {
do {
// Book
let drinkID = message["drinkID"] as! Int
try await BackendConnector().BookDrink(baseAddr: meteHostAddr, userID: meteUserID, drinkID: drinkID)
replyHandler([
"salut": SalutManager.getSalute()
])
} catch {
// Error booking against the backend
replyHandler([
"error": error,
"salut": ""
])
}
}
} else {
log("Error: received unknown request. \(message)")
}
}
// requestConnectionInfo requests User ID and Host address from the paired device
func requestConnectionInfo(replyHandler: @escaping (Int, String) -> Void, errorHandler: @escaping ((any Error)?) -> Void) {
session.sendMessage([
"request": WCMeteriosMessageTypes.ConnectionInfo.rawValue
], replyHandler: { reply in
replyHandler(reply["userID"] as! Int, reply["hostAddr"] as! String)
}, errorHandler: { error in
errorHandler(error)
})
}
// requestDrinksList requests the list of drinks available at the backend from the paired device
func requestDrinksList(replyHandler: @escaping (Array<Drink>) -> Void, errorHandler: @escaping ((any Error)?) -> Void) {
session.sendMessage([
"request": WCMeteriosMessageTypes.DrinkList.rawValue
], replyHandler: { reply in
let drinks = reply["drinks"] as! Array<String>
// Create Drink objects from response
var drinkArray: Array<Drink> = []
for d in drinks {
let jsonDecoded = Data(base64Encoded: d)
let drink = try! JSONDecoder().decode(Drink.self, from: jsonDecoded!)
drinkArray.append(drink)
}
// enjoy your drinks, replyHandler!
replyHandler(drinkArray)
}, errorHandler: { error in
errorHandler(error)
})
}
// bookDrink books the drink given by drinkID
func bookDrink(drinkID: Int, replyHandler: @escaping (String) -> Void, errorHandler: @escaping ((any Error)?) -> Void) {
session.sendMessage([
"request": WCMeteriosMessageTypes.BookDrink.rawValue,
"drinkID": drinkID,
], replyHandler: { reply in
let salut = reply["salut"] as! String
replyHandler(salut)
}, errorHandler: { error in
errorHandler(error)
})
}
private func log(_ line: String) {
#if os(iOS)
let prefix = "[WCM@iOS]"
#elseif os(watchOS)
let prefix = "[WCM@wOS]"
#else
let prefix = "[WCM@?OS]"
#endif // os(...)
print("\(prefix) \(line)")
}
}