Skip to content

Commit

Permalink
feat(ios): platforms list
Browse files Browse the repository at this point in the history
  • Loading branch information
krystxf committed Nov 5, 2024
1 parent efd15d3 commit bc424e5
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 46 deletions.
29 changes: 29 additions & 0 deletions apps/mobile/metro-now/common/components/route-name.view.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// metro-now
// https://github.com/krystxf/metro-now

import SwiftUI

struct RouteNameIconView: View {
let systemName: String
let background: Color

var body: some View {
Image(systemName: systemName) // TODO: replace with Text component for more flexibility
.imageScale(.medium)
.padding(5)
.foregroundStyle(.white)
.background(background)
.clipShape(.rect(cornerRadius: 6))
.overlay(
RoundedRectangle(cornerRadius: 6)
.stroke(.white, lineWidth: 2)
)
}
}

#Preview {
RouteNameIconView(
systemName: "a",
background: .green
)
}
4 changes: 4 additions & 0 deletions apps/mobile/metro-now/common/utils/metro-line.utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ func getMetroLineColor(_ line: MetroLine?) -> Color? {
default: nil
}
}

func getMetroLineColor(_ line: String) -> Color? {
getMetroLineColor(MetroLine(rawValue: line.uppercased()))
}
28 changes: 8 additions & 20 deletions apps/mobile/metro-now/metro-now.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,11 @@
/* End PBXFileReference section */

/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
2D9601C72CC812D6000EF3D5 /* Exceptions for "common" folder in "metro-now" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
components/countdown.view.swift,
"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 = (
components/countdown.view.swift,
"components/route-name.view.swift",
"const/api-const.swift",
"managers/location-manager.swift",
"managers/network-manager.swift",
Expand All @@ -84,7 +71,6 @@
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;
Expand Down Expand Up @@ -187,7 +173,7 @@
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1600;
LastUpgradeCheck = 1600;
LastUpgradeCheck = 1610;
TargetAttributes = {
2D001BA72CC8099B00C6B4F8 = {
CreatedOnToolsVersion = 16.0;
Expand Down Expand Up @@ -294,6 +280,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
Expand Down Expand Up @@ -355,6 +342,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
Expand Down Expand Up @@ -443,7 +431,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 0.2;
DEVELOPMENT_ASSET_PATHS = "\"metro-now/Preview Content\"";
DEVELOPMENT_TEAM = R6WU5ABNG2;
ENABLE_PREVIEWS = YES;
Expand All @@ -456,7 +444,7 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
IPHONEOS_DEPLOYMENT_TARGET = 18;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -477,7 +465,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 0.2;
DEVELOPMENT_ASSET_PATHS = "\"metro-now/Preview Content\"";
DEVELOPMENT_TEAM = R6WU5ABNG2;
ENABLE_PREVIEWS = YES;
Expand All @@ -490,7 +478,7 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
IPHONEOS_DEPLOYMENT_TARGET = 18;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down
165 changes: 139 additions & 26 deletions apps/mobile/metro-now/metro-now/ContentView.swift
Original file line number Diff line number Diff line change
@@ -1,46 +1,159 @@
// metro-now
// https://github.com/krystxf/metro-now

import SwiftUI

import CoreLocation
import Foundation
import SwiftUI

struct ContentView: View {
@StateObject private var locationManager = LocationManager()
@State var stops: [ApiStop]? = nil
@State var departures: [ApiDeparture]? = nil
private let timer = Timer.publish(every: 2, on: .main, in: .common).autoconnect()

var body: some View {
NavigationStack {
if let location = locationManager.location,
let stops,
let closestStop = findClosestStop(to: location, stops: stops)

{
List(closestStop.platforms, id: \.id) { platform in
let icon = RouteNameIconView(
systemName: platform
.routes[0].name
.lowercased(),
background: getMetroLineColor(platform
.routes[0].name) ?? .black
)

if let departures {
let platformDepartures = departures.filter { departure in
departure.platformId == platform.id
}

class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
VStack(alignment: .trailing) {
if platformDepartures.count > 0 {
HStack {
icon

@Published var location: CLLocation?
Text(platformDepartures[0].headsign)
Spacer()
CountdownView(
targetDate: platformDepartures[0].departure.predicted
)
}
}
if platformDepartures.count > 1 {
if platformDepartures[0].headsign != platformDepartures[1].headsign {
HStack {
Text(platformDepartures[1].headsign)
Spacer()
CountdownView(
targetDate: platformDepartures[1].departure.predicted
)
}

override init() {
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
} else {
CountdownView(
targetDate: platformDepartures[1].departure.predicted
) { "Also in \($0)" }
}
}
}
} else {
VStack {
HStack {
icon

Text("Loading...")
.redacted(reason: .placeholder)
Spacer()
Text("--m --s")
.redacted(reason: .placeholder)
}
HStack {
Spacer()
Text("also in --m --s")
.redacted(reason: .placeholder)
}
}
}
}
.navigationTitle(closestStop.name)
} else {
ProgressView()
}
}
.onAppear {
getAllMetroStops()
}
.onReceive(timer) { _ in
getStopDepartures()
}
}

func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
DispatchQueue.main.async {
self.location = location
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
}
}

struct ContentView: View {
@StateObject private var locationManager = LocationManager()
func getAllMetroStops() {
NetworkManager.shared.getMetroStops { result in
DispatchQueue.main.async {
switch result {
case let .success(stops):

var body: some View {
VStack {
if let location = locationManager.location {
Text("Latitude: \(location.coordinate.latitude)")
Text("Longitude: \(location.coordinate.longitude)")
} else {
Text("Fetching location...")
self.stops = stops

case let .failure(error):
print(error.localizedDescription)
}
}
}
.padding()
}

func getStopDepartures() {
guard
let location = locationManager.location,
let stops,
let closestStop = findClosestStop(to: location, stops: stops)
else {
return
}

NetworkManager.shared
.getDepartures(stopIds: [closestStop.id], platformIds: []) { result in
DispatchQueue.main.async {
switch result {
case let .success(departures):

self.departures = departures

case let .failure(error):
print(error.localizedDescription)
}
}
}
}
}

Expand Down

0 comments on commit bc424e5

Please sign in to comment.