From aa55b64cf0deedbfc9d1bba572887d409138017b Mon Sep 17 00:00:00 2001 From: Krystof Date: Mon, 28 Oct 2024 03:19:26 +0100 Subject: [PATCH] feat(app): fresh app project --- .github/workflows/app-ci.yaml | 2 +- .../metro-now/common/const/api-const.swift | 4 + .../common/managers/location-manager.swift | 29 + .../common/managers/network-manager.swift | 121 ++++ .../metro-now/common/types/api-types.swift | 37 ++ .../metro-now/common/types/metro-line.swift | 8 + .../common/utils/metro-line.utils.swift | 13 + .../common/utils/station.utils.swift | 86 +++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 13 + .../Assets.xcassets/Contents.json | 6 + .../metro-now Watch App/ContentView.swift | 99 ++++ .../Preview Assets.xcassets/Contents.json | 6 + .../metro-now Watch App/metro_nowApp.swift | 13 + .../pages/departure-list-item.swift | 85 +++ .../pages/departure-placeholder.swift | 33 ++ .../metro-now Watch App/pages/main-page.swift | 78 +++ .../metro-now.xcodeproj/project.pbxproj | 538 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 35 ++ .../metro-now/Assets.xcassets/Contents.json | 6 + .../metro-now/metro-now/ContentView.swift | 49 ++ .../Preview Assets.xcassets/Contents.json | 6 + .../metro-now/metro-now/metro_nowApp.swift | 13 + 25 files changed, 1308 insertions(+), 1 deletion(-) create mode 100644 apps/mobile/metro-now/common/const/api-const.swift create mode 100644 apps/mobile/metro-now/common/managers/location-manager.swift create mode 100644 apps/mobile/metro-now/common/managers/network-manager.swift create mode 100644 apps/mobile/metro-now/common/types/api-types.swift create mode 100644 apps/mobile/metro-now/common/types/metro-line.swift create mode 100644 apps/mobile/metro-now/common/utils/metro-line.utils.swift create mode 100644 apps/mobile/metro-now/common/utils/station.utils.swift create mode 100644 apps/mobile/metro-now/metro-now Watch App/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 apps/mobile/metro-now/metro-now Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 apps/mobile/metro-now/metro-now Watch App/Assets.xcassets/Contents.json create mode 100644 apps/mobile/metro-now/metro-now Watch App/ContentView.swift create mode 100644 apps/mobile/metro-now/metro-now Watch App/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 apps/mobile/metro-now/metro-now Watch App/metro_nowApp.swift create mode 100644 apps/mobile/metro-now/metro-now Watch App/pages/departure-list-item.swift create mode 100644 apps/mobile/metro-now/metro-now Watch App/pages/departure-placeholder.swift create mode 100644 apps/mobile/metro-now/metro-now Watch App/pages/main-page.swift create mode 100644 apps/mobile/metro-now/metro-now.xcodeproj/project.pbxproj create mode 100644 apps/mobile/metro-now/metro-now.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 apps/mobile/metro-now/metro-now/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 apps/mobile/metro-now/metro-now/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 apps/mobile/metro-now/metro-now/Assets.xcassets/Contents.json create mode 100644 apps/mobile/metro-now/metro-now/ContentView.swift create mode 100644 apps/mobile/metro-now/metro-now/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 apps/mobile/metro-now/metro-now/metro_nowApp.swift diff --git a/.github/workflows/app-ci.yaml b/.github/workflows/app-ci.yaml index 8823385f..4b7f247e 100644 --- a/.github/workflows/app-ci.yaml +++ b/.github/workflows/app-ci.yaml @@ -20,4 +20,4 @@ jobs: - name: Build run: | - xcodebuild build -scheme metro-now -project ./apps/mobile/metro-now/metro-now.xcodeproj | xcpretty && exit ${PIPESTATUS[0]} + # xcodebuild build -scheme metro-now -project ./apps/mobile/metro-now/metro-now.xcodeproj | xcpretty && exit ${PIPESTATUS[0]} diff --git a/apps/mobile/metro-now/common/const/api-const.swift b/apps/mobile/metro-now/common/const/api-const.swift new file mode 100644 index 00000000..3b51616a --- /dev/null +++ b/apps/mobile/metro-now/common/const/api-const.swift @@ -0,0 +1,4 @@ +// metro-now +// https://github.com/krystxf/metro-now + +let ENDPOINT: String = "https://api.metronow.dev" diff --git a/apps/mobile/metro-now/common/managers/location-manager.swift b/apps/mobile/metro-now/common/managers/location-manager.swift new file mode 100644 index 00000000..03b83234 --- /dev/null +++ b/apps/mobile/metro-now/common/managers/location-manager.swift @@ -0,0 +1,29 @@ +// metro-now +// https://github.com/krystxf/metro-now + +import SwiftUI + +import CoreLocation +import Foundation + +class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate { + private let locationManager = CLLocationManager() + + @Published var location: CLLocation? + + override init() { + super.init() + locationManager.delegate = self + locationManager.desiredAccuracy = kCLLocationAccuracyBest + locationManager.requestWhenInUseAuthorization() + locationManager.startUpdatingLocation() + } + + func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + if let location = locations.first { + DispatchQueue.main.async { + self.location = location + } + } + } +} diff --git a/apps/mobile/metro-now/common/managers/network-manager.swift b/apps/mobile/metro-now/common/managers/network-manager.swift new file mode 100644 index 00000000..f6a1b8e8 --- /dev/null +++ b/apps/mobile/metro-now/common/managers/network-manager.swift @@ -0,0 +1,121 @@ +// metro-now +// https://github.com/krystxf/metro-now + +import Foundation + +final class NetworkManager { + static let shared = NetworkManager() + + private init() {} + + func getMetroStops( + completed: @escaping (Result<[ApiStop], FetchErrorNew>) -> Void + ) { + guard let url = URL(string: "\(ENDPOINT)/stop/all?metroOnly=true") else { + completed(.failure(.invalidUrl)) + return + } + + let task = URLSession.shared.dataTask( + with: URLRequest(url: url) + ) { + data, response, error in + + if let _ = error { + completed(.failure(.general)) + return + } + + guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { + completed(.failure(.invalidResponse)) + return + } + + guard let data else { + completed(.failure(.invalidData)) + return + } + + do { + let decoder = JSONDecoder() + let decodedResponse = try decoder.decode([ApiStop].self, from: data) + completed(.success(decodedResponse)) + return + } catch { + completed(.failure(.invalidData)) + return + } + } + + task.resume() + } + + func getDepartures( + stopIds: [String], platformIds: [String], completed: @escaping (Result<[ApiDeparture], FetchErrorNew>) -> Void + ) { + guard let baseUrl = URL(string: "\(ENDPOINT)/departure") else { + completed(.failure(.invalidUrl)) + return + } + + let platformsQueryParams: [URLQueryItem] = platformIds.map { + URLQueryItem(name: "platform[]", value: $0) + } + let stopsQueryParams: [URLQueryItem] = stopIds.map { + URLQueryItem(name: "stop[]", value: $0) + } + + let url = baseUrl + .appending(queryItems: stopsQueryParams + platformsQueryParams + [ + URLQueryItem(name: "metroOnly", value: "true"), + ]) + print(url) + + let task = URLSession.shared.dataTask( + with: URLRequest(url: url) + ) { + data, response, error in + + if let _ = error { + completed(.failure(.general)) + return + } + + guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { + completed(.failure(.invalidResponse)) + return + } + + guard let data else { + print("Invalid data") + completed(.failure(.invalidData)) + return + } + + do { + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .iso8601 + + let decodedResponse = try decoder.decode( + [ApiDeparture].self, + from: data + ) + + completed(.success(decodedResponse)) + return + } catch { + completed(.failure(.invalidData)) + return + } + } + + task.resume() + } +} + +enum FetchErrorNew: Error { + case invalidUrl + case invalidResponse + case invalidData + case general +} diff --git a/apps/mobile/metro-now/common/types/api-types.swift b/apps/mobile/metro-now/common/types/api-types.swift new file mode 100644 index 00000000..0e9a0d1a --- /dev/null +++ b/apps/mobile/metro-now/common/types/api-types.swift @@ -0,0 +1,37 @@ +// metro-now +// https://github.com/krystxf/metro-now + +import Foundation + +struct ApiStop: Codable { + let id, name: String + let avgLatitude, avgLongitude: Double + let platforms: [ApiPlatform] +} + +struct ApiPlatform: Codable { + let id: String + let latitude, longitude: Double + let name: String + let isMetro: Bool + let routes: [ApiRoute] +} + +struct ApiRoute: Codable { + let id, name: String +} + +struct ApiDepartureDate: Codable { + let predicted: Date + let scheduled: Date +} + +struct ApiDeparture: Codable { + let platformId: String + let headsign: String + + let departure: ApiDepartureDate + let delay: Int + + let route: String +} diff --git a/apps/mobile/metro-now/common/types/metro-line.swift b/apps/mobile/metro-now/common/types/metro-line.swift new file mode 100644 index 00000000..9b9e6f6a --- /dev/null +++ b/apps/mobile/metro-now/common/types/metro-line.swift @@ -0,0 +1,8 @@ +// metro-now +// https://github.com/krystxf/metro-now + +enum MetroLine: String { + case A + case B + case C +} diff --git a/apps/mobile/metro-now/common/utils/metro-line.utils.swift b/apps/mobile/metro-now/common/utils/metro-line.utils.swift new file mode 100644 index 00000000..8a7f0b4d --- /dev/null +++ b/apps/mobile/metro-now/common/utils/metro-line.utils.swift @@ -0,0 +1,13 @@ +// metro-now +// https://github.com/krystxf/metro-now + +import SwiftUI + +func getMetroLineColor(_ line: MetroLine?) -> Color? { + switch line { + case .A: .green + case .B: .yellow + case .C: .red + default: nil + } +} diff --git a/apps/mobile/metro-now/common/utils/station.utils.swift b/apps/mobile/metro-now/common/utils/station.utils.swift new file mode 100644 index 00000000..1742977c --- /dev/null +++ b/apps/mobile/metro-now/common/utils/station.utils.swift @@ -0,0 +1,86 @@ +// metro-now +// https://github.com/krystxf/metro-now + +func shortenStopName(_ stop: String) -> String { + if stop == "Nemocnice Motol" { + return "N. Motol" + } else if stop == "Jiřího z Poděbrad" { + return "J. z Poděbrad" + } + + if stop.hasPrefix("Nádraží") { + return stop.replacingOccurrences(of: "Nádraží", with: "Nádr.") + } else if stop.hasSuffix("nádraží") { + return stop.replacingOccurrences(of: "nádraží", with: "nádr.") + } + + if stop.hasPrefix("Náměstí") { + return stop.replacingOccurrences(of: "Náměstí", with: "Nám.") + } else if stop.hasSuffix("náměstí") { + return stop.replacingOccurrences(of: "náměstí", with: "nám") + } + + return stop +} + +// All metro stops +/* + Anděl + Bořislavka + Bubenská + Budějovická + Černý Most + Českomoravská + Dejvická + Depo Hostivař + Flora + Florenc + Háje + Hlavní nádraží + Hloubětín + Hradčanská + Hůrka + Chodov + I. P. Pavlova + Invalidovna + Jinonice + Jiřího z Poděbrad + Kačerov + Karlovo náměstí + Kobylisy + Kolbenova + Křižíkova + Ládví + Letňany + Luka + Lužiny + Malostranská + Masarykovo nádraží + Můstek + Muzeum + Nádraží Holešovice + Nádraží Veleslavín + Nádraží Vysočany + Náměstí Míru + Národní třída + Nemocnice Motol + Nové Butovice + Opatov + Palmovka + Pankrác + Petřiny + Praha-Rajská zahrada + Praha-Smíchov + Pražského povstání + Prosek + Radlická + Roztyly + Skalka + Staroměstská + Stodůlky + Strašnická + Střížkov + Vyšehrad + Zličín + Želivského + */ diff --git a/apps/mobile/metro-now/metro-now Watch App/Assets.xcassets/AccentColor.colorset/Contents.json b/apps/mobile/metro-now/metro-now Watch App/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..b246f6b1 --- /dev/null +++ b/apps/mobile/metro-now/metro-now Watch App/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors": [ + { + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/apps/mobile/metro-now/metro-now Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json b/apps/mobile/metro-now/metro-now Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..876ec39e --- /dev/null +++ b/apps/mobile/metro-now/metro-now Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images": [ + { + "idiom": "universal", + "platform": "watchos", + "size": "1024x1024" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/apps/mobile/metro-now/metro-now Watch App/Assets.xcassets/Contents.json b/apps/mobile/metro-now/metro-now Watch App/Assets.xcassets/Contents.json new file mode 100644 index 00000000..dd65c04e --- /dev/null +++ b/apps/mobile/metro-now/metro-now Watch App/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/apps/mobile/metro-now/metro-now Watch App/ContentView.swift b/apps/mobile/metro-now/metro-now Watch App/ContentView.swift new file mode 100644 index 00000000..26cfa828 --- /dev/null +++ b/apps/mobile/metro-now/metro-now Watch App/ContentView.swift @@ -0,0 +1,99 @@ +// metro-now +// https://github.com/krystxf/metro-now + +import CoreLocation +import Foundation +import SwiftUI + +struct ContentView: View { + @StateObject private var locationManager = LocationManager() + @State var stops: [ApiStop]? = nil + @State var departures: [ApiDeparture]? = nil + + var body: some View { + VStack { + if + let location = locationManager.location, + let stops, + let closestStop = findClosestStop(to: location, stops: stops) + { + MainPage( + title: closestStop.name, + platforms: closestStop.platforms.map { + platform in + MainPagePlatform( + id: platform.id, + metroLine: MetroLine(rawValue: platform.routes[0].name), + departures: departures?.filter { departure in + departure.platformId == platform.id + } + ) + } + ) + + } else { + ProgressView() + } + } + .onAppear { + getAllMetroStops() + } + } + + func findClosestStop(to location: CLLocation, stops: [ApiStop]) -> ApiStop? { + var closestStop: ApiStop? + var closestDistance: CLLocationDistance? + + for stop in stops { + let stopLocation = CLLocation(latitude: stop.avgLatitude, longitude: stop.avgLongitude) + + let distance = location.distance(from: stopLocation) + + guard closestDistance != nil else { + closestStop = stop + closestDistance = distance + continue + } + + if distance < closestDistance! { + closestStop = stop + closestDistance = distance + } + } + + return closestStop + } + + func getAllMetroStops() { + NetworkManager.shared.getMetroStops { result in + DispatchQueue.main.async { + switch result { + case let .success(stops): + + self.stops = stops + + case let .failure(error): + print(error.localizedDescription) + } + } + } + + NetworkManager.shared + .getDepartures(stopIds: ["U462"], platformIds: []) { result in + DispatchQueue.main.async { + switch result { + case let .success(departures): + + self.departures = departures + + case let .failure(error): + print(error.localizedDescription) + } + } + } + } +} + +#Preview { + ContentView() +} diff --git a/apps/mobile/metro-now/metro-now Watch App/Preview Content/Preview Assets.xcassets/Contents.json b/apps/mobile/metro-now/metro-now Watch App/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 00000000..dd65c04e --- /dev/null +++ b/apps/mobile/metro-now/metro-now Watch App/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/apps/mobile/metro-now/metro-now Watch App/metro_nowApp.swift b/apps/mobile/metro-now/metro-now Watch App/metro_nowApp.swift new file mode 100644 index 00000000..af9d4060 --- /dev/null +++ b/apps/mobile/metro-now/metro-now Watch App/metro_nowApp.swift @@ -0,0 +1,13 @@ +// metro-now +// https://github.com/krystxf/metro-now + +import SwiftUI + +@main +struct metro_now_Watch_AppApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/apps/mobile/metro-now/metro-now Watch App/pages/departure-list-item.swift b/apps/mobile/metro-now/metro-now Watch App/pages/departure-list-item.swift new file mode 100644 index 00000000..48a9391a --- /dev/null +++ b/apps/mobile/metro-now/metro-now Watch App/pages/departure-list-item.swift @@ -0,0 +1,85 @@ +// metro-now +// https://github.com/krystxf/metro-now + +import SwiftUI + +struct DepartureListItem: View { + let color: Color + + let headsign: String + let departure: Date + + let nextHeadsign: String? + let nextDeparture: Date? + + let dateFormat = DateFormatter() + + init( + color: Color?, + headsign: String, + departure: Date, + nextHeadsign: String? = nil, + nextDeparture: Date? = nil + ) { + self.color = color ?? Color.gray.opacity(0.3) + self.headsign = headsign + self.departure = departure + self.nextHeadsign = nextHeadsign + self.nextDeparture = nextDeparture + } + + var body: some View { + VStack(alignment: .trailing) { + HStack { + Text(shortenStopName(headsign)) + Spacer() + + Text( + departure, + format: .relative( + presentation: .numeric, + unitsStyle: .narrow + ) + ) + +// format: +// .reference( +// to: departure, +// allowedFields: [.hour,.minute,.second], +// maxFieldCount: 2 +// ) +// + + // countdown +// Text( +// departure, +// format: +// .offset(to: .now) +// to: departure, +// allowedFields: [.hour, .minute, .second], +// maxFieldCount: 2 +// thresholdField: .second +// ) +// ) + } +// if let nextHeadsign, let nextDeparture { +// +// +// HStack { +// if nextHeadsign != headsign { +// Text(nextHeadsign) +// } +// Spacer() +// Text( +// "\(nextHeadsign == headsign ? "also " :"") \(departure,format: .relative(presentation: .numeric,unitsStyle: .narrow))" +// ) +// +// }.font(.system(size: 12)) +// } + } + .padding(.vertical, 10) + .padding(.horizontal, 5) + .background(color) + .clipShape(.rect(cornerRadius: 10)) + } +} diff --git a/apps/mobile/metro-now/metro-now Watch App/pages/departure-placeholder.swift b/apps/mobile/metro-now/metro-now Watch App/pages/departure-placeholder.swift new file mode 100644 index 00000000..4247b234 --- /dev/null +++ b/apps/mobile/metro-now/metro-now Watch App/pages/departure-placeholder.swift @@ -0,0 +1,33 @@ +// metro-now +// https://github.com/krystxf/metro-now + +import SwiftUI + +struct DeparturePlaceholder: View { + let color: Color + + init( + color: Color? + ) { + self.color = color ?? Color.gray.opacity(0.3) + } + + var body: some View { + VStack(alignment: .trailing) { + HStack { + Text("Loading...") + Spacer() + Text("--s") + } + HStack { + Spacer() + Text("also in --m --s") + }.font(.system(size: 12)) + } + .redacted(reason: .placeholder) + .padding(.vertical, 10) + .padding(.horizontal, 5) + .background(color) + .clipShape(.rect(cornerRadius: 10)) + } +} diff --git a/apps/mobile/metro-now/metro-now Watch App/pages/main-page.swift b/apps/mobile/metro-now/metro-now Watch App/pages/main-page.swift new file mode 100644 index 00000000..279aa2e9 --- /dev/null +++ b/apps/mobile/metro-now/metro-now Watch App/pages/main-page.swift @@ -0,0 +1,78 @@ +// metro-now +// https://github.com/krystxf/metro-now + +import SwiftUI + +struct Departure { + let id: String + let headsing: String +} + +struct MainPagePlatform { + let id: String + let metroLine: MetroLine? + let departures: [ApiDeparture]? +} + +struct MainPage: View { + let title: String + let platforms: [MainPagePlatform] + + var body: some View { + NavigationView { + ScrollView { + VStack { + ForEach(platforms, id: \.id) { platform in + let itemColor = getMetroLineColor(platform.metroLine) + + if let departures = platform.departures, departures.count > 0 { + let hasNextDeparture = departures.count > 1 + + DepartureListItem( + color: itemColor, + headsign: departures[0].headsign, + departure: departures[0].departure.predicted, + nextHeadsign: hasNextDeparture ? departures[1].headsign : nil, + nextDeparture: hasNextDeparture ? departures[1].departure.predicted : nil + ) + } else { + DeparturePlaceholder( + color: itemColor + ) + } + } + } + } + + .navigationTitle(shortenStopName(title)) + } + } +} + +#Preview { + MainPage( + title: "Muzeum", + platforms: [ + MainPagePlatform( + id: "1", + metroLine: .A, + departures: nil + ), + MainPagePlatform( + id: "2", + metroLine: .A, + departures: nil + ), + MainPagePlatform( + id: "3", + metroLine: .C, + departures: nil + ), + MainPagePlatform( + id: "4", + metroLine: .C, + departures: nil + ), + ] + ) +} diff --git a/apps/mobile/metro-now/metro-now.xcodeproj/project.pbxproj b/apps/mobile/metro-now/metro-now.xcodeproj/project.pbxproj new file mode 100644 index 00000000..35625cb1 --- /dev/null +++ b/apps/mobile/metro-now/metro-now.xcodeproj/project.pbxproj @@ -0,0 +1,538 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 2D001BB92CC8099C00C6B4F8 /* metro-now Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 2D001BB82CC8099C00C6B4F8 /* metro-now Watch App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 2D001BBA2CC8099C00C6B4F8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 2D001BA02CC8099B00C6B4F8 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2D001BB72CC8099C00C6B4F8; + remoteInfo = "metro-now Watch App"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 2D001BCB2CC8099D00C6B4F8 /* Embed Watch Content */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "$(CONTENTS_FOLDER_PATH)/Watch"; + dstSubfolderSpec = 16; + files = ( + 2D001BB92CC8099C00C6B4F8 /* metro-now Watch App.app in Embed Watch Content */, + ); + name = "Embed Watch Content"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 2D001BA82CC8099B00C6B4F8 /* metro-now.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "metro-now.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 2D001BB82CC8099C00C6B4F8 /* metro-now Watch App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "metro-now Watch App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 2D9601C72CC812D6000EF3D5 /* Exceptions for "common" folder in "metro-now" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + "const/api-const.swift", + "managers/location-manager.swift", + "managers/network-manager.swift", + "types/api-types.swift", + "types/metro-line.swift", + "utils/metro-line.utils.swift", + utils/station.utils.swift, + ); + target = 2D001BA72CC8099B00C6B4F8 /* metro-now */; + }; + 2D9601C92CC812EF000EF3D5 /* Exceptions for "common" folder in "metro-now Watch App" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + "const/api-const.swift", + "managers/location-manager.swift", + "managers/network-manager.swift", + "types/api-types.swift", + "types/metro-line.swift", + "utils/metro-line.utils.swift", + utils/station.utils.swift, + ); + target = 2D001BB72CC8099C00C6B4F8 /* metro-now Watch App */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 2D001BAA2CC8099B00C6B4F8 /* metro-now */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = "metro-now"; + sourceTree = ""; + }; + 2D001BBC2CC8099C00C6B4F8 /* metro-now Watch App */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = "metro-now Watch App"; + sourceTree = ""; + }; + 2D9601C12CC8126F000EF3D5 /* common */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 2D9601C72CC812D6000EF3D5 /* Exceptions for "common" folder in "metro-now" target */, + 2D9601C92CC812EF000EF3D5 /* Exceptions for "common" folder in "metro-now Watch App" target */, + ); + path = common; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 2D001BA52CC8099B00C6B4F8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D001BB52CC8099C00C6B4F8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 2D001B9F2CC8099B00C6B4F8 = { + isa = PBXGroup; + children = ( + 2D9601C12CC8126F000EF3D5 /* common */, + 2D001BAA2CC8099B00C6B4F8 /* metro-now */, + 2D001BBC2CC8099C00C6B4F8 /* metro-now Watch App */, + 2D001BA92CC8099B00C6B4F8 /* Products */, + ); + sourceTree = ""; + }; + 2D001BA92CC8099B00C6B4F8 /* Products */ = { + isa = PBXGroup; + children = ( + 2D001BA82CC8099B00C6B4F8 /* metro-now.app */, + 2D001BB82CC8099C00C6B4F8 /* metro-now Watch App.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 2D001BA72CC8099B00C6B4F8 /* metro-now */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2D001BCC2CC8099D00C6B4F8 /* Build configuration list for PBXNativeTarget "metro-now" */; + buildPhases = ( + 2D001BA42CC8099B00C6B4F8 /* Sources */, + 2D001BA52CC8099B00C6B4F8 /* Frameworks */, + 2D001BA62CC8099B00C6B4F8 /* Resources */, + 2D001BCB2CC8099D00C6B4F8 /* Embed Watch Content */, + ); + buildRules = ( + ); + dependencies = ( + 2D001BBB2CC8099C00C6B4F8 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 2D001BAA2CC8099B00C6B4F8 /* metro-now */, + 2D9601C12CC8126F000EF3D5 /* common */, + ); + name = "metro-now"; + packageProductDependencies = ( + ); + productName = "metro-now"; + productReference = 2D001BA82CC8099B00C6B4F8 /* metro-now.app */; + productType = "com.apple.product-type.application"; + }; + 2D001BB72CC8099C00C6B4F8 /* metro-now Watch App */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2D001BC82CC8099D00C6B4F8 /* Build configuration list for PBXNativeTarget "metro-now Watch App" */; + buildPhases = ( + 2D001BB42CC8099C00C6B4F8 /* Sources */, + 2D001BB52CC8099C00C6B4F8 /* Frameworks */, + 2D001BB62CC8099C00C6B4F8 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 2D001BBC2CC8099C00C6B4F8 /* metro-now Watch App */, + ); + name = "metro-now Watch App"; + packageProductDependencies = ( + ); + productName = "metro-now Watch App"; + productReference = 2D001BB82CC8099C00C6B4F8 /* metro-now Watch App.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 2D001BA02CC8099B00C6B4F8 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1600; + LastUpgradeCheck = 1600; + TargetAttributes = { + 2D001BA72CC8099B00C6B4F8 = { + CreatedOnToolsVersion = 16.0; + }; + 2D001BB72CC8099C00C6B4F8 = { + CreatedOnToolsVersion = 16.0; + }; + }; + }; + buildConfigurationList = 2D001BA32CC8099B00C6B4F8 /* Build configuration list for PBXProject "metro-now" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 2D001B9F2CC8099B00C6B4F8; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = 2D001BA92CC8099B00C6B4F8 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 2D001BA72CC8099B00C6B4F8 /* metro-now */, + 2D001BB72CC8099C00C6B4F8 /* metro-now Watch App */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 2D001BA62CC8099B00C6B4F8 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D001BB62CC8099C00C6B4F8 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 2D001BA42CC8099B00C6B4F8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D001BB42CC8099C00C6B4F8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 2D001BBB2CC8099C00C6B4F8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2D001BB72CC8099C00C6B4F8 /* metro-now Watch App */; + targetProxy = 2D001BBA2CC8099C00C6B4F8 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 2D001BC62CC8099D00C6B4F8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 2D001BC72CC8099D00C6B4F8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SWIFT_COMPILATION_MODE = wholemodule; + }; + name = Release; + }; + 2D001BC92CC8099D00C6B4F8 /* 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_ASSET_PATHS = "\"metro-now Watch App/Preview Content\""; + DEVELOPMENT_TEAM = R6WU5ABNG2; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "metro-now"; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = ""; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_WKCompanionAppBundleIdentifier = "com.krystof.metro-now"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.krystof.metro-now.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 = 11.0; + }; + name = Debug; + }; + 2D001BCA2CC8099D00C6B4F8 /* 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_ASSET_PATHS = "\"metro-now Watch App/Preview Content\""; + DEVELOPMENT_TEAM = R6WU5ABNG2; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "metro-now"; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = ""; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_WKCompanionAppBundleIdentifier = "com.krystof.metro-now"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.krystof.metro-now.watchkitapp"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + VALIDATE_PRODUCT = YES; + WATCHOS_DEPLOYMENT_TARGET = 11.0; + }; + name = Release; + }; + 2D001BCD2CC8099D00C6B4F8 /* 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_ASSET_PATHS = "\"metro-now/Preview Content\""; + DEVELOPMENT_TEAM = R6WU5ABNG2; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "metro-now"; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "We need your location to provide live updates."; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.krystof.metro-now"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 2D001BCE2CC8099D00C6B4F8 /* 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_ASSET_PATHS = "\"metro-now/Preview Content\""; + DEVELOPMENT_TEAM = R6WU5ABNG2; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "metro-now"; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "We need your location to provide live updates."; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.krystof.metro-now"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 2D001BA32CC8099B00C6B4F8 /* Build configuration list for PBXProject "metro-now" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D001BC62CC8099D00C6B4F8 /* Debug */, + 2D001BC72CC8099D00C6B4F8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2D001BC82CC8099D00C6B4F8 /* Build configuration list for PBXNativeTarget "metro-now Watch App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D001BC92CC8099D00C6B4F8 /* Debug */, + 2D001BCA2CC8099D00C6B4F8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2D001BCC2CC8099D00C6B4F8 /* Build configuration list for PBXNativeTarget "metro-now" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D001BCD2CC8099D00C6B4F8 /* Debug */, + 2D001BCE2CC8099D00C6B4F8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 2D001BA02CC8099B00C6B4F8 /* Project object */; +} diff --git a/apps/mobile/metro-now/metro-now.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/apps/mobile/metro-now/metro-now.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/apps/mobile/metro-now/metro-now.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/apps/mobile/metro-now/metro-now/Assets.xcassets/AccentColor.colorset/Contents.json b/apps/mobile/metro-now/metro-now/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..b246f6b1 --- /dev/null +++ b/apps/mobile/metro-now/metro-now/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors": [ + { + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/apps/mobile/metro-now/metro-now/Assets.xcassets/AppIcon.appiconset/Contents.json b/apps/mobile/metro-now/metro-now/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..208e83e1 --- /dev/null +++ b/apps/mobile/metro-now/metro-now/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images": [ + { + "idiom": "universal", + "platform": "ios", + "size": "1024x1024" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "dark" + } + ], + "idiom": "universal", + "platform": "ios", + "size": "1024x1024" + }, + { + "appearances": [ + { + "appearance": "luminosity", + "value": "tinted" + } + ], + "idiom": "universal", + "platform": "ios", + "size": "1024x1024" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/apps/mobile/metro-now/metro-now/Assets.xcassets/Contents.json b/apps/mobile/metro-now/metro-now/Assets.xcassets/Contents.json new file mode 100644 index 00000000..dd65c04e --- /dev/null +++ b/apps/mobile/metro-now/metro-now/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/apps/mobile/metro-now/metro-now/ContentView.swift b/apps/mobile/metro-now/metro-now/ContentView.swift new file mode 100644 index 00000000..24510966 --- /dev/null +++ b/apps/mobile/metro-now/metro-now/ContentView.swift @@ -0,0 +1,49 @@ +// metro-now +// https://github.com/krystxf/metro-now + +import SwiftUI + +import CoreLocation +import Foundation + +class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate { + private let locationManager = CLLocationManager() + + @Published var location: CLLocation? + + override init() { + super.init() + locationManager.delegate = self + locationManager.desiredAccuracy = kCLLocationAccuracyBest + locationManager.requestWhenInUseAuthorization() + locationManager.startUpdatingLocation() + } + + func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + if let location = locations.first { + DispatchQueue.main.async { + self.location = location + } + } + } +} + +struct ContentView: View { + @StateObject private var locationManager = LocationManager() + + var body: some View { + VStack { + if let location = locationManager.location { + Text("Latitude: \(location.coordinate.latitude)") + Text("Longitude: \(location.coordinate.longitude)") + } else { + Text("Fetching location...") + } + } + .padding() + } +} + +#Preview { + ContentView() +} diff --git a/apps/mobile/metro-now/metro-now/Preview Content/Preview Assets.xcassets/Contents.json b/apps/mobile/metro-now/metro-now/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 00000000..dd65c04e --- /dev/null +++ b/apps/mobile/metro-now/metro-now/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/apps/mobile/metro-now/metro-now/metro_nowApp.swift b/apps/mobile/metro-now/metro-now/metro_nowApp.swift new file mode 100644 index 00000000..26076c8d --- /dev/null +++ b/apps/mobile/metro-now/metro-now/metro_nowApp.swift @@ -0,0 +1,13 @@ +// metro-now +// https://github.com/krystxf/metro-now + +import SwiftUI + +@main +struct metro_nowApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +}