From 05a2d7c030f3bb80a36da43a8aa564bc7ec9c4e2 Mon Sep 17 00:00:00 2001 From: Krystof Date: Thu, 4 Jul 2024 22:41:56 +0200 Subject: [PATCH 1/6] refactor(ios): use native countdown --- .github/workflows/app-ci.yaml | 2 +- app/Common/Utils/timeUtils.swift | 52 --- app/metro-now-tests/timeUtilsTests.swift | 406 ------------------ app/metro-now.xcodeproj/project.pbxproj | 18 +- .../PlatformDetail/PlatformDetailView.swift | 66 --- .../Core/PlatformList/PlatformListItem.swift | 40 +- 6 files changed, 17 insertions(+), 567 deletions(-) delete mode 100644 app/Common/Utils/timeUtils.swift delete mode 100644 app/metro-now-tests/timeUtilsTests.swift delete mode 100644 app/metro-now/Core/PlatformDetail/PlatformDetailView.swift diff --git a/.github/workflows/app-ci.yaml b/.github/workflows/app-ci.yaml index f32e0ee5..a6e4fb52 100644 --- a/.github/workflows/app-ci.yaml +++ b/.github/workflows/app-ci.yaml @@ -9,7 +9,7 @@ jobs: fail-fast: false matrix: device: [14, 15] - iOS: [17.2, 17.5] + iOS: [18] name: App CI 🚀 (iPhone ${{ matrix.device }}, iOS ${{ matrix.iOS }} ) steps: diff --git a/app/Common/Utils/timeUtils.swift b/app/Common/Utils/timeUtils.swift deleted file mode 100644 index feb5867e..00000000 --- a/app/Common/Utils/timeUtils.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// metro-now -// -// Created by Kryštof Krátký on 15.05.2024. -// - -import Foundation - -let SECONDS_IN_MINUTE = 60 -let SECONDS_IN_HOUR = 60 * SECONDS_IN_MINUTE -let SECONDS_IN_TWELVE_HOURS = 12 * SECONDS_IN_HOUR - -func formatTime(seconds: Int) -> String { - // (-1 * Int.min) > Int.max - // => this has to be separate check before seconds is converted to positive number - let isMoreThan12Hours = seconds > SECONDS_IN_TWELVE_HOURS - guard !isMoreThan12Hours else { - return ">12h" - } - let isLessThanMinus12Hours = seconds < -SECONDS_IN_TWELVE_HOURS - guard !isLessThanMinus12Hours else { - return "- >12h" - } - - let output = seconds < 0 ? "-" : "" - let positiveSeconds = abs(seconds) - - let hours = positiveSeconds / SECONDS_IN_HOUR - let minutes = (positiveSeconds % SECONDS_IN_HOUR) / SECONDS_IN_MINUTE - let remainingSeconds = positiveSeconds % SECONDS_IN_MINUTE - - guard hours == 0 else { - if minutes == 0 { - return String(format: "\(output)%dh", hours) - } else { - return String(format: "\(output)%dh %dm", hours, minutes) - } - } - guard minutes == 0 else { - if remainingSeconds == 0 { - return String(format: "\(output)%dm", minutes) - } else { - return String(format: "\(output)%dm %ds", minutes, remainingSeconds) - } - } - - return String(format: "\(output)%ds", remainingSeconds) -} - -func secondsFromNow(_ date: Date) -> Int { - Int(date.timeIntervalSince(Date.now)) -} diff --git a/app/metro-now-tests/timeUtilsTests.swift b/app/metro-now-tests/timeUtilsTests.swift deleted file mode 100644 index 12ed0e0a..00000000 --- a/app/metro-now-tests/timeUtilsTests.swift +++ /dev/null @@ -1,406 +0,0 @@ -// -// timeUtilsTests.swift -// metro-now-tests -// -// Created by Kryštof Krátký on 15.05.2024. -// - -@testable import metro_now -import XCTest - -let SECONDS_FROM_NOW_TESTS_ACCURACY = 1 // seconds - -final class timeUtilsTests: XCTestCase { - func testSecondsFromNowWithFutureDate() { - let now = Date.now - let futureDate = now.addingTimeInterval(60) - - let result = secondsFromNow(futureDate) - - XCTAssert(abs(result - 60) <= SECONDS_FROM_NOW_TESTS_ACCURACY) - } - - func testSecondsFromNowWithPastDate() { - let now = Date.now - let pastDate = now.addingTimeInterval(-30) - - let result = secondsFromNow(pastDate) - - XCTAssert(abs(result + 30) <= SECONDS_FROM_NOW_TESTS_ACCURACY) - } - - func testSecondsFromNowWithCurrentDate() { - let now = Date.now - - let result = secondsFromNow(now) - - XCTAssert(abs(result) <= SECONDS_FROM_NOW_TESTS_ACCURACY) - } - - func testSecondsFromNowWithPrecision() { - let now = Date() - let almostFutureDate = now.addingTimeInterval(2) - - let result = secondsFromNow(almostFutureDate) - - XCTAssert(abs(result - 2) <= SECONDS_FROM_NOW_TESTS_ACCURACY) - } - - // MARK: - outputs with seconds - - /// should show positive and negative or zero seconds without any issues - - func testsFormatTimePositiveSeconds() { - /// init variables for testing - var seconds: Int - var result: String - var expectedResult: String - - /// test 1 - seconds = 40 - result = formatTime(seconds: seconds) - expectedResult = "40s" - - XCTAssertEqual(result, expectedResult) - - /// test 2 - seconds = 1 - result = formatTime(seconds: seconds) - expectedResult = "1s" - - XCTAssertEqual(result, expectedResult) - - /// test 3 - seconds = 59 - result = formatTime(seconds: seconds) - expectedResult = "59s" - - XCTAssertEqual(result, expectedResult) - } - - func testsFormatTimeNegativeSeconds() { - /// init variables for testing - var seconds: Int - var result: String - var expectedResult: String - - /// test 1 - seconds = -40 - result = formatTime(seconds: seconds) - expectedResult = "-40s" - - XCTAssertEqual(result, expectedResult) - - /// test 2 - seconds = -1 - result = formatTime(seconds: seconds) - expectedResult = "-1s" - - XCTAssertEqual(result, expectedResult) - - /// test 3 - seconds = -59 - result = formatTime(seconds: seconds) - expectedResult = "-59s" - - XCTAssertEqual(result, expectedResult) - - /// test 4 - seconds = -47 - result = formatTime(seconds: seconds) - expectedResult = "-47s" - - XCTAssertEqual(result, expectedResult) - } - - func testsFormatTimeZeroSeconds() { - /// init variables for testing - var seconds: Int - var result: String - var expectedResult: String - - /// test 1 - seconds = 0 - result = formatTime(seconds: seconds) - expectedResult = "0s" - - XCTAssertEqual(result, expectedResult) - - /// test 2 - seconds = -0 - result = formatTime(seconds: seconds) - expectedResult = "0s" - - XCTAssertEqual(result, expectedResult) - } - - // MARK: - outputs with minutes - - /// should show minutes and seconds (if seconds is > 0) - - func testsFormatTimePositiveMinutes() { - /// init variables for testing - var seconds: Int - var result: String - var expectedResult: String - - /// test 1 - seconds = 60 - result = formatTime(seconds: seconds) - expectedResult = "1m" - - XCTAssertEqual(result, expectedResult) - - /// test 2 - seconds = 61 - result = formatTime(seconds: seconds) - expectedResult = "1m 1s" - - XCTAssertEqual(result, expectedResult) - - /// test 3 - seconds = 119 - result = formatTime(seconds: seconds) - expectedResult = "1m 59s" - - XCTAssertEqual(result, expectedResult) - - /// test 4 - seconds = 3540 - result = formatTime(seconds: seconds) - expectedResult = "59m" - - XCTAssertEqual(result, expectedResult) - - /// test 5 - seconds = 3599 - result = formatTime(seconds: seconds) - expectedResult = "59m 59s" - - XCTAssertEqual(result, expectedResult) - } - - func testsFormatTimeNegativeMinutes() { - /// init variables for testing - var seconds: Int - var result: String - var expectedResult: String - - /// test 1 - seconds = -60 - result = formatTime(seconds: seconds) - expectedResult = "-1m" - - XCTAssertEqual(result, expectedResult) - - /// test 2 - seconds = -61 - result = formatTime(seconds: seconds) - expectedResult = "-1m 1s" - - XCTAssertEqual(result, expectedResult) - - /// test 3 - seconds = -119 - result = formatTime(seconds: seconds) - expectedResult = "-1m 59s" - - XCTAssertEqual(result, expectedResult) - - /// test 4 - seconds = -3540 - result = formatTime(seconds: seconds) - expectedResult = "-59m" - - XCTAssertEqual(result, expectedResult) - - /// test 5 - seconds = -3599 - result = formatTime(seconds: seconds) - expectedResult = "-59m 59s" - - XCTAssertEqual(result, expectedResult) - } - - // MARK: - outputs with hours - - /// shouldn't return seconds if value > 1hr - - func testsFormatTimePositiveHours() { - /// init variables for testing - var seconds: Int - var result: String - var expectedResult: String - - /// test 1 - seconds = 3600 - result = formatTime(seconds: seconds) - expectedResult = "1h" - - XCTAssertEqual(result, expectedResult) - - /// test 2 - seconds = 3601 - result = formatTime(seconds: seconds) - expectedResult = "1h" - - XCTAssertEqual(result, expectedResult) - - /// test 3 - seconds = 3659 - result = formatTime(seconds: seconds) - expectedResult = "1h" - - XCTAssertEqual(result, expectedResult) - - /// test 4 - seconds = 3660 - result = formatTime(seconds: seconds) - expectedResult = "1h 1m" - - XCTAssertEqual(result, expectedResult) - - /// test 5 - seconds = 3661 - result = formatTime(seconds: seconds) - expectedResult = "1h 1m" - - XCTAssertEqual(result, expectedResult) - - /// test 6 - seconds = 7199 - result = formatTime(seconds: seconds) - expectedResult = "1h 59m" - - XCTAssertEqual(result, expectedResult) - - /// test 7 - seconds = 7200 - result = formatTime(seconds: seconds) - expectedResult = "2h" - - /// test 8 - seconds = 7201 - result = formatTime(seconds: seconds) - expectedResult = "2h" - - XCTAssertEqual(result, expectedResult) - - /// test 10 - seconds = 7260 - result = formatTime(seconds: seconds) - expectedResult = "2h 1m" - - XCTAssertEqual(result, expectedResult) - - /// test 11 - seconds = 43200 - result = formatTime(seconds: seconds) - expectedResult = "12h" - - XCTAssertEqual(result, expectedResult) - - /// test 11 - seconds = 43201 - result = formatTime(seconds: seconds) - expectedResult = ">12h" - - XCTAssertEqual(result, expectedResult) - - /// test 13 - seconds = Int.max - result = formatTime(seconds: seconds) - expectedResult = ">12h" - - XCTAssertEqual(result, expectedResult) - } - - func testsFormatTimeNegativeHours() { - /// init variables for testing - var seconds: Int - var result: String - var expectedResult: String - - /// test 1 - seconds = -3600 - result = formatTime(seconds: seconds) - expectedResult = "-1h" - - XCTAssertEqual(result, expectedResult) - - /// test 2 - seconds = -3601 - result = formatTime(seconds: seconds) - expectedResult = "-1h" - - XCTAssertEqual(result, expectedResult) - - /// test 3 - seconds = -3659 - result = formatTime(seconds: seconds) - expectedResult = "-1h" - - XCTAssertEqual(result, expectedResult) - - /// test 4 - seconds = -3660 - result = formatTime(seconds: seconds) - expectedResult = "-1h 1m" - - XCTAssertEqual(result, expectedResult) - - /// test 5 - seconds = -3661 - result = formatTime(seconds: seconds) - expectedResult = "-1h 1m" - - XCTAssertEqual(result, expectedResult) - - /// test 6 - seconds = -7199 - result = formatTime(seconds: seconds) - expectedResult = "-1h 59m" - - XCTAssertEqual(result, expectedResult) - - /// test 7 - seconds = -7200 - result = formatTime(seconds: seconds) - expectedResult = "-2h" - - /// test 8 - seconds = -7201 - result = formatTime(seconds: seconds) - expectedResult = "-2h" - - XCTAssertEqual(result, expectedResult) - - /// test 10 - seconds = -7260 - result = formatTime(seconds: seconds) - expectedResult = "-2h 1m" - - XCTAssertEqual(result, expectedResult) - - /// test 11 - seconds = -43200 - result = formatTime(seconds: seconds) - expectedResult = "-12h" - - XCTAssertEqual(result, expectedResult) - - /// test 11 - seconds = -43201 - result = formatTime(seconds: seconds) - expectedResult = "- >12h" - - XCTAssertEqual(result, expectedResult) - - /// test 13 - seconds = Int.min - result = formatTime(seconds: seconds) - expectedResult = "- >12h" - - XCTAssertEqual(result, expectedResult) - } -} diff --git a/app/metro-now.xcodeproj/project.pbxproj b/app/metro-now.xcodeproj/project.pbxproj index bbecb096..4eeb0151 100644 --- a/app/metro-now.xcodeproj/project.pbxproj +++ b/app/metro-now.xcodeproj/project.pbxproj @@ -9,8 +9,6 @@ /* Begin PBXBuildFile section */ 2D1B2C3B2BFAD6CC007ED5EB /* LocationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1B2C3A2BFAD6CC007ED5EB /* LocationModel.swift */; }; 2D1B2C3C2BFAD6CC007ED5EB /* LocationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1B2C3A2BFAD6CC007ED5EB /* LocationModel.swift */; }; - 2D1B2C3F2BFAD70F007ED5EB /* timeUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1B2C3E2BFAD70F007ED5EB /* timeUtils.swift */; }; - 2D1B2C402BFAD70F007ED5EB /* timeUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1B2C3E2BFAD70F007ED5EB /* timeUtils.swift */; }; 2D1B2C422BFAD72C007ED5EB /* jsonUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1B2C412BFAD72C007ED5EB /* jsonUtils.swift */; }; 2D1B2C432BFAD72C007ED5EB /* jsonUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1B2C412BFAD72C007ED5EB /* jsonUtils.swift */; }; 2D1B2C452BFAD7DB007ED5EB /* mapUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1B2C442BFAD7DB007ED5EB /* mapUtils.swift */; }; @@ -40,11 +38,9 @@ 2DC639E02BF3CCBC00A72C7F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2DC639DF2BF3CCBC00A72C7F /* Assets.xcassets */; }; 2DC639E32BF3CCBC00A72C7F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2DC639E22BF3CCBC00A72C7F /* Preview Assets.xcassets */; }; 2DC639ED2BF3CFCF00A72C7F /* PlatformListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC639EC2BF3CFCF00A72C7F /* PlatformListItem.swift */; }; - 2DC63A002BF4B1E300A72C7F /* PlatformDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC639FF2BF4B1E300A72C7F /* PlatformDetailView.swift */; }; 2DC63A022BF4B20E00A72C7F /* PlatformDetailDepartureListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC63A012BF4B20E00A72C7F /* PlatformDetailDepartureListView.swift */; }; 2DC63A042BF4C1E200A72C7F /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC63A032BF4C1E200A72C7F /* MainTabView.swift */; }; 2DC63A082BF4C25B00A72C7F /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC63A072BF4C25B00A72C7F /* MapView.swift */; }; - 2DC63A222BF50EDD00A72C7F /* timeUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC63A212BF50EDD00A72C7F /* timeUtilsTests.swift */; }; 2DC63A242BF5266700A72C7F /* metroUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC63A232BF5266700A72C7F /* metroUtilsTests.swift */; }; 2DC63A262BF5280F00A72C7F /* jsonUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC63A252BF5280F00A72C7F /* jsonUtilsTests.swift */; }; 2DD3DEA92C07D319002233DE /* metro-stations.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 2DD3DEA72C07D319002233DE /* metro-stations.geojson */; }; @@ -129,7 +125,6 @@ /* Begin PBXFileReference section */ 2D1B2C3A2BFAD6CC007ED5EB /* LocationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationModel.swift; sourceTree = ""; }; - 2D1B2C3E2BFAD70F007ED5EB /* timeUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = timeUtils.swift; sourceTree = ""; }; 2D1B2C412BFAD72C007ED5EB /* jsonUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = jsonUtils.swift; sourceTree = ""; }; 2D1B2C442BFAD7DB007ED5EB /* mapUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = mapUtils.swift; sourceTree = ""; }; 2D1B2C472BFAD7F2007ED5EB /* metroUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = metroUtils.swift; sourceTree = ""; }; @@ -154,12 +149,10 @@ 2DC639DF2BF3CCBC00A72C7F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 2DC639E22BF3CCBC00A72C7F /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 2DC639EC2BF3CFCF00A72C7F /* PlatformListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformListItem.swift; sourceTree = ""; }; - 2DC639FF2BF4B1E300A72C7F /* PlatformDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformDetailView.swift; sourceTree = ""; }; 2DC63A012BF4B20E00A72C7F /* PlatformDetailDepartureListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformDetailDepartureListView.swift; sourceTree = ""; }; 2DC63A032BF4C1E200A72C7F /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = ""; }; 2DC63A072BF4C25B00A72C7F /* MapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = ""; }; 2DC63A182BF50E8F00A72C7F /* metro-now-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "metro-now-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 2DC63A212BF50EDD00A72C7F /* timeUtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = timeUtilsTests.swift; sourceTree = ""; }; 2DC63A232BF5266700A72C7F /* metroUtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = metroUtilsTests.swift; sourceTree = ""; }; 2DC63A252BF5280F00A72C7F /* jsonUtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = jsonUtilsTests.swift; sourceTree = ""; }; 2DD3DEA72C07D319002233DE /* metro-stations.geojson */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "metro-stations.geojson"; path = "../../../data/metro-stations.geojson"; sourceTree = ""; }; @@ -240,7 +233,6 @@ 2D1B2C3D2BFAD6F6007ED5EB /* Utils */ = { isa = PBXGroup; children = ( - 2D1B2C3E2BFAD70F007ED5EB /* timeUtils.swift */, 2D4D8F842C0010A5006F9080 /* networkUtils.swift */, 2D1B2C412BFAD72C007ED5EB /* jsonUtils.swift */, 2D1B2C442BFAD7DB007ED5EB /* mapUtils.swift */, @@ -363,7 +355,6 @@ 2DC639FE2BF4B1C600A72C7F /* PlatformDetail */ = { isa = PBXGroup; children = ( - 2DC639FF2BF4B1E300A72C7F /* PlatformDetailView.swift */, 2DC63A012BF4B20E00A72C7F /* PlatformDetailDepartureListView.swift */, ); path = PlatformDetail; @@ -391,7 +382,6 @@ 2DC63A192BF50E8F00A72C7F /* metro-now-tests */ = { isa = PBXGroup; children = ( - 2DC63A212BF50EDD00A72C7F /* timeUtilsTests.swift */, 2DC63A232BF5266700A72C7F /* metroUtilsTests.swift */, 2DC63A252BF5280F00A72C7F /* jsonUtilsTests.swift */, ); @@ -645,7 +635,6 @@ 2D1B2C502BFAD8ED007ED5EB /* metroRoutesTypes.swift in Sources */, 2D1B2C462BFAD7DB007ED5EB /* mapUtils.swift in Sources */, 2D1B2C3C2BFAD6CC007ED5EB /* LocationModel.swift in Sources */, - 2D1B2C402BFAD70F007ED5EB /* timeUtils.swift in Sources */, 2D4486862BFAA10A005C59CE /* metro_now_watchApp.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -663,13 +652,11 @@ 2D1B2C3B2BFAD6CC007ED5EB /* LocationModel.swift in Sources */, 2D350E672BFBE50600F68039 /* MapStationAnnotationView.swift in Sources */, 2D1B2C422BFAD72C007ED5EB /* jsonUtils.swift in Sources */, - 2D1B2C3F2BFAD70F007ED5EB /* timeUtils.swift in Sources */, 2DC639DC2BF3CCBA00A72C7F /* metro_nowApp.swift in Sources */, 2D6585102C253944002D7F45 /* DetailedMapView.swift in Sources */, 2D4D8F852C0010A5006F9080 /* networkUtils.swift in Sources */, 2DF66D982BFD39B000B31FA2 /* test-coordinates.swift in Sources */, 2D65850E2C251761002D7F45 /* StationDetailView.swift in Sources */, - 2DC63A002BF4B1E300A72C7F /* PlatformDetailView.swift in Sources */, 2DC63A022BF4B20E00A72C7F /* PlatformDetailDepartureListView.swift in Sources */, 2DF48A712C02514E002F754E /* departuresResponseTypes.swift in Sources */, 2D1B2C4F2BFAD8ED007ED5EB /* metroRoutesTypes.swift in Sources */, @@ -685,7 +672,6 @@ files = ( 2DC63A242BF5266700A72C7F /* metroUtilsTests.swift in Sources */, 2DC63A262BF5280F00A72C7F /* jsonUtilsTests.swift in Sources */, - 2DC63A222BF50EDD00A72C7F /* timeUtilsTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -935,7 +921,7 @@ INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 17.0; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -969,7 +955,7 @@ INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 17.0; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/app/metro-now/Core/PlatformDetail/PlatformDetailView.swift b/app/metro-now/Core/PlatformDetail/PlatformDetailView.swift deleted file mode 100644 index 7c98be97..00000000 --- a/app/metro-now/Core/PlatformDetail/PlatformDetailView.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// PlatformDetailView.swift -// metro-now -// -// Created by Kryštof Krátký on 15.05.2024. -// - -import SwiftUI - -struct PlatformDetailView: View { - let defaultDirection: String - @State var gtfsID: String - @State private var departures: [ApiDeparture]? - - var body: some View { - ZStack { - Color.black.edgesIgnoringSafeArea(.all) - - LinearGradient( - colors: [Color.green.opacity(0.50), Color.black], - startPoint: .top, - endPoint: .bottom - ) - .edgesIgnoringSafeArea(.all) - VStack { - Label(departures?.first?.heading ?? defaultDirection, systemImage: "arrowshape.right.fill") - .font(.largeTitle) - .fontWeight(.bold) - .foregroundStyle(.white) - if let departures, departures.count >= 1 { - Text(formatTime(seconds: secondsFromNow(departures[0].departure))) - .font(.largeTitle) - .foregroundStyle(.white) - } - if let departures, departures.count >= 2 { - Text("Also in \(formatTime(seconds: secondsFromNow(departures[1].departure)))") - .font(.title2) - .foregroundStyle(.white) - } - Spacer() - } - .padding(.top, 50) - .task { - do { - departures = try await (getDepartures(gtfsIDs: [gtfsID], groupBy: .platform))[gtfsID] - - } catch { - print(error) - } - } - .refreshable { - do { - departures = try await (getDepartures(gtfsIDs: [gtfsID], groupBy: .platform))[gtfsID] - - } catch { - print(error) - } - } - } - } -} - -// #Preview { -// PlatformDetailView( -// direction: "Háje") -// } diff --git a/app/metro-now/Core/PlatformList/PlatformListItem.swift b/app/metro-now/Core/PlatformList/PlatformListItem.swift index 408f4e03..6b5be9a5 100644 --- a/app/metro-now/Core/PlatformList/PlatformListItem.swift +++ b/app/metro-now/Core/PlatformList/PlatformListItem.swift @@ -13,13 +13,7 @@ struct PlatformListItemView: View { /// this view doesn't handle logic of deciding which departures are outdated (shouldn't be shown) @State var departureDates: [Date] - /// time until departure in human readable form - @State private var departureStrings: [String] = [] - @State var metroLine: String - private let timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect() - - @State private var deltatime: Double = 0 var body: some View { HStack { @@ -34,15 +28,23 @@ struct PlatformListItemView: View { Spacer() VStack { - if departureStrings.count >= 1 { - Text(departureStrings[0]) + if departureDates.count >= 1 { + Text( + .currentDate, format: .reference(to: departureDates[0], allowedFields: [.second, .minute, .hour]) + ) + .fontWeight(.bold) + .foregroundStyle(.white) + .foregroundStyle(.white) + } + if departureDates.count >= 2 { + Text( + "Also in " + ).font(.caption2) .fontWeight(.bold) .foregroundStyle(.white) - .foregroundStyle(.white) - } - if departureStrings.count >= 2 { + .opacity(0.9) Text( - "Also in \(departureStrings[1])" + .currentDate, format: .reference(to: departureDates[1], allowedFields: [.second, .minute, .hour]) ) .font(.caption2) .fontWeight(.bold) @@ -51,20 +53,6 @@ struct PlatformListItemView: View { } } } - .onReceive(timer) { - _ in - guard departureDates.count > 0 else { return } - - if departureDates.count >= 2 { - departureStrings = departureDates[0 ..< 2].map { - formatTime(seconds: secondsFromNow($0)) - } - } else { - departureStrings = [ - formatTime(seconds: secondsFromNow(departureDates[0])), - ] - } - } .padding(.horizontal, 20) .padding(.vertical, 10) .background( From df8beb4a3bfc9b6d9518c32fb2050f3d70d725b2 Mon Sep 17 00:00:00 2001 From: Krystof Date: Fri, 5 Jul 2024 02:43:53 +0200 Subject: [PATCH 2/6] refactor(ios): remove unused code --- app/metro-now.xcodeproj/project.pbxproj | 20 -- app/metro-now/Core/Map/MapView.swift | 2 +- .../Core/Map/StationDetailView.swift | 114 +++++++---- .../PlatformDetailDepartureListView.swift | 18 -- .../Core/PlatformList/PlatformListView.swift | 184 ------------------ .../PlatformList/PlatformListViewModel.swift | 38 ---- app/metro-now/Core/TabBar/MainTabView.swift | 6 +- 7 files changed, 79 insertions(+), 303 deletions(-) delete mode 100644 app/metro-now/Core/PlatformDetail/PlatformDetailDepartureListView.swift delete mode 100644 app/metro-now/Core/PlatformList/PlatformListView.swift delete mode 100644 app/metro-now/Core/PlatformList/PlatformListViewModel.swift diff --git a/app/metro-now.xcodeproj/project.pbxproj b/app/metro-now.xcodeproj/project.pbxproj index 4eeb0151..039041f6 100644 --- a/app/metro-now.xcodeproj/project.pbxproj +++ b/app/metro-now.xcodeproj/project.pbxproj @@ -31,14 +31,11 @@ 2D4D8F852C0010A5006F9080 /* networkUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D4D8F842C0010A5006F9080 /* networkUtils.swift */; }; 2D65850E2C251761002D7F45 /* StationDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D65850D2C251761002D7F45 /* StationDetailView.swift */; }; 2D6585102C253944002D7F45 /* DetailedMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D65850F2C253944002D7F45 /* DetailedMapView.swift */; }; - 2D84CCA12BF8BD7500D2382B /* PlatformListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D84CCA02BF8BD7500D2382B /* PlatformListViewModel.swift */; }; 2D9213E92C081D99004A37C2 /* WidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D9213E82C081D99004A37C2 /* WidgetView.swift */; }; 2DC639DC2BF3CCBA00A72C7F /* metro_nowApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC639DB2BF3CCBA00A72C7F /* metro_nowApp.swift */; }; - 2DC639DE2BF3CCBA00A72C7F /* PlatformListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC639DD2BF3CCBA00A72C7F /* PlatformListView.swift */; }; 2DC639E02BF3CCBC00A72C7F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2DC639DF2BF3CCBC00A72C7F /* Assets.xcassets */; }; 2DC639E32BF3CCBC00A72C7F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2DC639E22BF3CCBC00A72C7F /* Preview Assets.xcassets */; }; 2DC639ED2BF3CFCF00A72C7F /* PlatformListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC639EC2BF3CFCF00A72C7F /* PlatformListItem.swift */; }; - 2DC63A022BF4B20E00A72C7F /* PlatformDetailDepartureListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC63A012BF4B20E00A72C7F /* PlatformDetailDepartureListView.swift */; }; 2DC63A042BF4C1E200A72C7F /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC63A032BF4C1E200A72C7F /* MainTabView.swift */; }; 2DC63A082BF4C25B00A72C7F /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC63A072BF4C25B00A72C7F /* MapView.swift */; }; 2DC63A242BF5266700A72C7F /* metroUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC63A232BF5266700A72C7F /* metroUtilsTests.swift */; }; @@ -141,15 +138,12 @@ 2D4D8F842C0010A5006F9080 /* networkUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = networkUtils.swift; sourceTree = ""; }; 2D65850D2C251761002D7F45 /* StationDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StationDetailView.swift; sourceTree = ""; }; 2D65850F2C253944002D7F45 /* DetailedMapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailedMapView.swift; sourceTree = ""; }; - 2D84CCA02BF8BD7500D2382B /* PlatformListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformListViewModel.swift; sourceTree = ""; }; 2D9213E82C081D99004A37C2 /* WidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetView.swift; sourceTree = ""; }; 2DC639D82BF3CCBA00A72C7F /* metro-now.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "metro-now.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 2DC639DB2BF3CCBA00A72C7F /* metro_nowApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = metro_nowApp.swift; sourceTree = ""; }; - 2DC639DD2BF3CCBA00A72C7F /* PlatformListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformListView.swift; sourceTree = ""; }; 2DC639DF2BF3CCBC00A72C7F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 2DC639E22BF3CCBC00A72C7F /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 2DC639EC2BF3CFCF00A72C7F /* PlatformListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformListItem.swift; sourceTree = ""; }; - 2DC63A012BF4B20E00A72C7F /* PlatformDetailDepartureListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformDetailDepartureListView.swift; sourceTree = ""; }; 2DC63A032BF4C1E200A72C7F /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = ""; }; 2DC63A072BF4C25B00A72C7F /* MapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = ""; }; 2DC63A182BF50E8F00A72C7F /* metro-now-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "metro-now-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -335,7 +329,6 @@ isa = PBXGroup; children = ( 2DC63A062BF4C24C00A72C7F /* Map */, - 2DC639FE2BF4B1C600A72C7F /* PlatformDetail */, 2DC639EA2BF3CD4700A72C7F /* PlatformList */, 2DC63A052BF4C1E800A72C7F /* TabBar */, ); @@ -345,21 +338,11 @@ 2DC639EA2BF3CD4700A72C7F /* PlatformList */ = { isa = PBXGroup; children = ( - 2DC639DD2BF3CCBA00A72C7F /* PlatformListView.swift */, - 2D84CCA02BF8BD7500D2382B /* PlatformListViewModel.swift */, 2DC639EC2BF3CFCF00A72C7F /* PlatformListItem.swift */, ); path = PlatformList; sourceTree = ""; }; - 2DC639FE2BF4B1C600A72C7F /* PlatformDetail */ = { - isa = PBXGroup; - children = ( - 2DC63A012BF4B20E00A72C7F /* PlatformDetailDepartureListView.swift */, - ); - path = PlatformDetail; - sourceTree = ""; - }; 2DC63A052BF4C1E800A72C7F /* TabBar */ = { isa = PBXGroup; children = ( @@ -644,7 +627,6 @@ buildActionMask = 2147483647; files = ( 2D1B2C522BFAD90B007ED5EB /* metroStationsTypes.swift in Sources */, - 2DC639DE2BF3CCBA00A72C7F /* PlatformListView.swift in Sources */, 2DC63A082BF4C25B00A72C7F /* MapView.swift in Sources */, 2DC63A042BF4C1E200A72C7F /* MainTabView.swift in Sources */, 2D1B2C4B2BFAD807007ED5EB /* fileUtils.swift in Sources */, @@ -657,10 +639,8 @@ 2D4D8F852C0010A5006F9080 /* networkUtils.swift in Sources */, 2DF66D982BFD39B000B31FA2 /* test-coordinates.swift in Sources */, 2D65850E2C251761002D7F45 /* StationDetailView.swift in Sources */, - 2DC63A022BF4B20E00A72C7F /* PlatformDetailDepartureListView.swift in Sources */, 2DF48A712C02514E002F754E /* departuresResponseTypes.swift in Sources */, 2D1B2C4F2BFAD8ED007ED5EB /* metroRoutesTypes.swift in Sources */, - 2D84CCA12BF8BD7500D2382B /* PlatformListViewModel.swift in Sources */, 2D1B2C452BFAD7DB007ED5EB /* mapUtils.swift in Sources */, 2DC639ED2BF3CFCF00A72C7F /* PlatformListItem.swift in Sources */, ); diff --git a/app/metro-now/Core/Map/MapView.swift b/app/metro-now/Core/Map/MapView.swift index f4caf921..2c107494 100644 --- a/app/metro-now/Core/Map/MapView.swift +++ b/app/metro-now/Core/Map/MapView.swift @@ -21,7 +21,7 @@ struct MapView: View { ForEach(metroStationAnnotations, id: \.name) { station in Annotation(station.name, coordinate: station.coordinate) { - NavigationLink(destination: StationDetailView(stationName: station.name)) { + NavigationLink(destination: StationDetailView(stationName: station.name, showMap: true, showDirection: true)) { MapMetroStationView(metroLines: station.metroLines) } } diff --git a/app/metro-now/Core/Map/StationDetailView.swift b/app/metro-now/Core/Map/StationDetailView.swift index f51f86e3..b58ab581 100644 --- a/app/metro-now/Core/Map/StationDetailView.swift +++ b/app/metro-now/Core/Map/StationDetailView.swift @@ -2,11 +2,13 @@ import MapKit import SwiftUI struct StationDetailView: View { + let showMap: Bool let stationName: String let station: MetroStationsGeoJSONFeature let stationCoordinate: CLLocationCoordinate2D let distanceFormatter = MKDistanceFormatter() - let mapUrl: URL + let mapUrl: URL? + @State private var isFavourite: Bool = false @State private var trigger = false @StateObject private var locationModel = LocationModel() @@ -14,8 +16,9 @@ struct StationDetailView: View { @State private var departures: GroupedDepartures = [:] @State private var errorMessage: String? - init(stationName: String) { + init(stationName: String, showMap: Bool = false, showDirection: Bool = false) { self.stationName = stationName + self.showMap = showMap let stations: MetroStationsGeoJSON = getParsedJSONFile(.METRO_STATIONS_FILE)! station = stations.features.first(where: { $0.properties.name == stationName })! @@ -23,55 +26,57 @@ struct StationDetailView: View { latitude: station.geometry.coordinates[1], longitude: station.geometry.coordinates[0] ) - mapUrl = URL( + + mapUrl = showDirection ? URL( string: "maps://?saddr=&daddr=\(stationCoordinate.latitude),\(stationCoordinate.longitude)" - )! + ) : nil } var body: some View { ScrollView { - ZStack { - Text("Map") - .hidden() - .frame(height: 150) - .frame(maxWidth: .infinity) - } - - .background(alignment: .bottom) { - TimelineView(.animation) { context in - let seconds = context.date.timeIntervalSince1970 - - DetailedMapView( - location: CLLocation( - latitude: stationCoordinate.latitude, - longitude: stationCoordinate.longitude - ), - distance: 500, - pitch: 30, - heading: seconds * 6 - ) - .mask { - LinearGradient( - stops: [ - .init(color: .clear, location: 0), - .init(color: .black.opacity(0.15), location: 0.1), - .init(color: .black, location: 0.6), - .init(color: .black, location: 1), - ], - startPoint: .top, - endPoint: .bottom + if showMap { + ZStack { + Text("Map") + .hidden() + .frame(height: 150) + .frame(maxWidth: .infinity) + } + .background(alignment: .bottom) { + TimelineView(.animation) { context in + let seconds = context.date.timeIntervalSince1970 + + DetailedMapView( + location: CLLocation( + latitude: stationCoordinate.latitude, + longitude: stationCoordinate.longitude + ), + distance: 500, + pitch: 30, + heading: seconds * 6 ) + .mask { + LinearGradient( + stops: [ + .init(color: .clear, location: 0), + .init(color: .black.opacity(0.15), location: 0.1), + .init(color: .black, location: 0.6), + .init(color: .black, location: 1), + ], + startPoint: .top, + endPoint: .bottom + ) + } + .padding(.top, -150) } - .padding(.top, -150) } - } - .ignoresSafeArea(edges: .top) + .ignoresSafeArea(edges: .top) + } VStack(spacing: 20) { - HStack(spacing: 20) { - if UIApplication.shared.canOpenURL(mapUrl) { + if let mapUrl, UIApplication.shared.canOpenURL(mapUrl) { + HStack(spacing: 20) { Button(action: { UIApplication.shared.open(mapUrl, options: [:], completionHandler: nil) @@ -87,8 +92,9 @@ struct StationDetailView: View { .foregroundColor(.white) .cornerRadius(8) } + + Spacer() } - Spacer() } VStack(spacing: 10) { @@ -164,6 +170,34 @@ struct StationDetailView: View { } } +#Preview("With Map") { + NavigationStack { + StationDetailView( + stationName: "Muzeum", + showMap: true + ) + } +} + +#Preview("With Map and Directions") { + NavigationStack { + StationDetailView( + stationName: "Muzeum", + showMap: true, + showDirection: true + ) + } +} + +#Preview("With Directions") { + NavigationStack { + StationDetailView( + stationName: "Muzeum", + showDirection: true + ) + } +} + #Preview("Muzeum") { NavigationStack { StationDetailView( diff --git a/app/metro-now/Core/PlatformDetail/PlatformDetailDepartureListView.swift b/app/metro-now/Core/PlatformDetail/PlatformDetailDepartureListView.swift deleted file mode 100644 index 2058444f..00000000 --- a/app/metro-now/Core/PlatformDetail/PlatformDetailDepartureListView.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// PlatformDetailDepartureListView.swift -// metro-now -// -// Created by Kryštof Krátký on 15.05.2024. -// - -import SwiftUI - -struct PlatformDetailDepartureListView_: View { - var body: some View { - Text("Departures") - } -} - -#Preview { - PlatformDetailDepartureListView_() -} diff --git a/app/metro-now/Core/PlatformList/PlatformListView.swift b/app/metro-now/Core/PlatformList/PlatformListView.swift deleted file mode 100644 index 05ad3942..00000000 --- a/app/metro-now/Core/PlatformList/PlatformListView.swift +++ /dev/null @@ -1,184 +0,0 @@ -// -// PlatformListView.swift -// metro-now -// - -import CoreLocation -import SwiftUI - -struct Departure { - let platform: String - let direction: String - let departure: Date -} - -struct PlatformsListView: View { - var station: MetroStationsGeoJSONFeature? - // @StateObject private var viewModel = PlatformListViewModel() - @State private var departuresByGtfsID: GroupedDepartures? - - var body: some View { - NavigationStack { - ScrollView { - VStack(spacing: 10) { - if let station, departuresByGtfsID != nil { - ForEach(station.properties.platforms, id: \.gtfsId) { platform in - PlatformListItemView( - direction: platform.direction, - departureDates: [], - metroLine: platform.name - ) - // if departuresByGtfsID?.contains(k -> k == platform.gtfsId) { - // PlatformListItemView( - // direction: "platform.direction", - // departureDates: Date.now() , // departuresByGtfsID[platform.gtfsId].map(\.departureTimestamp.predicted) as! [Date], - // metroLine: "A" // platform.name - // ) - // } - // else { - // Text("No departures for this platform") - // .foregroundColor(.red) - // .font(.headline) - // } - // let platformDepartures = departuresByGtfsID?[platform.gtfsId] - // let platformDepartures = departuresByGtfsID?[platform.gtfsId] - // let direction = platformDepartures.first?.heading ?? platform.direction - - // NavigationLink { - // PlatformDetailView( - // defaultDirection: platform.direction, - // gtfsID: platform.gtfsId - // ) - // } - // label: { - // PlatformListItemView( - // direction: platform.direction, - // departureDates: platformDepartures.map(\.departureTimestamp.predicted) as! [Date], - // metroLine: "A" // platform.name - // ) - // } - } - } - // if let station, departuresByGtfsID != nil { - // ForEach(station.properties.platforms, id: \.gtfsId) { platform in - // let platformDepartures = departuresByGtfsID?[platform.gtfsId] ?? [] - // - // let direction = platformDepartures.first?.heading ?? platform.direction - // - // NavigationLink { - // PlatformDetailView( - // defaultDirection: direction, - // gtfsID: platform.gtfsId - // ) - // } - // label: { - // PlatformListItemView( - // direction: direction, - // departureDates: platformDepartures.map(\.departureTimestamp.predicted) as! [Date], - // metroLine: "A" // platform.name - // ) - // } - // } - // - // } - } - .padding(10) - } - - .navigationTitle(station?.properties.name ?? "") - .task { - guard let station else { - return - } - - do { - let gtfsIDs = station.properties.platforms.map(\.gtfsId) - departuresByGtfsID = try await (getDepartures(gtfsIDs: gtfsIDs, groupBy: .platform)) - } catch { - print(error) - } - } - .refreshable { - guard let station else { - return - } - - do { - let gtfsIDs = station.properties.platforms.map(\.gtfsId) - departuresByGtfsID = try await (getDepartures(gtfsIDs: gtfsIDs, groupBy: .platform)) - } catch { - print(error) - } - } - // .onAppear { - // viewModel.$departuresByGtfsID - // // guard let station else { - //// return - //// } - //// - //// do { - //// try await viewModel.getData(gtfsIDs: station.properties.platforms.map(\.gtfsId)) - //// } catch {} - // } - // .refreshable { - // guard let station else { - // return - // } - // - // do { - // try await viewModel.getData(gtfsIDs: station.properties.platforms.map(\.gtfsId)) - // } catch { - // print(error) - // }// - // } - } - } -} - -#Preview("Muzeum") { - PlatformsListView( - station: getClosestStationFromGeoJSON( - location: MUZEUM_COORDINATES - ) - ) -} - -#Preview("Florenc") { - PlatformsListView( - station: getClosestStationFromGeoJSON( - location: FLORENC_COORDINATES - ) - ) -} - -#Preview("Můstek") { - PlatformsListView( - station: getClosestStationFromGeoJSON( - location: MUSTEK_COORDINATES - ) - ) -} - -#Preview("Dejvická") { - PlatformsListView( - station: getClosestStationFromGeoJSON( - location: DEJVICKA_COORDINATES - ) - ) -} - -#Preview("Hlavní nádraží") { - PlatformsListView( - station: getClosestStationFromGeoJSON( - location: HLAVNI_NADRAZI_COORDINATES - ) - ) -} - -#Preview("Černý Most") { - PlatformsListView( - station: getClosestStationFromGeoJSON( - location: CERNY_MOST_COORDINATES - ) - ) -} diff --git a/app/metro-now/Core/PlatformList/PlatformListViewModel.swift b/app/metro-now/Core/PlatformList/PlatformListViewModel.swift deleted file mode 100644 index 61ec7380..00000000 --- a/app/metro-now/Core/PlatformList/PlatformListViewModel.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// Author: Kryštof Krátký -// - -import Foundation -import SwiftUI - -final class PlatformListViewModel: ObservableObject { - @Published var departuresByGtfsID: [String: [ApiDeparture]] = [:] - - func getData(gtfsIDs: [String]) async throws { - let params = (gtfsIDs.map { "gtfsID=\($0)" }).joined(separator: "&") - let endpoint = "\(METRO_NOW_API)/v1/metro/departures?\(params)" - - guard let url = URL(string: endpoint) else { throw FetchError.InvalidURL } - - let (data, response) = try await URLSession.shared.data(from: url) - - guard let respones = response as? HTTPURLResponse else { - throw FetchError.InvalidResponse - } - - do { - let decoder = JSONDecoder() - decoder.keyDecodingStrategy = .convertFromSnakeCase - decoder.dateDecodingStrategy = .iso8601 - let decoded = try decoder.decode([String: [ApiDeparture]].self, from: data) - DispatchQueue.main.async { - self.departuresByGtfsID = decoded - } - } - - catch { - print(error) - throw FetchError.InvalidaData - } - } -} diff --git a/app/metro-now/Core/TabBar/MainTabView.swift b/app/metro-now/Core/TabBar/MainTabView.swift index a8ed27f7..04ba61c7 100644 --- a/app/metro-now/Core/TabBar/MainTabView.swift +++ b/app/metro-now/Core/TabBar/MainTabView.swift @@ -15,8 +15,10 @@ struct MainTabView: View { var body: some View { TabView { - if let closestStation { PlatformsListView( - station: closestStation) + if let closestStation { + NavigationStack { + StationDetailView(stationName: closestStation.properties.name) + } .tabItem { Label("Near me", systemImage: "tram.fill.tunnel") } From 18fbbf213245a90b4612d02f2b27253ce3234286 Mon Sep 17 00:00:00 2001 From: Krystof Date: Fri, 5 Jul 2024 15:46:48 +0200 Subject: [PATCH 3/6] refactor(ios): smaller components --- .../MetroStationAnnotation.swift} | 36 ++++++---- .../Components/MetroDeparture/Card.swift | 57 +++++++++++++++ .../MetroDeparture/FirstDeparture.swift | 18 +++++ .../Components/MetroDeparture/Label.swift | 21 ++++++ .../MetroDeparture/MetroDeparture.swift | 29 ++++++++ .../MetroDeparture/SecondDeparture.swift | 22 ++++++ app/metro-now.xcodeproj/project.pbxproj | 48 ++++++++----- .../Core/Map/StationDetailView.swift | 2 +- .../Core/PlatformList/PlatformListItem.swift | 71 ------------------- app/metro-now/Core/TabBar/MainTabView.swift | 1 - 10 files changed, 200 insertions(+), 105 deletions(-) rename app/{metro-now/Core/Map/MapStationAnnotationView.swift => Common/Components/MapAnnotation/MetroAnnotation/MetroStationAnnotation.swift} (71%) create mode 100644 app/Common/Components/MetroDeparture/Card.swift create mode 100644 app/Common/Components/MetroDeparture/FirstDeparture.swift create mode 100644 app/Common/Components/MetroDeparture/Label.swift create mode 100644 app/Common/Components/MetroDeparture/MetroDeparture.swift create mode 100644 app/Common/Components/MetroDeparture/SecondDeparture.swift delete mode 100644 app/metro-now/Core/PlatformList/PlatformListItem.swift diff --git a/app/metro-now/Core/Map/MapStationAnnotationView.swift b/app/Common/Components/MapAnnotation/MetroAnnotation/MetroStationAnnotation.swift similarity index 71% rename from app/metro-now/Core/Map/MapStationAnnotationView.swift rename to app/Common/Components/MapAnnotation/MetroAnnotation/MetroStationAnnotation.swift index ac76d628..2c2cd244 100644 --- a/app/metro-now/Core/Map/MapStationAnnotationView.swift +++ b/app/Common/Components/MapAnnotation/MetroAnnotation/MetroStationAnnotation.swift @@ -7,6 +7,26 @@ import MapKit import SwiftUI +struct MetroStationIcon: View { + let metroLine: String + + var body: some View { + Image( + systemName: + getMetroLineIcon(metroLine) + ) + .imageScale(.medium) + .padding(5) + .foregroundStyle(.white) + .background(getMetroLineColor(metroLine)) + .clipShape(.rect(cornerRadius: 6)) + .overlay( + RoundedRectangle(cornerRadius: 6) + .stroke(.white, lineWidth: 2) + ) + } +} + struct MapMetroStationView: View { let metroLines: [String] @@ -16,20 +36,8 @@ struct MapMetroStationView: View { index, metroLine in let offset: CGFloat = index == 0 ? 0 : (-16 * CGFloat(index)) - Image( - systemName: - getMetroLineIcon(metroLine) - ) - .imageScale(.medium) - .padding(5) - .foregroundStyle(.white) - .background(getMetroLineColor(metroLine)) - .clipShape(.rect(cornerRadius: 6)) - .overlay( - RoundedRectangle(cornerRadius: 6) - .stroke(.white, lineWidth: 2) - ) - .offset(x: offset, y: offset) + MetroStationIcon(metroLine: metroLine) + .offset(x: offset, y: offset) } } } diff --git a/app/Common/Components/MetroDeparture/Card.swift b/app/Common/Components/MetroDeparture/Card.swift new file mode 100644 index 00000000..cf156cdd --- /dev/null +++ b/app/Common/Components/MetroDeparture/Card.swift @@ -0,0 +1,57 @@ + +import SwiftUI + +struct MetroDepartureCard: View { + let backgroundColor: Color + let content: Content + + init(backgroundColor: Color, @ViewBuilder content: () -> Content) { + self.backgroundColor = backgroundColor + self.content = content() + } + + var body: some View { + HStack { + content + }.padding(.horizontal, 20) + .padding(.vertical, 10) + .background( + LinearGradient( + colors: [ + backgroundColor, + backgroundColor.opacity(0.8), + ], + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + ) + .clipShape(.rect(cornerRadius: 15)) + } +} + +#Preview { + 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("C")) + { + HStack { + MetroDepartureCardLabel(direction: "Haje", metroLine: "C") + Spacer() + } + } +} diff --git a/app/Common/Components/MetroDeparture/FirstDeparture.swift b/app/Common/Components/MetroDeparture/FirstDeparture.swift new file mode 100644 index 00000000..591e117a --- /dev/null +++ b/app/Common/Components/MetroDeparture/FirstDeparture.swift @@ -0,0 +1,18 @@ + +import SwiftUI + +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) + } +} diff --git a/app/Common/Components/MetroDeparture/Label.swift b/app/Common/Components/MetroDeparture/Label.swift new file mode 100644 index 00000000..3476dab4 --- /dev/null +++ b/app/Common/Components/MetroDeparture/Label.swift @@ -0,0 +1,21 @@ + +import SwiftUI + +struct MetroDepartureCardLabel: View { + let direction: String + let metroLine: String + + var body: some View { + Label( + title: { + Text(direction) + }, + icon: { + Image(systemName: getMetroLineIcon(metroLine)) + } + ) + .fontWeight(.bold) + .font(.headline) + .foregroundStyle(.white) + } +} diff --git a/app/Common/Components/MetroDeparture/MetroDeparture.swift b/app/Common/Components/MetroDeparture/MetroDeparture.swift new file mode 100644 index 00000000..fa7b0b53 --- /dev/null +++ b/app/Common/Components/MetroDeparture/MetroDeparture.swift @@ -0,0 +1,29 @@ + +import SwiftUI + +struct MetroDeparture: View { + @State var direction: String + + /// only first two items from array are shown + /// this view doesn't handle logic of deciding which departures are outdated (shouldn't be shown) + @State var departureDates: [Date] + + @State var metroLine: String + + var body: some View { + MetroDepartureCard(backgroundColor: getMetroLineColor(metroLine)) { + MetroDepartureCardLabel(direction: direction, metroLine: metroLine) + + Spacer() + + VStack { + if departureDates.count >= 1 { + MetroDepartureCardFirstDeparture(departureDate: departureDates[0]) + } + if departureDates.count >= 2 { + MetroDepartureCardSecondDeparture(departureDate: departureDates[1]) + } + } + } + } +} diff --git a/app/Common/Components/MetroDeparture/SecondDeparture.swift b/app/Common/Components/MetroDeparture/SecondDeparture.swift new file mode 100644 index 00000000..e04417a8 --- /dev/null +++ b/app/Common/Components/MetroDeparture/SecondDeparture.swift @@ -0,0 +1,22 @@ + +import SwiftUI + +struct MetroDepartureCardSecondDeparture: View { + let departureDate: Date + + var body: some View { + Text( + "Also in " + ).font(.caption2) + .fontWeight(.bold) + .foregroundStyle(.white) + .opacity(0.9) + Text( + .currentDate, format: .reference(to: departureDate, allowedFields: [.second, .minute, .hour]) + ) + .font(.caption2) + .fontWeight(.bold) + .foregroundStyle(.white) + .opacity(0.9) + } +} diff --git a/app/metro-now.xcodeproj/project.pbxproj b/app/metro-now.xcodeproj/project.pbxproj index 039041f6..1bfce890 100644 --- a/app/metro-now.xcodeproj/project.pbxproj +++ b/app/metro-now.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 56; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ @@ -21,7 +21,6 @@ 2D1B2C502BFAD8ED007ED5EB /* metroRoutesTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1B2C4E2BFAD8ED007ED5EB /* metroRoutesTypes.swift */; }; 2D1B2C522BFAD90B007ED5EB /* metroStationsTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1B2C512BFAD90B007ED5EB /* metroStationsTypes.swift */; }; 2D1B2C532BFAD90B007ED5EB /* metroStationsTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1B2C512BFAD90B007ED5EB /* metroStationsTypes.swift */; }; - 2D350E672BFBE50600F68039 /* MapStationAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D350E662BFBE50600F68039 /* MapStationAnnotationView.swift */; }; 2D373CC22C0825790025BCDF /* ListWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D373CC12C0825790025BCDF /* ListWidgetView.swift */; }; 2D4486862BFAA10A005C59CE /* metro_now_watchApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D4486852BFAA10A005C59CE /* metro_now_watchApp.swift */; }; 2D4486882BFAA10A005C59CE /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D4486872BFAA10A005C59CE /* ContentView.swift */; }; @@ -35,7 +34,6 @@ 2DC639DC2BF3CCBA00A72C7F /* metro_nowApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC639DB2BF3CCBA00A72C7F /* metro_nowApp.swift */; }; 2DC639E02BF3CCBC00A72C7F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2DC639DF2BF3CCBC00A72C7F /* Assets.xcassets */; }; 2DC639E32BF3CCBC00A72C7F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2DC639E22BF3CCBC00A72C7F /* Preview Assets.xcassets */; }; - 2DC639ED2BF3CFCF00A72C7F /* PlatformListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC639EC2BF3CFCF00A72C7F /* PlatformListItem.swift */; }; 2DC63A042BF4C1E200A72C7F /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC63A032BF4C1E200A72C7F /* MainTabView.swift */; }; 2DC63A082BF4C25B00A72C7F /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC63A072BF4C25B00A72C7F /* MapView.swift */; }; 2DC63A242BF5266700A72C7F /* metroUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC63A232BF5266700A72C7F /* metroUtilsTests.swift */; }; @@ -128,7 +126,6 @@ 2D1B2C4A2BFAD807007ED5EB /* fileUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = fileUtils.swift; sourceTree = ""; }; 2D1B2C4E2BFAD8ED007ED5EB /* metroRoutesTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = metroRoutesTypes.swift; sourceTree = ""; }; 2D1B2C512BFAD90B007ED5EB /* metroStationsTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = metroStationsTypes.swift; sourceTree = ""; }; - 2D350E662BFBE50600F68039 /* MapStationAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapStationAnnotationView.swift; sourceTree = ""; }; 2D373CC12C0825790025BCDF /* ListWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListWidgetView.swift; sourceTree = ""; }; 2D4486832BFAA10A005C59CE /* metro-now-watch Watch App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "metro-now-watch Watch App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 2D4486852BFAA10A005C59CE /* metro_now_watchApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = metro_now_watchApp.swift; sourceTree = ""; }; @@ -143,7 +140,6 @@ 2DC639DB2BF3CCBA00A72C7F /* metro_nowApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = metro_nowApp.swift; sourceTree = ""; }; 2DC639DF2BF3CCBC00A72C7F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 2DC639E22BF3CCBC00A72C7F /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - 2DC639EC2BF3CFCF00A72C7F /* PlatformListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformListItem.swift; sourceTree = ""; }; 2DC63A032BF4C1E200A72C7F /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = ""; }; 2DC63A072BF4C25B00A72C7F /* MapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = ""; }; 2DC63A182BF50E8F00A72C7F /* metro-now-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "metro-now-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -170,6 +166,32 @@ 2DF66D972BFD39B000B31FA2 /* test-coordinates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "test-coordinates.swift"; sourceTree = ""; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 2D5BA5E22C382D8D0055F12A /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Card.swift, + FirstDeparture.swift, + Label.swift, + MetroDeparture.swift, + SecondDeparture.swift, + ); + target = 2DC639D72BF3CCBA00A72C7F /* metro-now */; + }; + 2D5BA5EB2C382FEA0055F12A /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + MetroAnnotation/MetroStationAnnotation.swift, + ); + target = 2DC639D72BF3CCBA00A72C7F /* metro-now */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* 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 = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 2D4486802BFAA10A005C59CE /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -259,6 +281,8 @@ 2D1B2C5B2BFADAAA007ED5EB /* Components */ = { isa = PBXGroup; children = ( + 2D5BA5EA2C382FC30055F12A /* MapAnnotation */, + 2D5BA5DE2C382D6A0055F12A /* MetroDeparture */, ); path = Components; sourceTree = ""; @@ -329,20 +353,11 @@ isa = PBXGroup; children = ( 2DC63A062BF4C24C00A72C7F /* Map */, - 2DC639EA2BF3CD4700A72C7F /* PlatformList */, 2DC63A052BF4C1E800A72C7F /* TabBar */, ); path = Core; sourceTree = ""; }; - 2DC639EA2BF3CD4700A72C7F /* PlatformList */ = { - isa = PBXGroup; - children = ( - 2DC639EC2BF3CFCF00A72C7F /* PlatformListItem.swift */, - ); - path = PlatformList; - sourceTree = ""; - }; 2DC63A052BF4C1E800A72C7F /* TabBar */ = { isa = PBXGroup; children = ( @@ -354,10 +369,9 @@ 2DC63A062BF4C24C00A72C7F /* Map */ = { isa = PBXGroup; children = ( - 2D65850D2C251761002D7F45 /* StationDetailView.swift */, 2D65850F2C253944002D7F45 /* DetailedMapView.swift */, 2DC63A072BF4C25B00A72C7F /* MapView.swift */, - 2D350E662BFBE50600F68039 /* MapStationAnnotationView.swift */, + 2D65850D2C251761002D7F45 /* StationDetailView.swift */, ); path = Map; sourceTree = ""; @@ -632,7 +646,6 @@ 2D1B2C4B2BFAD807007ED5EB /* fileUtils.swift in Sources */, 2D1B2C482BFAD7F2007ED5EB /* metroUtils.swift in Sources */, 2D1B2C3B2BFAD6CC007ED5EB /* LocationModel.swift in Sources */, - 2D350E672BFBE50600F68039 /* MapStationAnnotationView.swift in Sources */, 2D1B2C422BFAD72C007ED5EB /* jsonUtils.swift in Sources */, 2DC639DC2BF3CCBA00A72C7F /* metro_nowApp.swift in Sources */, 2D6585102C253944002D7F45 /* DetailedMapView.swift in Sources */, @@ -642,7 +655,6 @@ 2DF48A712C02514E002F754E /* departuresResponseTypes.swift in Sources */, 2D1B2C4F2BFAD8ED007ED5EB /* metroRoutesTypes.swift in Sources */, 2D1B2C452BFAD7DB007ED5EB /* mapUtils.swift in Sources */, - 2DC639ED2BF3CFCF00A72C7F /* PlatformListItem.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/app/metro-now/Core/Map/StationDetailView.swift b/app/metro-now/Core/Map/StationDetailView.swift index b58ab581..c0f9aec4 100644 --- a/app/metro-now/Core/Map/StationDetailView.swift +++ b/app/metro-now/Core/Map/StationDetailView.swift @@ -108,7 +108,7 @@ struct StationDetailView: View { let d = departures[k]! if d.count > 0 { - PlatformListItemView( + MetroDeparture( direction: d[0].heading, departureDates: d.map(\.departure), metroLine: d[0].line diff --git a/app/metro-now/Core/PlatformList/PlatformListItem.swift b/app/metro-now/Core/PlatformList/PlatformListItem.swift deleted file mode 100644 index 6b5be9a5..00000000 --- a/app/metro-now/Core/PlatformList/PlatformListItem.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// metro-now -// -// Created by Kryštof Krátký on 14.05.2024. -// - -import SwiftUI - -struct PlatformListItemView: View { - @State var direction: String - - /// only first two items from array are shown - /// this view doesn't handle logic of deciding which departures are outdated (shouldn't be shown) - @State var departureDates: [Date] - - @State var metroLine: String - - var body: some View { - HStack { - Label( - title: { Text(direction) }, - icon: { Image(systemName: getMetroLineIcon(metroLine)) } - ) - .fontWeight(.bold) - .font(.headline) - .foregroundStyle(.white) - - Spacer() - - VStack { - if departureDates.count >= 1 { - Text( - .currentDate, format: .reference(to: departureDates[0], allowedFields: [.second, .minute, .hour]) - ) - .fontWeight(.bold) - .foregroundStyle(.white) - .foregroundStyle(.white) - } - if departureDates.count >= 2 { - Text( - "Also in " - ).font(.caption2) - .fontWeight(.bold) - .foregroundStyle(.white) - .opacity(0.9) - Text( - .currentDate, format: .reference(to: departureDates[1], allowedFields: [.second, .minute, .hour]) - ) - .font(.caption2) - .fontWeight(.bold) - .foregroundStyle(.white) - .opacity(0.9) - } - } - } - .padding(.horizontal, 20) - .padding(.vertical, 10) - .background( - LinearGradient( - colors: [ - getMetroLineColor(metroLine), - getMetroLineColor(metroLine).opacity(0.8), - ], - startPoint: .topLeading, - endPoint: .bottomTrailing - ) - ) - - .clipShape(.rect(cornerRadius: 15)) - } -} diff --git a/app/metro-now/Core/TabBar/MainTabView.swift b/app/metro-now/Core/TabBar/MainTabView.swift index 04ba61c7..3cbb86b6 100644 --- a/app/metro-now/Core/TabBar/MainTabView.swift +++ b/app/metro-now/Core/TabBar/MainTabView.swift @@ -30,7 +30,6 @@ struct MainTabView: View { } } .onReceive(locationModel.$location) { location in - guard let location else { print("Unknown location") return From df3be951409ae2f20e640c7ba73b7d2217eb25b4 Mon Sep 17 00:00:00 2001 From: Krystof Date: Fri, 5 Jul 2024 15:59:43 +0200 Subject: [PATCH 4/6] refactor(ios): map annotations --- app/Common/Components/.gitkeep | 0 .../MetroAnnotationStack.swift | 61 +++++++++++++++++ .../BusAnnotation/BusAnnotation.swift | 33 ++++++++++ .../MetroStationAnnotation.swift | 66 +------------------ app/metro-now.xcodeproj/project.pbxproj | 2 + app/metro-now/Core/Map/MapView.swift | 22 +++++-- 6 files changed, 113 insertions(+), 71 deletions(-) delete mode 100644 app/Common/Components/.gitkeep create mode 100644 app/Common/Components/MapAnnotation/AnnotationStack/MetroAnnotationStack.swift create mode 100644 app/Common/Components/MapAnnotation/BusAnnotation/BusAnnotation.swift diff --git a/app/Common/Components/.gitkeep b/app/Common/Components/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/app/Common/Components/MapAnnotation/AnnotationStack/MetroAnnotationStack.swift b/app/Common/Components/MapAnnotation/AnnotationStack/MetroAnnotationStack.swift new file mode 100644 index 00000000..91bb59c1 --- /dev/null +++ b/app/Common/Components/MapAnnotation/AnnotationStack/MetroAnnotationStack.swift @@ -0,0 +1,61 @@ + + +import SwiftUI +import MapKit + + +struct MetroAnnotationStack: View { + let metroLines: [String] + + var body: some View { + ZStack { + ForEach(Array(metroLines.enumerated()), id: \.0) { + index, metroLine in + let offset: CGFloat = index == 0 ? 0 : (-16 * CGFloat(index)) + + MetroStationAnnotation(metroLine: metroLine) + .offset(x: offset, y: offset) + } + } + } +} + +#Preview("One station annotation") { + Map { + Annotation( + "Random place on map", coordinate: CLLocationCoordinate2D( + latitude: 50.113680, longitude: 14.449520) + ) { + MetroAnnotationStack( + metroLines: ["A"] + ) + } + } +} + +#Preview("Two stations annotation") { + Map { + Annotation( + "Random place on map", coordinate: CLLocationCoordinate2D( + latitude: 50.113680, longitude: 14.449520) + ) { + MetroAnnotationStack( + metroLines: ["A", "B"] + ) + } + } +} + +// this is not very valid for Prague, but might be useful in the future +#Preview("Multiple stations annotation") { + Map { + Annotation( + "Random place on map", coordinate: CLLocationCoordinate2D( + latitude: 50.113680, longitude: 14.449520) + ) { + MetroAnnotationStack( + metroLines: ["A", "B", "C", "A", "B", "C"] + ) + } + } +} diff --git a/app/Common/Components/MapAnnotation/BusAnnotation/BusAnnotation.swift b/app/Common/Components/MapAnnotation/BusAnnotation/BusAnnotation.swift new file mode 100644 index 00000000..3a97cb7e --- /dev/null +++ b/app/Common/Components/MapAnnotation/BusAnnotation/BusAnnotation.swift @@ -0,0 +1,33 @@ + +import SwiftUI +import MapKit + +struct BusStationAnnotation: View { + var body: some View { + Image( + systemName:"bus" + ) + .imageScale(.medium) + .padding(5) + .foregroundStyle(.white) + .background(.blue) + .clipShape(.rect(cornerRadius: 6)) + .overlay( + RoundedRectangle(cornerRadius: 6) + .stroke(.white, lineWidth: 2) + ) + } +} + +#Preview("Bus station annotation") { + Map { + Annotation( + "Random place on map", coordinate: CLLocationCoordinate2D( + latitude: 50.113680, longitude: 14.449520) + ) { + BusStationAnnotation( + + ) + } + } +} diff --git a/app/Common/Components/MapAnnotation/MetroAnnotation/MetroStationAnnotation.swift b/app/Common/Components/MapAnnotation/MetroAnnotation/MetroStationAnnotation.swift index 2c2cd244..a3b931f1 100644 --- a/app/Common/Components/MapAnnotation/MetroAnnotation/MetroStationAnnotation.swift +++ b/app/Common/Components/MapAnnotation/MetroAnnotation/MetroStationAnnotation.swift @@ -1,13 +1,7 @@ -// -// metro-now -// -// Created by Kryštof Krátký on 20.05.2024. -// - -import MapKit + import SwiftUI -struct MetroStationIcon: View { +struct MetroStationAnnotation: View { let metroLine: String var body: some View { @@ -26,59 +20,3 @@ struct MetroStationIcon: View { ) } } - -struct MapMetroStationView: View { - let metroLines: [String] - - var body: some View { - ZStack { - ForEach(Array(metroLines.enumerated()), id: \.0) { - index, metroLine in - let offset: CGFloat = index == 0 ? 0 : (-16 * CGFloat(index)) - - MetroStationIcon(metroLine: metroLine) - .offset(x: offset, y: offset) - } - } - } -} - -#Preview("One station annotation") { - Map { - Annotation( - "Random place on map", coordinate: CLLocationCoordinate2D( - latitude: 50.113680, longitude: 14.449520) - ) { - MapMetroStationView( - metroLines: ["A"] - ) - } - } -} - -#Preview("Two stations annotation") { - Map { - Annotation( - "Random place on map", coordinate: CLLocationCoordinate2D( - latitude: 50.113680, longitude: 14.449520) - ) { - MapMetroStationView( - metroLines: ["A", "B"] - ) - } - } -} - -// this is not very valid for Prague, but might be useful in the future -#Preview("Multiple stations annotation") { - Map { - Annotation( - "Random place on map", coordinate: CLLocationCoordinate2D( - latitude: 50.113680, longitude: 14.449520) - ) { - MapMetroStationView( - metroLines: ["A", "B", "C", "A", "B", "C"] - ) - } - } -} diff --git a/app/metro-now.xcodeproj/project.pbxproj b/app/metro-now.xcodeproj/project.pbxproj index 1bfce890..90d453a0 100644 --- a/app/metro-now.xcodeproj/project.pbxproj +++ b/app/metro-now.xcodeproj/project.pbxproj @@ -181,6 +181,8 @@ 2D5BA5EB2C382FEA0055F12A /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( + AnnotationStack/MetroAnnotationStack.swift, + BusAnnotation/BusAnnotation.swift, MetroAnnotation/MetroStationAnnotation.swift, ); target = 2DC639D72BF3CCBA00A72C7F /* metro-now */; diff --git a/app/metro-now/Core/Map/MapView.swift b/app/metro-now/Core/Map/MapView.swift index 2c107494..2b27a217 100644 --- a/app/metro-now/Core/Map/MapView.swift +++ b/app/metro-now/Core/Map/MapView.swift @@ -5,14 +5,14 @@ import MapKit import SwiftUI -private struct MetroStationAnnotation { +private struct MetroStationAnnotationType { var name: String let coordinate: CLLocationCoordinate2D - let metroLines: [String] // A | B | C + let metroLines: [String] // A | B | C } struct MapView: View { - @State private var metroStationAnnotations: [MetroStationAnnotation] = [] + @State private var metroStationAnnotations: [MetroStationAnnotationType] = [] var body: some View { NavigationStack { @@ -21,22 +21,30 @@ struct MapView: View { ForEach(metroStationAnnotations, id: \.name) { station in Annotation(station.name, coordinate: station.coordinate) { - NavigationLink(destination: StationDetailView(stationName: station.name, showMap: true, showDirection: true)) { - MapMetroStationView(metroLines: station.metroLines) + NavigationLink( + destination: StationDetailView( + stationName: station.name, + showMap: true, + showDirection: true + ) + ) { + MetroAnnotationStack(metroLines: station.metroLines) } } } } } .task { - let metroStationsGeoJSON: MetroStationsGeoJSON! = getParsedJSONFile(.METRO_STATIONS_FILE) + let metroStationsGeoJSON: MetroStationsGeoJSON! = getParsedJSONFile( + .METRO_STATIONS_FILE + ) guard metroStationsGeoJSON != nil, metroStationsGeoJSON?.features != nil else { return } metroStationAnnotations = metroStationsGeoJSON.features.map { feature in - MetroStationAnnotation( + MetroStationAnnotationType( name: feature.properties.name, coordinate: CLLocationCoordinate2D( latitude: feature.geometry.coordinates[1], From 92075d61e25e1f46dd483381fbc96225a3fc59f1 Mon Sep 17 00:00:00 2001 From: Krystof Date: Sat, 13 Jul 2024 02:22:10 +0200 Subject: [PATCH 5/6] refactor(ci): disable app ci for now --- .github/workflows/app-ci.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/app-ci.yaml b/.github/workflows/app-ci.yaml index a6e4fb52..620402f7 100644 --- a/.github/workflows/app-ci.yaml +++ b/.github/workflows/app-ci.yaml @@ -21,14 +21,14 @@ jobs: - name: Setup Xcode run: | cd ./app - xcodebuild -downloadAllPlatforms + # xcodebuild -downloadAllPlatforms - name: Build run: | cd ./app - xcodebuild build -scheme metro-now -project metro-now.xcodeproj -destination 'platform=iOS Simulator,name=iPhone ${{ matrix.device }},OS=${{ matrix.iOS }}' | xcpretty && exit ${PIPESTATUS[0]} + # xcodebuild build -scheme metro-now -project metro-now.xcodeproj -destination 'platform=iOS Simulator,name=iPhone ${{ matrix.device }},OS=${{ matrix.iOS }}' | xcpretty && exit ${PIPESTATUS[0]} - name: Test run: | cd ./app - xcodebuild test -scheme metro-now -project metro-now.xcodeproj -destination 'platform=iOS Simulator,name=iPhone ${{ matrix.device }},OS=${{ matrix.iOS }}' | xcpretty && exit ${PIPESTATUS[0]} + # xcodebuild test -scheme metro-now -project metro-now.xcodeproj -destination 'platform=iOS Simulator,name=iPhone ${{ matrix.device }},OS=${{ matrix.iOS }}' | xcpretty && exit ${PIPESTATUS[0]} From f72e129e2e6e74ceb70661c4ca64a40d22bb83aa Mon Sep 17 00:00:00 2001 From: Krystof Date: Sat, 13 Jul 2024 02:23:57 +0200 Subject: [PATCH 6/6] fix(app): format --- .github/workflows/app-ci.yaml | 6 ++-- .../MetroAnnotationStack.swift | 5 ++- .../BusAnnotation/BusAnnotation.swift | 31 +++++++++---------- .../MetroStationAnnotation.swift | 2 +- app/metro-now/Core/Map/MapView.swift | 2 +- 5 files changed, 22 insertions(+), 24 deletions(-) diff --git a/.github/workflows/app-ci.yaml b/.github/workflows/app-ci.yaml index 620402f7..c5be9b32 100644 --- a/.github/workflows/app-ci.yaml +++ b/.github/workflows/app-ci.yaml @@ -21,14 +21,14 @@ jobs: - name: Setup Xcode run: | cd ./app - # xcodebuild -downloadAllPlatforms + # xcodebuild -downloadAllPlatforms - name: Build run: | cd ./app - # xcodebuild build -scheme metro-now -project metro-now.xcodeproj -destination 'platform=iOS Simulator,name=iPhone ${{ matrix.device }},OS=${{ matrix.iOS }}' | xcpretty && exit ${PIPESTATUS[0]} + # xcodebuild build -scheme metro-now -project metro-now.xcodeproj -destination 'platform=iOS Simulator,name=iPhone ${{ matrix.device }},OS=${{ matrix.iOS }}' | xcpretty && exit ${PIPESTATUS[0]} - name: Test run: | cd ./app - # xcodebuild test -scheme metro-now -project metro-now.xcodeproj -destination 'platform=iOS Simulator,name=iPhone ${{ matrix.device }},OS=${{ matrix.iOS }}' | xcpretty && exit ${PIPESTATUS[0]} + # xcodebuild test -scheme metro-now -project metro-now.xcodeproj -destination 'platform=iOS Simulator,name=iPhone ${{ matrix.device }},OS=${{ matrix.iOS }}' | xcpretty && exit ${PIPESTATUS[0]} diff --git a/app/Common/Components/MapAnnotation/AnnotationStack/MetroAnnotationStack.swift b/app/Common/Components/MapAnnotation/AnnotationStack/MetroAnnotationStack.swift index 91bb59c1..f8353253 100644 --- a/app/Common/Components/MapAnnotation/AnnotationStack/MetroAnnotationStack.swift +++ b/app/Common/Components/MapAnnotation/AnnotationStack/MetroAnnotationStack.swift @@ -1,8 +1,7 @@ - -import SwiftUI -import MapKit +import MapKit +import SwiftUI struct MetroAnnotationStack: View { let metroLines: [String] diff --git a/app/Common/Components/MapAnnotation/BusAnnotation/BusAnnotation.swift b/app/Common/Components/MapAnnotation/BusAnnotation/BusAnnotation.swift index 3a97cb7e..c85f9030 100644 --- a/app/Common/Components/MapAnnotation/BusAnnotation/BusAnnotation.swift +++ b/app/Common/Components/MapAnnotation/BusAnnotation/BusAnnotation.swift @@ -1,22 +1,22 @@ -import SwiftUI import MapKit +import SwiftUI struct BusStationAnnotation: View { - var body: some View { - Image( - systemName:"bus" - ) - .imageScale(.medium) - .padding(5) - .foregroundStyle(.white) - .background(.blue) - .clipShape(.rect(cornerRadius: 6)) - .overlay( - RoundedRectangle(cornerRadius: 6) - .stroke(.white, lineWidth: 2) - ) - } + var body: some View { + Image( + systemName: "bus" + ) + .imageScale(.medium) + .padding(5) + .foregroundStyle(.white) + .background(.blue) + .clipShape(.rect(cornerRadius: 6)) + .overlay( + RoundedRectangle(cornerRadius: 6) + .stroke(.white, lineWidth: 2) + ) + } } #Preview("Bus station annotation") { @@ -26,7 +26,6 @@ struct BusStationAnnotation: View { latitude: 50.113680, longitude: 14.449520) ) { BusStationAnnotation( - ) } } diff --git a/app/Common/Components/MapAnnotation/MetroAnnotation/MetroStationAnnotation.swift b/app/Common/Components/MapAnnotation/MetroAnnotation/MetroStationAnnotation.swift index a3b931f1..2853256e 100644 --- a/app/Common/Components/MapAnnotation/MetroAnnotation/MetroStationAnnotation.swift +++ b/app/Common/Components/MapAnnotation/MetroAnnotation/MetroStationAnnotation.swift @@ -1,4 +1,4 @@ - + import SwiftUI struct MetroStationAnnotation: View { diff --git a/app/metro-now/Core/Map/MapView.swift b/app/metro-now/Core/Map/MapView.swift index 2b27a217..9b4a04ac 100644 --- a/app/metro-now/Core/Map/MapView.swift +++ b/app/metro-now/Core/Map/MapView.swift @@ -8,7 +8,7 @@ import SwiftUI private struct MetroStationAnnotationType { var name: String let coordinate: CLLocationCoordinate2D - let metroLines: [String] // A | B | C + let metroLines: [String] // A | B | C } struct MapView: View {