From c1ad59c33ac1301224259bebcfafc81d4f4e5a5b Mon Sep 17 00:00:00 2001 From: Krystof Date: Wed, 24 Jul 2024 17:27:37 +0200 Subject: [PATCH] refactor(app): bring back iOS17 support --- .../Components/Countdown/Countdown.swift | 94 +++++++++++++++++++ .../Components/MetroDeparture/Card.swift | 44 +++++---- .../MetroDeparture/FirstDeparture.swift | 13 +-- .../MetroDeparture/MetroDeparture.swift | 25 ++--- .../MetroDeparture/SecondDeparture.swift | 25 +++-- app/metro-now.xcodeproj/project.pbxproj | 13 ++- .../Core/Map/StationDetailView.swift | 11 +-- 7 files changed, 168 insertions(+), 57 deletions(-) create mode 100644 app/Common/Components/Countdown/Countdown.swift diff --git a/app/Common/Components/Countdown/Countdown.swift b/app/Common/Components/Countdown/Countdown.swift new file mode 100644 index 00000000..ddb076ff --- /dev/null +++ b/app/Common/Components/Countdown/Countdown.swift @@ -0,0 +1,94 @@ + +import Foundation +import SwiftUI + +@available(iOS 18.0, *) +struct CountdownIOS18: View { + let date: Date + + init(_ date: Date) { + self.date = date + } + + var body: some View { + Text( + .currentDate, + format: + .reference( + to: date, + allowedFields: [.second, .minute, .hour], + maxFieldCount: 2 + ) + ) + } +} + +struct CountdownLegacy: View { + let date: Date + @State var countdownText: String = " - " + + init(_ date: Date) { + self.date = date + } + + func updateCountdownText() { + let formatter = RelativeDateTimeFormatter() + formatter.unitsStyle = .abbreviated + countdownText = formatter.localizedString(for: date, relativeTo: .now) + } + + let timer = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect() + + var body: some View { + Text(countdownText) + .onAppear { + updateCountdownText() + } + .onReceive(timer) { _ in + updateCountdownText() + } + } +} + +struct Countdown: View { + let date: Date + + init(_ date: Date) { + self.date = date + } + + var body: some View { + Group { + if #available(iOS 18.0, *) { + CountdownIOS18(date) + } else { + CountdownLegacy(date) + } + } + } +} + +#Preview("Seconds") { + Countdown(Date(timeIntervalSinceNow: 59)) +} + +#Preview("Seconds in past") { + Countdown(Date(timeIntervalSinceNow: 0)) +} + +#Preview("Minutes") { + Countdown(Date(timeIntervalSinceNow: 59 * 60)) +} + +#Preview("Minutes in past") { + Countdown(Date(timeIntervalSinceNow: -1 * 60)) +} + +#Preview("Hours") { + Countdown(Date(timeIntervalSinceNow: 10 * 60 * 60)) +} + + +#Preview("Hours in past") { + Countdown(Date(timeIntervalSinceNow: -1 * 2 * 60 * 60)) +} diff --git a/app/Common/Components/MetroDeparture/Card.swift b/app/Common/Components/MetroDeparture/Card.swift index cf156cdd..1e4204cb 100644 --- a/app/Common/Components/MetroDeparture/Card.swift +++ b/app/Common/Components/MetroDeparture/Card.swift @@ -30,28 +30,32 @@ struct MetroDepartureCard: View { } #Preview { - MetroDepartureCard( - backgroundColor: getMetroLineColor("A")) - { - HStack { - MetroDepartureCardLabel(direction: "Nemocnice Motol", metroLine: "A") - Spacer() + VStack { + MetroDepartureCard( + backgroundColor: getMetroLineColor("A")) + { + HStack { + MetroDepartureCardLabel(direction: "Nemocnice Motol", metroLine: "A") + Spacer() + } } - } - MetroDepartureCard( - backgroundColor: getMetroLineColor("B")) - { - HStack { - MetroDepartureCardLabel(direction: "Černý Most", metroLine: "B") - Spacer() + MetroDepartureCard( + backgroundColor: getMetroLineColor("B")) + { + HStack { + MetroDepartureCardLabel(direction: "Černý Most", metroLine: "B") + Spacer() + } } - } - MetroDepartureCard( - backgroundColor: getMetroLineColor("C")) - { - HStack { - MetroDepartureCardLabel(direction: "Haje", metroLine: "C") - Spacer() + MetroDepartureCard( + backgroundColor: getMetroLineColor("C")) + { + HStack { + MetroDepartureCardLabel(direction: "Haje", metroLine: "C") + Spacer() + } } } + .padding(.horizontal, 10) + Spacer() } diff --git a/app/Common/Components/MetroDeparture/FirstDeparture.swift b/app/Common/Components/MetroDeparture/FirstDeparture.swift index 591e117a..42d00a41 100644 --- a/app/Common/Components/MetroDeparture/FirstDeparture.swift +++ b/app/Common/Components/MetroDeparture/FirstDeparture.swift @@ -5,14 +5,9 @@ struct MetroDepartureCardFirstDeparture: View { let departureDate: Date var body: some View { - Text( - .currentDate, format: .reference( - to: departureDate, - allowedFields: [.second, .minute, .hour] - ) - ) - .fontWeight(.bold) - .foregroundStyle(.white) - .foregroundStyle(.white) + Countdown(departureDate) + .fontWeight(.bold) + .foregroundStyle(.white) + .foregroundStyle(.white) } } diff --git a/app/Common/Components/MetroDeparture/MetroDeparture.swift b/app/Common/Components/MetroDeparture/MetroDeparture.swift index f6f2c3f0..7839d349 100644 --- a/app/Common/Components/MetroDeparture/MetroDeparture.swift +++ b/app/Common/Components/MetroDeparture/MetroDeparture.swift @@ -13,19 +13,22 @@ struct MetroDeparture: View { var body: some View { MetroDepartureCard(backgroundColor: getMetroLineColor(metroLine)) { - MetroDepartureCardLabel(direction: direction, metroLine: metroLine) + HStack { + MetroDepartureCardLabel(direction: direction, metroLine: metroLine) - Spacer() + Spacer() - VStack { - if departures.count >= 1 { - MetroDepartureCardFirstDeparture(departureDate: departures[0].departure) - } - if departures.count >= 2 { - MetroDepartureCardSecondDeparture( - direction: departures[0].heading == departures[1].heading ? nil : departures[1].heading, - departureDate: departures[1].departure - ) + VStack(alignment: .trailing) { + if departures.count >= 1 { + MetroDepartureCardFirstDeparture(departureDate: departures[0].departure) + } + + if departures.count >= 2 { + MetroDepartureCardSecondDeparture( + direction: departures[0].heading == departures[1].heading ? nil : departures[1].heading, + departureDate: departures[1].departure + ) + } } } } diff --git a/app/Common/Components/MetroDeparture/SecondDeparture.swift b/app/Common/Components/MetroDeparture/SecondDeparture.swift index a294cf93..db6e11e8 100644 --- a/app/Common/Components/MetroDeparture/SecondDeparture.swift +++ b/app/Common/Components/MetroDeparture/SecondDeparture.swift @@ -4,17 +4,24 @@ import SwiftUI struct MetroDepartureCardSecondDeparture: View { let direction: String? let departureDate: Date + let text: String + + init(direction: String?, departureDate: Date) { + self.direction = direction + self.departureDate = departureDate + + if let direction { + text = "To \(direction) " + } else { + text = "Also " + } + } var body: some View { - Text( - direction == nil ? "Also in " : "Also to \(String(describing: direction)) in" - ).font(.caption2) - .fontWeight(.bold) - .foregroundStyle(.white) - .opacity(0.9) - Text( - .currentDate, format: .reference(to: departureDate, allowedFields: [.second, .minute, .hour]) - ) + HStack(spacing: 0) { + Text(text) + Countdown(departureDate) + } .font(.caption2) .fontWeight(.bold) .foregroundStyle(.white) diff --git a/app/metro-now.xcodeproj/project.pbxproj b/app/metro-now.xcodeproj/project.pbxproj index fb92cccb..8b928705 100644 --- a/app/metro-now.xcodeproj/project.pbxproj +++ b/app/metro-now.xcodeproj/project.pbxproj @@ -181,6 +181,13 @@ ); target = 2DC639D72BF3CCBA00A72C7F /* metro-now */; }; + 2D6C8CE02C51488E003E09A8 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Countdown.swift, + ); + target = 2DC639D72BF3CCBA00A72C7F /* metro-now */; + }; 2DFD2F2B2C4DC2EC009C81CC /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( @@ -206,6 +213,7 @@ /* Begin PBXFileSystemSynchronizedRootGroup section */ 2D5BA5DE2C382D6A0055F12A /* MetroDeparture */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2D5BA5E22C382D8D0055F12A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = MetroDeparture; sourceTree = ""; }; 2D5BA5EA2C382FC30055F12A /* MapAnnotation */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2D5BA5EB2C382FEA0055F12A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = MapAnnotation; sourceTree = ""; }; + 2D6C8CDD2C514882003E09A8 /* Countdown */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2D6C8CE02C51488E003E09A8 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Countdown; sourceTree = ""; }; 2DFD2F272C4DC296009C81CC /* API */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2DFD2F2B2C4DC2EC009C81CC /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 2DFD2F432C4DCC37009C81CC /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = API; sourceTree = ""; }; /* End PBXFileSystemSynchronizedRootGroup section */ @@ -297,6 +305,7 @@ 2D1B2C5B2BFADAAA007ED5EB /* Components */ = { isa = PBXGroup; children = ( + 2D6C8CDD2C514882003E09A8 /* Countdown */, 2D5BA5EA2C382FC30055F12A /* MapAnnotation */, 2D5BA5DE2C382D6A0055F12A /* MetroDeparture */, ); @@ -925,7 +934,7 @@ 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; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -959,7 +968,7 @@ 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; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/app/metro-now/Core/Map/StationDetailView.swift b/app/metro-now/Core/Map/StationDetailView.swift index b917ca84..b4ad655a 100644 --- a/app/metro-now/Core/Map/StationDetailView.swift +++ b/app/metro-now/Core/Map/StationDetailView.swift @@ -28,8 +28,7 @@ struct StationDetailView: View { grouping: upcomingDeparturesArr, by: { $0.platform } ) - } - catch { + } catch { errorMessage = "Failed to fetch departures: \(error)" } } @@ -47,10 +46,10 @@ struct StationDetailView: View { mapUrl = showDirection - ? URL( - string: + ? URL( + string: "maps://?saddr=&daddr=\(stationCoordinate.latitude),\(stationCoordinate.longitude)" - ) : nil + ) : nil } var body: some View { @@ -131,7 +130,7 @@ struct StationDetailView: View { MetroDeparture( metroLine: d[0].line, direction: d[0].heading, - departures: d + departures: d ) } }