Add watchOS support
This commit is contained in:
@@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
# meterios
|
# meterios
|
||||||
|
|
||||||
*Pick your poison*, now on iOS.
|
*Pick your poison*, now on iOS & watchOS.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// SalutManager.swift
|
||||||
|
// meterios
|
||||||
|
//
|
||||||
|
// (c) 2025 Martin "maride" Dessauer
|
||||||
|
//
|
||||||
|
|
||||||
|
class SalutManager {
|
||||||
|
static let salutes: Array<String> = [
|
||||||
|
"Prost!", "Guten!", "Kippis!", "Cheers!", "Chin-Chin!", "Zum Wohl!", "Salut!", "לחיים",
|
||||||
|
"Jamas!", // thanks cocorilla
|
||||||
|
"Budmo!", "за здоровье!", "Sláinte!", "乾杯", "Noroc!", // thanks ChaosAyumi
|
||||||
|
]
|
||||||
|
|
||||||
|
static func getSalute() -> String {
|
||||||
|
return salutes[Int.random(in: 0..<salutes.count)]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
//
|
||||||
|
// Drink.swift
|
||||||
|
// meterios
|
||||||
|
//
|
||||||
|
// (c) 2025 Martin "maride" Dessauer
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct Drink: Identifiable, Encodable, Decodable {
|
||||||
|
public var id: Int
|
||||||
|
public var name: String
|
||||||
|
public var logoURL: String
|
||||||
|
private var price: String
|
||||||
|
private var bottle_size: String
|
||||||
|
public var caffeine: Int
|
||||||
|
public var active: Bool
|
||||||
|
|
||||||
|
// KeyMap is an enum covering the field names as used by SpaceMarket API
|
||||||
|
private enum KeyMap : String, CodingKey { case id, name, bottle_size, caffeine, price, active, logo_url }
|
||||||
|
|
||||||
|
// Init function to be used with JSONDecoder using SpaceMarket API's strange types
|
||||||
|
init(from decoder : Decoder) throws {
|
||||||
|
let container = try decoder.container(keyedBy: KeyMap.self)
|
||||||
|
self.id = try container.decode(Int.self, forKey: .id)
|
||||||
|
self.name = try container.decode(String.self, forKey: .name)
|
||||||
|
self.bottle_size = (try? container.decode(String.self, forKey: .bottle_size)) ?? ""
|
||||||
|
self.caffeine = (try? container.decode(Int.self, forKey: .caffeine)) ?? 0
|
||||||
|
self.price = (try? container.decode(String.self, forKey: .price)) ?? ""
|
||||||
|
self.active = (try? container.decode(Bool.self, forKey: .active)) ?? false
|
||||||
|
self.logoURL = (try? container.decode(String.self, forKey: .logo_url)) ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init function, as straight-forward as it gets
|
||||||
|
init(id: Int, name: String, logoURL: String = "", price: String = "", bottle_size: String = "", caffeine: Int = 0, active: Bool = true) {
|
||||||
|
self.id = id
|
||||||
|
self.name = name
|
||||||
|
self.logoURL = logoURL
|
||||||
|
self.price = price
|
||||||
|
self.bottle_size = bottle_size
|
||||||
|
self.caffeine = caffeine
|
||||||
|
self.active = active
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPrice() -> Float {
|
||||||
|
return Float(self.price) ?? 9999.99
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBottleSize() -> Float {
|
||||||
|
return Float(self.bottle_size) ?? 9999.99
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "mete-new.jpg",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"platform" : "watchos",
|
||||||
|
"size" : "1024x1024"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// meteriosWatchApp.swift
|
||||||
|
// meterios-watch
|
||||||
|
//
|
||||||
|
// (c) 2025 Martin "maride" Dessauer
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct meteriosWatchApp: App {
|
||||||
|
private var wcMgr = WCManager()
|
||||||
|
|
||||||
|
var body: some Scene {
|
||||||
|
WindowGroup {
|
||||||
|
ContentView(wcMgr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
//
|
||||||
|
// ContentView.swift
|
||||||
|
// meterios-watch
|
||||||
|
//
|
||||||
|
// (c) 2025 Martin "maride" Dessauer
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ContentView: View {
|
||||||
|
private var wcMgr: WCManager
|
||||||
|
|
||||||
|
@State private var drinks: Array<Drink> = []
|
||||||
|
|
||||||
|
@State private var showUpdateError: Bool = false
|
||||||
|
@State private var updateError: String = ""
|
||||||
|
@State private var updateErrorCount: Int = 0
|
||||||
|
|
||||||
|
init(_ wcMgr: WCManager) {
|
||||||
|
self.wcMgr = wcMgr
|
||||||
|
}
|
||||||
|
|
||||||
|
// update performs a full update from the paired device, covering connection info (user ID and backend address) and list of drinks
|
||||||
|
func update() {
|
||||||
|
if updateErrorCount >= 3 {
|
||||||
|
showUpdateError = true
|
||||||
|
updateErrorCount = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update drinks list from paired device
|
||||||
|
wcMgr.requestDrinksList(
|
||||||
|
replyHandler: { drinks in
|
||||||
|
// Drinks received from backend
|
||||||
|
self.drinks = drinks
|
||||||
|
showUpdateError = false
|
||||||
|
}, errorHandler: { error in
|
||||||
|
// Error receiving drinks
|
||||||
|
print("Error receiving drinks: \(error)")
|
||||||
|
updateError = error?.localizedDescription ?? "Unknown error"
|
||||||
|
updateErrorCount += 1
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
if drinks.count > 0 {
|
||||||
|
NavigationView {
|
||||||
|
List() {
|
||||||
|
// Drinks
|
||||||
|
Section("Drinks") {
|
||||||
|
ForEach(drinks) { drink in
|
||||||
|
NavigationLink(drink.name) {
|
||||||
|
DrinkDetailView(wcMgr: wcMgr, drink: drink)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controls
|
||||||
|
Section {
|
||||||
|
Button("Refresh") {
|
||||||
|
drinks = []
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ProgressView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear(perform: update)
|
||||||
|
// Alert to communicate an error in communication with the paired device
|
||||||
|
.alert("Connection Error", isPresented: $showUpdateError) {
|
||||||
|
Text(updateError).tint(.red).font(.footnote)
|
||||||
|
Button("Retry", systemImage: "arrow.clockwise", action: update)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
//
|
||||||
|
// DrinkDetailView.swift
|
||||||
|
// meterios-watch
|
||||||
|
//
|
||||||
|
// (c) 2025 Martin "maride" Dessauer
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct DrinkDetailView: View {
|
||||||
|
private var wcMgr: WCManager
|
||||||
|
private var drink: Drink
|
||||||
|
|
||||||
|
@State var shouldShowTransactionSheet = false
|
||||||
|
|
||||||
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
|
||||||
|
init(wcMgr: WCManager, drink: Drink) {
|
||||||
|
self.wcMgr = wcMgr
|
||||||
|
self.drink = drink
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
// Title
|
||||||
|
Text(drink.name).font(.title)
|
||||||
|
|
||||||
|
// Basic info
|
||||||
|
HStack {
|
||||||
|
VStack {
|
||||||
|
Text(String(format: "%img", drink.caffeine)).fontWeight(.bold)
|
||||||
|
Text("Caffeine").fontWeight(Font.Weight.thin)
|
||||||
|
}
|
||||||
|
Divider()
|
||||||
|
VStack {
|
||||||
|
Text(String(format: "%.2f€", drink.GetPrice())).fontWeight(.bold)
|
||||||
|
Text("Price").fontWeight(Font.Weight.thin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick button
|
||||||
|
Button(action: {
|
||||||
|
// Open the transaction sheet
|
||||||
|
shouldShowTransactionSheet = true
|
||||||
|
}) {
|
||||||
|
Text("Poison picked!")
|
||||||
|
}.tint(.green)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transaction sheet
|
||||||
|
.sheet(isPresented: $shouldShowTransactionSheet) {
|
||||||
|
// Sheet dismissed - dismiss detail perspective as well.
|
||||||
|
dismiss()
|
||||||
|
} content: {
|
||||||
|
TransactionView(wcMgr: wcMgr, drinkID: drink.id).interactiveDismissDisabled()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
//
|
||||||
|
// TransactionView.swift
|
||||||
|
// meterios-watch
|
||||||
|
//
|
||||||
|
// (c) 2025 Martin "maride" Dessauer
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct TransactionView: View {
|
||||||
|
private var wcMgr: WCManager
|
||||||
|
private var drinkID: Int
|
||||||
|
|
||||||
|
@State var statusLine = "Booking..."
|
||||||
|
|
||||||
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
|
||||||
|
// If the backend reacts too fast (we hate that, huh?), the user is confused because of the short life of this view
|
||||||
|
// closeTimer takes care of that confusion and gives the user the good old 56k modem feeling.
|
||||||
|
@State var closeTimer: Timer?
|
||||||
|
@State var closeTimerReady: Bool = false
|
||||||
|
@State var backendBookTimer: Timer?
|
||||||
|
@State var backendTimerReady: Bool = false
|
||||||
|
|
||||||
|
init(wcMgr: WCManager, drinkID: Int) {
|
||||||
|
self.wcMgr = wcMgr
|
||||||
|
self.drinkID = drinkID
|
||||||
|
}
|
||||||
|
|
||||||
|
// maybeDismiss dismisses this view after the backend call(s) finished AND the timer(s) ran out
|
||||||
|
func maybeDismiss() {
|
||||||
|
if closeTimerReady && backendTimerReady {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Books the drinks handed over to this view
|
||||||
|
func book() {
|
||||||
|
// Try to book the drink(s)
|
||||||
|
wcMgr.bookDrink(drinkID: drinkID) { salut in
|
||||||
|
statusLine = salut
|
||||||
|
backendTimerReady = true
|
||||||
|
maybeDismiss()
|
||||||
|
} errorHandler: { error in
|
||||||
|
statusLine = error!.localizedDescription
|
||||||
|
print("Error booking drinks: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
// Green checkmark or progress indicator
|
||||||
|
VStack {
|
||||||
|
if backendTimerReady {
|
||||||
|
Image(systemName: "checkmark.circle.fill")
|
||||||
|
.foregroundStyle(.green)
|
||||||
|
} else {
|
||||||
|
ProgressView()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status line, on success with salute
|
||||||
|
Text(statusLine)
|
||||||
|
.font(.title)
|
||||||
|
|
||||||
|
// Let watch vibrate on success
|
||||||
|
if #available(watchOS 10.0, *) {
|
||||||
|
Text("")
|
||||||
|
.opacity(0)
|
||||||
|
.sensoryFeedback(.success, trigger: backendTimerReady)
|
||||||
|
}
|
||||||
|
}.onAppear(perform: {
|
||||||
|
closeTimer = Timer.scheduledTimer(withTimeInterval: 1.5, repeats: false) { timer in
|
||||||
|
closeTimerReady = true
|
||||||
|
maybeDismiss()
|
||||||
|
}
|
||||||
|
backendBookTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { timer in
|
||||||
|
book()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,16 +6,81 @@
|
|||||||
objectVersion = 77;
|
objectVersion = 77;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
A7FA4CB72D9E8FF5005ACDBB /* meterios-watch.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = A7FA4CAA2D9E8FF1005ACDBB /* meterios-watch.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
A7FA4CB52D9E8FF5005ACDBB /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = A7F97A862D8DD542004EF4ED /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = A7FA4CA92D9E8FF1005ACDBB;
|
||||||
|
remoteInfo = "meterios-watch Watch App";
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
A7C3C98C2D9D67EB00731E27 /* Embed Watch Content */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "$(CONTENTS_FOLDER_PATH)/Watch";
|
||||||
|
dstSubfolderSpec = 16;
|
||||||
|
files = (
|
||||||
|
A7FA4CB72D9E8FF5005ACDBB /* meterios-watch.app in Embed Watch Content */,
|
||||||
|
);
|
||||||
|
name = "Embed Watch Content";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
A7F97A8E2D8DD542004EF4ED /* meterios.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = meterios.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
A7F97A8E2D8DD542004EF4ED /* meterios.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = meterios.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
A7FA4CAA2D9E8FF1005ACDBB /* meterios-watch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "meterios-watch.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||||
|
A7FA4CBE2D9E90B0005ACDBB /* Exceptions for "meterios" folder in "meterios-watch" target */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||||
|
membershipExceptions = (
|
||||||
|
BackendConnector.swift,
|
||||||
|
);
|
||||||
|
target = A7FA4CA92D9E8FF1005ACDBB /* meterios-watch */;
|
||||||
|
};
|
||||||
|
A7FA4CCB2D9E9564005ACDBB /* Exceptions for "common" folder in "meterios-watch" target */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||||
|
membershipExceptions = (
|
||||||
|
SalutManager.swift,
|
||||||
|
types/Drink.swift,
|
||||||
|
types/User.swift,
|
||||||
|
WCManager.swift,
|
||||||
|
);
|
||||||
|
target = A7FA4CA92D9E8FF1005ACDBB /* meterios-watch */;
|
||||||
|
};
|
||||||
|
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||||
|
|
||||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||||
A7F97A902D8DD542004EF4ED /* meterios */ = {
|
A7F97A902D8DD542004EF4ED /* meterios */ = {
|
||||||
isa = PBXFileSystemSynchronizedRootGroup;
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
exceptions = (
|
||||||
|
A7FA4CBE2D9E90B0005ACDBB /* Exceptions for "meterios" folder in "meterios-watch" target */,
|
||||||
|
);
|
||||||
path = meterios;
|
path = meterios;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
A7FA4CAB2D9E8FF1005ACDBB /* meterios-watch */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
path = "meterios-watch";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
A7FA4CC62D9E94BD005ACDBB /* common */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
exceptions = (
|
||||||
|
A7FA4CCB2D9E9564005ACDBB /* Exceptions for "common" folder in "meterios-watch" target */,
|
||||||
|
);
|
||||||
|
path = common;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -26,13 +91,22 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
A7FA4CA72D9E8FF1005ACDBB /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
A7F97A852D8DD542004EF4ED = {
|
A7F97A852D8DD542004EF4ED = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
A7FA4CC62D9E94BD005ACDBB /* common */,
|
||||||
A7F97A902D8DD542004EF4ED /* meterios */,
|
A7F97A902D8DD542004EF4ED /* meterios */,
|
||||||
|
A7FA4CAB2D9E8FF1005ACDBB /* meterios-watch */,
|
||||||
A7F97A8F2D8DD542004EF4ED /* Products */,
|
A7F97A8F2D8DD542004EF4ED /* Products */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -41,6 +115,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
A7F97A8E2D8DD542004EF4ED /* meterios.app */,
|
A7F97A8E2D8DD542004EF4ED /* meterios.app */,
|
||||||
|
A7FA4CAA2D9E8FF1005ACDBB /* meterios-watch.app */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -55,13 +130,16 @@
|
|||||||
A7F97A8A2D8DD542004EF4ED /* Sources */,
|
A7F97A8A2D8DD542004EF4ED /* Sources */,
|
||||||
A7F97A8B2D8DD542004EF4ED /* Frameworks */,
|
A7F97A8B2D8DD542004EF4ED /* Frameworks */,
|
||||||
A7F97A8C2D8DD542004EF4ED /* Resources */,
|
A7F97A8C2D8DD542004EF4ED /* Resources */,
|
||||||
|
A7C3C98C2D9D67EB00731E27 /* Embed Watch Content */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
|
A7FA4CB62D9E8FF5005ACDBB /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
fileSystemSynchronizedGroups = (
|
fileSystemSynchronizedGroups = (
|
||||||
A7F97A902D8DD542004EF4ED /* meterios */,
|
A7F97A902D8DD542004EF4ED /* meterios */,
|
||||||
|
A7FA4CC62D9E94BD005ACDBB /* common */,
|
||||||
);
|
);
|
||||||
name = meterios;
|
name = meterios;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
@@ -70,6 +148,28 @@
|
|||||||
productReference = A7F97A8E2D8DD542004EF4ED /* meterios.app */;
|
productReference = A7F97A8E2D8DD542004EF4ED /* meterios.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
|
A7FA4CA92D9E8FF1005ACDBB /* meterios-watch */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = A7FA4CB82D9E8FF5005ACDBB /* Build configuration list for PBXNativeTarget "meterios-watch" */;
|
||||||
|
buildPhases = (
|
||||||
|
A7FA4CA62D9E8FF1005ACDBB /* Sources */,
|
||||||
|
A7FA4CA72D9E8FF1005ACDBB /* Frameworks */,
|
||||||
|
A7FA4CA82D9E8FF1005ACDBB /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
fileSystemSynchronizedGroups = (
|
||||||
|
A7FA4CAB2D9E8FF1005ACDBB /* meterios-watch */,
|
||||||
|
);
|
||||||
|
name = "meterios-watch";
|
||||||
|
packageProductDependencies = (
|
||||||
|
);
|
||||||
|
productName = "meterios-watch Watch App";
|
||||||
|
productReference = A7FA4CAA2D9E8FF1005ACDBB /* meterios-watch.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
@@ -83,6 +183,9 @@
|
|||||||
A7F97A8D2D8DD542004EF4ED = {
|
A7F97A8D2D8DD542004EF4ED = {
|
||||||
CreatedOnToolsVersion = 16.2;
|
CreatedOnToolsVersion = 16.2;
|
||||||
};
|
};
|
||||||
|
A7FA4CA92D9E8FF1005ACDBB = {
|
||||||
|
CreatedOnToolsVersion = 16.2;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = A7F97A892D8DD542004EF4ED /* Build configuration list for PBXProject "meterios" */;
|
buildConfigurationList = A7F97A892D8DD542004EF4ED /* Build configuration list for PBXProject "meterios" */;
|
||||||
@@ -100,6 +203,7 @@
|
|||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
A7F97A8D2D8DD542004EF4ED /* meterios */,
|
A7F97A8D2D8DD542004EF4ED /* meterios */,
|
||||||
|
A7FA4CA92D9E8FF1005ACDBB /* meterios-watch */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
@@ -112,6 +216,13 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
A7FA4CA82D9E8FF1005ACDBB /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
@@ -122,8 +233,23 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
A7FA4CA62D9E8FF1005ACDBB /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
A7FA4CB62D9E8FF5005ACDBB /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = A7FA4CA92D9E8FF1005ACDBB /* meterios-watch */;
|
||||||
|
targetProxy = A7FA4CB52D9E8FF5005ACDBB /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
A7F97A9A2D8DD54B004EF4ED /* Debug */ = {
|
A7F97A9A2D8DD54B004EF4ED /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
@@ -272,7 +398,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = space.chaosdorf.meterios;
|
PRODUCT_BUNDLE_IDENTIFIER = space.chaosdorf.meterios;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||||
@@ -309,7 +435,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = space.chaosdorf.meterios;
|
PRODUCT_BUNDLE_IDENTIFIER = space.chaosdorf.meterios;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||||
@@ -322,6 +448,64 @@
|
|||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
A7FA4CB92D9E8FF5005ACDBB /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = 3RNVF52786;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = Meterios;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
|
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = space.chaosdorf.meterios;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.1;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = space.chaosdorf.meterios.watchkitapp;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = watchos;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = 4;
|
||||||
|
WATCHOS_DEPLOYMENT_TARGET = 8.7;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
A7FA4CBA2D9E8FF5005ACDBB /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = 3RNVF52786;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = Meterios;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
|
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = space.chaosdorf.meterios;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.1;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = space.chaosdorf.meterios.watchkitapp;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = watchos;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = 4;
|
||||||
|
WATCHOS_DEPLOYMENT_TARGET = 8.7;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
@@ -343,6 +527,15 @@
|
|||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
A7FA4CB82D9E8FF5005ACDBB /* Build configuration list for PBXNativeTarget "meterios-watch" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
A7FA4CB92D9E8FF5005ACDBB /* Debug */,
|
||||||
|
A7FA4CBA2D9E8FF5005ACDBB /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
rootObject = A7F97A862D8DD542004EF4ED /* Project object */;
|
rootObject = A7F97A862D8DD542004EF4ED /* Project object */;
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1620"
|
||||||
|
version = "1.7">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES"
|
||||||
|
buildArchitectures = "Automatic">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "A7FA4CA92D9E8FF1005ACDBB"
|
||||||
|
BuildableName = "meterios-watch.app"
|
||||||
|
BlueprintName = "meterios-watch"
|
||||||
|
ReferencedContainer = "container:meterios.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "A7F97A8D2D8DD542004EF4ED"
|
||||||
|
BuildableName = "meterios.app"
|
||||||
|
BlueprintName = "meterios"
|
||||||
|
ReferencedContainer = "container:meterios.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
shouldAutocreateTestPlan = "YES">
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "A7FA4CA92D9E8FF1005ACDBB"
|
||||||
|
BuildableName = "meterios-watch.app"
|
||||||
|
BlueprintName = "meterios-watch"
|
||||||
|
ReferencedContainer = "container:meterios.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "A7FA4CA92D9E8FF1005ACDBB"
|
||||||
|
BuildableName = "meterios-watch.app"
|
||||||
|
BlueprintName = "meterios-watch"
|
||||||
|
ReferencedContainer = "container:meterios.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1620"
|
||||||
|
version = "1.7">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES"
|
||||||
|
buildArchitectures = "Automatic">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "A7F97A8D2D8DD542004EF4ED"
|
||||||
|
BuildableName = "meterios.app"
|
||||||
|
BlueprintName = "meterios"
|
||||||
|
ReferencedContainer = "container:meterios.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
shouldAutocreateTestPlan = "YES">
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "A7F97A8D2D8DD542004EF4ED"
|
||||||
|
BuildableName = "meterios.app"
|
||||||
|
BlueprintName = "meterios"
|
||||||
|
ReferencedContainer = "container:meterios.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "A7F97A8D2D8DD542004EF4ED"
|
||||||
|
BuildableName = "meterios.app"
|
||||||
|
BlueprintName = "meterios"
|
||||||
|
ReferencedContainer = "container:meterios.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
||||||
@@ -12,6 +12,8 @@ struct meteriosApp: App {
|
|||||||
@AppStorage("meteHostAddr") private var meteHostAddr = ""
|
@AppStorage("meteHostAddr") private var meteHostAddr = ""
|
||||||
@AppStorage("meteUserID") private var meteUserID = -1
|
@AppStorage("meteUserID") private var meteUserID = -1
|
||||||
|
|
||||||
|
private var wcMgr = WCManager()
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
if meteHostAddr == "" || meteUserID == -1 {
|
if meteHostAddr == "" || meteUserID == -1 {
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
//
|
|
||||||
// Drink.swift
|
|
||||||
// meterios
|
|
||||||
//
|
|
||||||
// (c) 2025 Martin "maride" Dessauer
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct Drink: Identifiable, Decodable {
|
|
||||||
var id: Int
|
|
||||||
var name: String
|
|
||||||
var logoURL: String = ""
|
|
||||||
var price: Float = 0.00
|
|
||||||
var bottle_size: Float = 0.00
|
|
||||||
var caffeine: Int = 0
|
|
||||||
var active: Bool = true
|
|
||||||
|
|
||||||
// KeyMap is an enum covering the field names as used by SpaceMarket API
|
|
||||||
private enum KeyMap : String, CodingKey { case id, name, bottle_size, caffeine, price, active, logo_url }
|
|
||||||
|
|
||||||
// Init function to be used with JSONDecoder, converting from SpaceMarket API's strange types to proper types
|
|
||||||
init(from decoder : Decoder) throws {
|
|
||||||
let container = try decoder.container(keyedBy: KeyMap.self)
|
|
||||||
self.id = try container.decode(Int.self, forKey: .id)
|
|
||||||
self.name = try container.decode(String.self, forKey: .name)
|
|
||||||
self.bottle_size = Float(try container.decode(String.self, forKey: .bottle_size)) ?? 0.00
|
|
||||||
self.caffeine = (try? container.decode(Int.self, forKey: .caffeine)) ?? 0
|
|
||||||
self.price = Float(try container.decode(String.self, forKey: .price)) ?? 9999.99
|
|
||||||
self.active = (try? container.decode(Bool.self, forKey: .active)) ?? false
|
|
||||||
self.logoURL = (try? container.decode(String.self, forKey: .logo_url)) ?? ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -65,7 +65,7 @@ struct DrinkDetail: View {
|
|||||||
}.fixedSize()
|
}.fixedSize()
|
||||||
Divider()
|
Divider()
|
||||||
VStack {
|
VStack {
|
||||||
Text(String(format: "%.2f€", drink.price)).fontWeight(.bold)
|
Text(String(format: "%.2f€", drink.GetPrice())).fontWeight(.bold)
|
||||||
Text("Price").fontWeight(Font.Weight.thin)
|
Text("Price").fontWeight(Font.Weight.thin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ struct DrinkDetail: View {
|
|||||||
HStack {
|
HStack {
|
||||||
Text("Total sum:")
|
Text("Total sum:")
|
||||||
Spacer()
|
Spacer()
|
||||||
Text(String(format: "%.2f€", self.drink.price * Float(self.drinkAmount))).fontWeight(.bold)
|
Text(String(format: "%.2f€", self.drink.GetPrice() * Float(self.drinkAmount))).fontWeight(.bold)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,15 +25,6 @@ struct TransactionView: View {
|
|||||||
@State var backendBookTimer: Timer?
|
@State var backendBookTimer: Timer?
|
||||||
@State var backendTimerReady: Bool = false
|
@State var backendTimerReady: Bool = false
|
||||||
|
|
||||||
let salutes: Array<String> = [
|
|
||||||
"Prost!", "Guten!", "Kippis!", "Cheers!", "Chin-Chin!", "Zum Wohl!", "Salut!", "לחיים",
|
|
||||||
"Jamas!", // thanks cocorilla
|
|
||||||
"Budmo!", "за здоровье!", "Sláinte!", "乾杯", "Noroc!", // thanks ChaosAyumi
|
|
||||||
]
|
|
||||||
func getSalute() -> String {
|
|
||||||
return salutes[Int.random(in: 0..<salutes.count)]
|
|
||||||
}
|
|
||||||
|
|
||||||
init(drinkID: Int, drinkAmount: Int) {
|
init(drinkID: Int, drinkAmount: Int) {
|
||||||
self.drinkID = drinkID
|
self.drinkID = drinkID
|
||||||
self.drinkAmount = drinkAmount
|
self.drinkAmount = drinkAmount
|
||||||
@@ -54,7 +45,7 @@ struct TransactionView: View {
|
|||||||
for _ in 1...drinkAmount {
|
for _ in 1...drinkAmount {
|
||||||
try await BackendConnector().BookDrink(baseAddr: meteHostAddr, userID: meteUserID, drinkID: drinkID)
|
try await BackendConnector().BookDrink(baseAddr: meteHostAddr, userID: meteUserID, drinkID: drinkID)
|
||||||
}
|
}
|
||||||
statusLine = getSalute()
|
statusLine = SalutManager.getSalute()
|
||||||
backendTimerReady = true
|
backendTimerReady = true
|
||||||
maybeDismiss()
|
maybeDismiss()
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
Reference in New Issue
Block a user