Skip to content

Commit bc424e5

Browse files
committed
feat(ios): platforms list
1 parent efd15d3 commit bc424e5

File tree

4 files changed

+180
-46
lines changed

4 files changed

+180
-46
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// metro-now
2+
// https://github.com/krystxf/metro-now
3+
4+
import SwiftUI
5+
6+
struct RouteNameIconView: View {
7+
let systemName: String
8+
let background: Color
9+
10+
var body: some View {
11+
Image(systemName: systemName) // TODO: replace with Text component for more flexibility
12+
.imageScale(.medium)
13+
.padding(5)
14+
.foregroundStyle(.white)
15+
.background(background)
16+
.clipShape(.rect(cornerRadius: 6))
17+
.overlay(
18+
RoundedRectangle(cornerRadius: 6)
19+
.stroke(.white, lineWidth: 2)
20+
)
21+
}
22+
}
23+
24+
#Preview {
25+
RouteNameIconView(
26+
systemName: "a",
27+
background: .green
28+
)
29+
}

apps/mobile/metro-now/common/utils/metro-line.utils.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ func getMetroLineColor(_ line: MetroLine?) -> Color? {
1111
default: nil
1212
}
1313
}
14+
15+
func getMetroLineColor(_ line: String) -> Color? {
16+
getMetroLineColor(MetroLine(rawValue: line.uppercased()))
17+
}

apps/mobile/metro-now/metro-now.xcodeproj/project.pbxproj

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,24 +40,11 @@
4040
/* End PBXFileReference section */
4141

4242
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
43-
2D9601C72CC812D6000EF3D5 /* Exceptions for "common" folder in "metro-now" target */ = {
44-
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
45-
membershipExceptions = (
46-
components/countdown.view.swift,
47-
"const/api-const.swift",
48-
"managers/location-manager.swift",
49-
"managers/network-manager.swift",
50-
"types/api-types.swift",
51-
"types/metro-line.swift",
52-
"utils/metro-line.utils.swift",
53-
utils/station.utils.swift,
54-
);
55-
target = 2D001BA72CC8099B00C6B4F8 /* metro-now */;
56-
};
5743
2D9601C92CC812EF000EF3D5 /* Exceptions for "common" folder in "metro-now Watch App" target */ = {
5844
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
5945
membershipExceptions = (
6046
components/countdown.view.swift,
47+
"components/route-name.view.swift",
6148
"const/api-const.swift",
6249
"managers/location-manager.swift",
6350
"managers/network-manager.swift",
@@ -84,7 +71,6 @@
8471
2D9601C12CC8126F000EF3D5 /* common */ = {
8572
isa = PBXFileSystemSynchronizedRootGroup;
8673
exceptions = (
87-
2D9601C72CC812D6000EF3D5 /* Exceptions for "common" folder in "metro-now" target */,
8874
2D9601C92CC812EF000EF3D5 /* Exceptions for "common" folder in "metro-now Watch App" target */,
8975
);
9076
path = common;
@@ -187,7 +173,7 @@
187173
attributes = {
188174
BuildIndependentTargetsInParallel = 1;
189175
LastSwiftUpdateCheck = 1600;
190-
LastUpgradeCheck = 1600;
176+
LastUpgradeCheck = 1610;
191177
TargetAttributes = {
192178
2D001BA72CC8099B00C6B4F8 = {
193179
CreatedOnToolsVersion = 16.0;
@@ -294,6 +280,7 @@
294280
CLANG_WARN_UNREACHABLE_CODE = YES;
295281
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
296282
COPY_PHASE_STRIP = NO;
283+
DEAD_CODE_STRIPPING = YES;
297284
DEBUG_INFORMATION_FORMAT = dwarf;
298285
ENABLE_STRICT_OBJC_MSGSEND = YES;
299286
ENABLE_TESTABILITY = YES;
@@ -355,6 +342,7 @@
355342
CLANG_WARN_UNREACHABLE_CODE = YES;
356343
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
357344
COPY_PHASE_STRIP = NO;
345+
DEAD_CODE_STRIPPING = YES;
358346
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
359347
ENABLE_NS_ASSERTIONS = NO;
360348
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -443,7 +431,7 @@
443431
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
444432
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
445433
CODE_SIGN_STYLE = Automatic;
446-
CURRENT_PROJECT_VERSION = 1;
434+
CURRENT_PROJECT_VERSION = 0.2;
447435
DEVELOPMENT_ASSET_PATHS = "\"metro-now/Preview Content\"";
448436
DEVELOPMENT_TEAM = R6WU5ABNG2;
449437
ENABLE_PREVIEWS = YES;
@@ -456,7 +444,7 @@
456444
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
457445
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
458446
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
459-
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
447+
IPHONEOS_DEPLOYMENT_TARGET = 18;
460448
LD_RUNPATH_SEARCH_PATHS = (
461449
"$(inherited)",
462450
"@executable_path/Frameworks",
@@ -477,7 +465,7 @@
477465
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
478466
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
479467
CODE_SIGN_STYLE = Automatic;
480-
CURRENT_PROJECT_VERSION = 1;
468+
CURRENT_PROJECT_VERSION = 0.2;
481469
DEVELOPMENT_ASSET_PATHS = "\"metro-now/Preview Content\"";
482470
DEVELOPMENT_TEAM = R6WU5ABNG2;
483471
ENABLE_PREVIEWS = YES;
@@ -490,7 +478,7 @@
490478
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
491479
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
492480
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
493-
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
481+
IPHONEOS_DEPLOYMENT_TARGET = 18;
494482
LD_RUNPATH_SEARCH_PATHS = (
495483
"$(inherited)",
496484
"@executable_path/Frameworks",

apps/mobile/metro-now/metro-now/ContentView.swift

Lines changed: 139 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,159 @@
11
// metro-now
22
// https://github.com/krystxf/metro-now
33

4-
import SwiftUI
5-
64
import CoreLocation
75
import Foundation
6+
import SwiftUI
7+
8+
struct ContentView: View {
9+
@StateObject private var locationManager = LocationManager()
10+
@State var stops: [ApiStop]? = nil
11+
@State var departures: [ApiDeparture]? = nil
12+
private let timer = Timer.publish(every: 2, on: .main, in: .common).autoconnect()
13+
14+
var body: some View {
15+
NavigationStack {
16+
if let location = locationManager.location,
17+
let stops,
18+
let closestStop = findClosestStop(to: location, stops: stops)
19+
20+
{
21+
List(closestStop.platforms, id: \.id) { platform in
22+
let icon = RouteNameIconView(
23+
systemName: platform
24+
.routes[0].name
25+
.lowercased(),
26+
background: getMetroLineColor(platform
27+
.routes[0].name) ?? .black
28+
)
29+
30+
if let departures {
31+
let platformDepartures = departures.filter { departure in
32+
departure.platformId == platform.id
33+
}
834

9-
class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
10-
private let locationManager = CLLocationManager()
35+
VStack(alignment: .trailing) {
36+
if platformDepartures.count > 0 {
37+
HStack {
38+
icon
1139

12-
@Published var location: CLLocation?
40+
Text(platformDepartures[0].headsign)
41+
Spacer()
42+
CountdownView(
43+
targetDate: platformDepartures[0].departure.predicted
44+
)
45+
}
46+
}
47+
if platformDepartures.count > 1 {
48+
if platformDepartures[0].headsign != platformDepartures[1].headsign {
49+
HStack {
50+
Text(platformDepartures[1].headsign)
51+
Spacer()
52+
CountdownView(
53+
targetDate: platformDepartures[1].departure.predicted
54+
)
55+
}
1356

14-
override init() {
15-
super.init()
16-
locationManager.delegate = self
17-
locationManager.desiredAccuracy = kCLLocationAccuracyBest
18-
locationManager.requestWhenInUseAuthorization()
19-
locationManager.startUpdatingLocation()
57+
} else {
58+
CountdownView(
59+
targetDate: platformDepartures[1].departure.predicted
60+
) { "Also in \($0)" }
61+
}
62+
}
63+
}
64+
} else {
65+
VStack {
66+
HStack {
67+
icon
68+
69+
Text("Loading...")
70+
.redacted(reason: .placeholder)
71+
Spacer()
72+
Text("--m --s")
73+
.redacted(reason: .placeholder)
74+
}
75+
HStack {
76+
Spacer()
77+
Text("also in --m --s")
78+
.redacted(reason: .placeholder)
79+
}
80+
}
81+
}
82+
}
83+
.navigationTitle(closestStop.name)
84+
} else {
85+
ProgressView()
86+
}
87+
}
88+
.onAppear {
89+
getAllMetroStops()
90+
}
91+
.onReceive(timer) { _ in
92+
getStopDepartures()
93+
}
2094
}
2195

22-
func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
23-
if let location = locations.first {
24-
DispatchQueue.main.async {
25-
self.location = location
96+
func findClosestStop(to location: CLLocation, stops: [ApiStop]) -> ApiStop? {
97+
var closestStop: ApiStop?
98+
var closestDistance: CLLocationDistance?
99+
100+
for stop in stops {
101+
let stopLocation = CLLocation(latitude: stop.avgLatitude, longitude: stop.avgLongitude)
102+
103+
let distance = location.distance(from: stopLocation)
104+
105+
guard closestDistance != nil else {
106+
closestStop = stop
107+
closestDistance = distance
108+
continue
109+
}
110+
111+
if distance < closestDistance! {
112+
closestStop = stop
113+
closestDistance = distance
26114
}
27115
}
116+
117+
return closestStop
28118
}
29-
}
30119

31-
struct ContentView: View {
32-
@StateObject private var locationManager = LocationManager()
120+
func getAllMetroStops() {
121+
NetworkManager.shared.getMetroStops { result in
122+
DispatchQueue.main.async {
123+
switch result {
124+
case let .success(stops):
33125

34-
var body: some View {
35-
VStack {
36-
if let location = locationManager.location {
37-
Text("Latitude: \(location.coordinate.latitude)")
38-
Text("Longitude: \(location.coordinate.longitude)")
39-
} else {
40-
Text("Fetching location...")
126+
self.stops = stops
127+
128+
case let .failure(error):
129+
print(error.localizedDescription)
130+
}
41131
}
42132
}
43-
.padding()
133+
}
134+
135+
func getStopDepartures() {
136+
guard
137+
let location = locationManager.location,
138+
let stops,
139+
let closestStop = findClosestStop(to: location, stops: stops)
140+
else {
141+
return
142+
}
143+
144+
NetworkManager.shared
145+
.getDepartures(stopIds: [closestStop.id], platformIds: []) { result in
146+
DispatchQueue.main.async {
147+
switch result {
148+
case let .success(departures):
149+
150+
self.departures = departures
151+
152+
case let .failure(error):
153+
print(error.localizedDescription)
154+
}
155+
}
156+
}
44157
}
45158
}
46159

0 commit comments

Comments
 (0)