diff --git a/.github/workflows/obakittests.yml b/.github/workflows/obakittests.yml index 7fed49482..3c20310b9 100644 --- a/.github/workflows/obakittests.yml +++ b/.github/workflows/obakittests.yml @@ -8,12 +8,12 @@ on: jobs: build: - runs-on: macos-14 + runs-on: macos-15 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - uses: actions/cache@v2 + - uses: actions/cache@v4 with: path: .build key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} @@ -21,7 +21,7 @@ jobs: ${{ runner.os }}-spm- - name: Switch Xcode 15 - run: sudo xcode-select -switch /Applications/Xcode_15.4.app + run: sudo xcode-select -switch /Applications/Xcode_16.1.app - name: Install xcodegen run: brew install xcodegen @@ -33,7 +33,7 @@ jobs: - name: Build OneBusAway run: xcodebuild clean build-for-testing -scheme 'App' - -destination 'platform=iOS Simulator,name=iPhone 15' + -destination 'platform=iOS Simulator,name=iPhone 16' -quiet # Unit Test @@ -42,7 +42,7 @@ jobs: -only-testing:OBAKitTests -project 'OBAKit.xcodeproj' -scheme 'App' - -destination 'platform=iOS Simulator,name=iPhone 15' + -destination 'platform=iOS Simulator,name=iPhone 16' -resultBundlePath OBAKitTests.xcresult -quiet diff --git a/OBAKit/Bookmarks/BookmarksViewController.swift b/OBAKit/Bookmarks/BookmarksViewController.swift index dd07a68c8..6ea726a81 100644 --- a/OBAKit/Bookmarks/BookmarksViewController.swift +++ b/OBAKit/Bookmarks/BookmarksViewController.swift @@ -158,13 +158,13 @@ public class BookmarksViewController: UIViewController, let sortMenu = UIMenu(title: Strings.sort, options: .displayInline, children: [groupSortAction, distanceSortAction]) navigationItem.rightBarButtonItem = UIBarButtonItem(title: "MORE", image: UIImage(systemName: "arrow.up.arrow.down.circle"), menu: sortMenu) } - + // MARK: Refresh Widget func reloadWidget() { print("Reloading the widget") WidgetCenter.shared.reloadTimelines(ofKind: "OBAWidget") } - + // MARK: - Refresh Control @objc private func refreshControlPulled() { diff --git a/OBAKit/Bookmarks/TripBookmarkCell.swift b/OBAKit/Bookmarks/TripBookmarkCell.swift index f9c6993ef..f5491b92f 100644 --- a/OBAKit/Bookmarks/TripBookmarkCell.swift +++ b/OBAKit/Bookmarks/TripBookmarkCell.swift @@ -116,7 +116,8 @@ final class TripBookmarkTableCell: OBAListViewCell { ]) // MARK: - UI Builders - private class func buildLabel(textStyle: UIFont.TextStyle) -> UILabel { + + private static func buildLabel(textStyle: UIFont.TextStyle) -> UILabel { let label = UILabel.obaLabel(font: .preferredFont(forTextStyle: textStyle)) label.setContentCompressionResistancePriority(.required, for: .vertical) label.setCompressionResistance(horizontal: .required, vertical: .required) @@ -141,9 +142,9 @@ final class TripBookmarkTableCell: OBAListViewCell { isAccessibilityElement = true accessibilityTraits = [.button, .updatesFrequently] - + let sizeTraits: [UITrait] = [UITraitVerticalSizeClass.self, UITraitHorizontalSizeClass.self] - registerForTraitChanges(sizeTraits) { (self: Self, previousTraitCollection: UITraitCollection) in + registerForTraitChanges(sizeTraits) { (self: Self, _) in self.layoutView() } } diff --git a/OBAKit/DeepLinks/AppLinksRouter.swift b/OBAKit/DeepLinks/AppLinksRouter.swift index 9f8baf82f..38e61ecae 100644 --- a/OBAKit/DeepLinks/AppLinksRouter.swift +++ b/OBAKit/DeepLinks/AppLinksRouter.swift @@ -20,7 +20,7 @@ public class AppLinksRouter: NSObject { public init?(application: Application) { self.application = application } - + /// The base URL for all operations in this object. private var baseURL: URL? { application.regionsService.currentRegion?.sidecarBaseURL @@ -33,7 +33,7 @@ public class AppLinksRouter: NSObject { /// - region: The region in which the link will exist. public func url(for stop: Stop, region: Region) -> URL? { guard let baseURL else { return nil } - + guard var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: false) else { return nil } components.path = String(format: "/regions/%d/stops/%@", region.regionIdentifier, stop.id) @@ -49,7 +49,7 @@ public class AppLinksRouter: NSObject { /// - region: The region in which the `ArrivalDeparture` exists. public func encode(arrivalDeparture: ArrivalDeparture, region: Region) -> URL? { guard let baseURL else { return nil } - + var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: false)! components.path = String(format: deepLinkPathFormat, region.regionIdentifier, arrivalDeparture.stopID) components.queryItems = [ diff --git a/OBAKit/Donations/StripeExtensions.swift b/OBAKit/Donations/StripeExtensions.swift index 94dcb0e04..e3bb160ad 100644 --- a/OBAKit/Donations/StripeExtensions.swift +++ b/OBAKit/Donations/StripeExtensions.swift @@ -8,7 +8,7 @@ #if canImport(Stripe) import StripePaymentSheet -extension PaymentSheetResult: Equatable { +extension PaymentSheetResult: @retroactive Equatable { public static func == (lhs: PaymentSheetResult, rhs: PaymentSheetResult) -> Bool { switch (lhs, rhs) { case (.completed, .completed), (.canceled, .canceled): diff --git a/OBAKit/Extensions/ModelExtensions.swift b/OBAKit/Extensions/ModelExtensions.swift index 0707f1e09..05acdd48c 100644 --- a/OBAKit/Extensions/ModelExtensions.swift +++ b/OBAKit/Extensions/ModelExtensions.swift @@ -13,7 +13,7 @@ import OBAKitCore // MARK: - Bookmark/MKAnnotation -extension Bookmark: MKAnnotation { +extension Bookmark: @retroactive MKAnnotation { public var coordinate: CLLocationCoordinate2D { stop.coordinate } @@ -25,7 +25,7 @@ extension Bookmark: MKAnnotation { // MARK: - Region/MKAnnotation -extension Region: MKAnnotation { +extension Region: @retroactive MKAnnotation { public var coordinate: CLLocationCoordinate2D { centerCoordinate } @@ -41,7 +41,7 @@ extension Region: MKAnnotation { /// Includes additional methods for rendering extra data directly onto the map. /// /// - Note: See `StopAnnotationView`for more details. -extension Stop: MKAnnotation { +extension Stop: @retroactive MKAnnotation { public var coordinate: CLLocationCoordinate2D { location.coordinate } @@ -64,7 +64,7 @@ extension Stop: MKAnnotation { // MARK: - TripStatus/MKAnnotation /// Adds conformance to `MKAnnotation` to `TripStatus`. -extension TripStatus: MKAnnotation { +extension TripStatus: @retroactive MKAnnotation { public var coordinate: CLLocationCoordinate2D { lastKnownLocation?.coordinate ?? CLLocationCoordinate2D(latitude: 0, longitude: 0) @@ -85,7 +85,7 @@ extension TripStatus: MKAnnotation { /// Adds conformance to `MKAnnotation` to `TripStopTime`. /// /// - Note: See `MinimalStopAnnotationView`for more details. -extension TripStopTime: MKAnnotation { +extension TripStopTime: @retroactive MKAnnotation { public var coordinate: CLLocationCoordinate2D { stop.location.coordinate } diff --git a/OBAKit/Mapping/MapViewController.swift b/OBAKit/Mapping/MapViewController.swift index e8c0beba8..87318eda5 100644 --- a/OBAKit/Mapping/MapViewController.swift +++ b/OBAKit/Mapping/MapViewController.swift @@ -629,10 +629,8 @@ class MapViewController: UIViewController, } else { promptUserOnRegionMismatch = false - if - let regionMismatchBulletin = RegionMismatchBulletin(application: application), - let uiApp = application.delegate?.uiApplication - { + if let regionMismatchBulletin = RegionMismatchBulletin(application: application), + let uiApp = application.delegate?.uiApplication { self.regionMismatchBulletin = regionMismatchBulletin self.regionMismatchBulletin?.show(in: uiApp) } diff --git a/OBAKit/Mapping/PulsingAnnotationView.swift b/OBAKit/Mapping/PulsingAnnotationView.swift index 58da7351b..982aaa7d5 100644 --- a/OBAKit/Mapping/PulsingAnnotationView.swift +++ b/OBAKit/Mapping/PulsingAnnotationView.swift @@ -118,7 +118,7 @@ class PulsingAnnotationView: MKAnnotationView { layer.anchorPoint = CGPoint(x: 0.5, y: 0.5) calloutOffset = CGPoint(x: 0, y: 4) self.bounds = bounds - + registerForTraitChanges([UITraitUserInterfaceStyle.self]) { (self: Self, previousTraitCollection: UITraitCollection) in if self.traitCollection.userInterfaceStyle != previousTraitCollection.userInterfaceStyle { self.rebuildLayers() diff --git a/OBAKit/Mapping/StopAnnotationView.swift b/OBAKit/Mapping/StopAnnotationView.swift index 0403e8f30..26e8781f6 100644 --- a/OBAKit/Mapping/StopAnnotationView.swift +++ b/OBAKit/Mapping/StopAnnotationView.swift @@ -76,7 +76,7 @@ class StopAnnotationView: MKAnnotationView { updateAccessibility() NotificationCenter.default.addObserver(self, selector: #selector(voiceoverStatusDidChange), name: UIAccessibility.voiceOverStatusDidChangeNotification, object: nil) - + registerForTraitChanges([UITraitUserInterfaceStyle.self]) { (self: Self, previousTraitCollection: UITraitCollection) in if self.traitCollection.userInterfaceStyle != previousTraitCollection.userInterfaceStyle { self.rebuildIcon() @@ -174,7 +174,7 @@ class StopAnnotationView: MKAnnotationView { // we should skip the callout and push directly to the annotation's destination view. canShowCallout = !UIAccessibility.isVoiceOverRunning } - + private func rebuildIcon() { guard let stop = annotation as? Stop, diff --git a/OBAKit/Onboarding/DataMigration/DataMigrationError+Localization.swift b/OBAKit/Onboarding/DataMigration/DataMigrationError+Localization.swift index 312bae859..94b69675b 100644 --- a/OBAKit/Onboarding/DataMigration/DataMigrationError+Localization.swift +++ b/OBAKit/Onboarding/DataMigration/DataMigrationError+Localization.swift @@ -7,7 +7,7 @@ import OBAKitCore -extension DataMigrationError: LocalizedError { +extension DataMigrationError: @retroactive LocalizedError { public var errorDescription: String? { switch self { case .invalidAPIService(let explanation): @@ -22,7 +22,7 @@ extension DataMigrationError: LocalizedError { } } -extension DataMigrationBookmarkError: LocalizedError { +extension DataMigrationBookmarkError: @retroactive LocalizedError { public var errorDescription: String? { switch self { case .noActiveTrips: diff --git a/OBAKit/Orchestration/Application.swift b/OBAKit/Orchestration/Application.swift index 2dfde5621..0fb0cd315 100644 --- a/OBAKit/Orchestration/Application.swift +++ b/OBAKit/Orchestration/Application.swift @@ -94,7 +94,7 @@ public class Application: CoreApplication, PushServiceDelegate { /// Handles all deep-linking into the app. @objc public private(set) lazy var appLinksRouter: AppLinksRouter? = { let router = AppLinksRouter(application: self) - + router?.showStopHandler = { [weak self] stop in guard let self = self, diff --git a/OBAKit/Reporting/StopProblemViewController.swift b/OBAKit/Reporting/StopProblemViewController.swift index 95cdbd3a6..0fc3b6e16 100644 --- a/OBAKit/Reporting/StopProblemViewController.swift +++ b/OBAKit/Reporting/StopProblemViewController.swift @@ -14,7 +14,7 @@ import OBAKitCore // This seems...busted. I can't figure out when this // initializer will actually be called, though. Do I even // really need it? -extension StopProblemCode: InputTypeInitiable { +extension StopProblemCode: @retroactive InputTypeInitiable { public init?(string stringValue: String) { return nil } diff --git a/OBAKit/Reporting/VehicleProblemViewController.swift b/OBAKit/Reporting/VehicleProblemViewController.swift index d8c97ae33..580d94611 100644 --- a/OBAKit/Reporting/VehicleProblemViewController.swift +++ b/OBAKit/Reporting/VehicleProblemViewController.swift @@ -14,7 +14,7 @@ import OBAKitCore // This seems...busted. I can't figure out when this // initializer will actually be called, though. Do I even // really need it? -extension TripProblemCode: InputTypeInitiable { +extension TripProblemCode: @retroactive InputTypeInitiable { public init?(string stringValue: String) { return nil } diff --git a/OBAKit/Stops/NearbyStopsViewController.swift b/OBAKit/Stops/NearbyStopsViewController.swift index 13348b0da..d51dd1200 100644 --- a/OBAKit/Stops/NearbyStopsViewController.swift +++ b/OBAKit/Stops/NearbyStopsViewController.swift @@ -128,7 +128,7 @@ class NearbyStopsViewController: UIViewController, directions[stop.direction] = list } - let tapHandler = { [unowned self] (vm: StopViewModel) -> Void in + let tapHandler = { [unowned self] (vm: StopViewModel) in self.application.viewRouter.navigateTo(stopID: vm.stopID, from: self) } diff --git a/OBAKit/Stops/StopHeaderController.swift b/OBAKit/Stops/StopHeaderController.swift index 7d4ed89f7..0e27e0a8a 100644 --- a/OBAKit/Stops/StopHeaderController.swift +++ b/OBAKit/Stops/StopHeaderController.swift @@ -149,9 +149,9 @@ class StopHeaderView: UIView { ]) addGestureRecognizer(toggleRouteDetailsGestureRecognizer) - + let sizeTraits: [UITrait] = [UITraitVerticalSizeClass.self, UITraitHorizontalSizeClass.self] - registerForTraitChanges(sizeTraits) { (self: Self, previousTraitCollection: UITraitCollection) in + registerForTraitChanges(sizeTraits) { (self: Self, _) in self.configureView() } } diff --git a/OBAKit/Stops/WalkTimeView.swift b/OBAKit/Stops/WalkTimeView.swift index 826b60ee7..1de029172 100644 --- a/OBAKit/Stops/WalkTimeView.swift +++ b/OBAKit/Stops/WalkTimeView.swift @@ -74,9 +74,9 @@ class WalkTimeView: UIView { ]) NotificationCenter.default.addObserver(self, selector: #selector(deviceOrientationDidChange), name: UIDevice.orientationDidChangeNotification, object: nil) - + let sizeTraits: [UITrait] = [UITraitVerticalSizeClass.self, UITraitHorizontalSizeClass.self] - registerForTraitChanges(sizeTraits) { (self: Self, previousTraitCollection: UITraitCollection) in + registerForTraitChanges(sizeTraits) { (self: Self, _) in self.setNeedsDisplay() } } diff --git a/OBAKit/Trip/TripStopListItem.swift b/OBAKit/Trip/TripStopListItem.swift index 8437f9b0d..d43082b4d 100644 --- a/OBAKit/Trip/TripStopListItem.swift +++ b/OBAKit/Trip/TripStopListItem.swift @@ -178,8 +178,8 @@ final class TripStopCell: OBAListViewCell { ]) isAccessibilityElement = true - - registerForTraitChanges([UITraitPreferredContentSizeCategory.self]) { (self: Self, previousTraitCollection: UITraitCollection) in + + registerForTraitChanges([UITraitPreferredContentSizeCategory.self]) { (self: Self, _) in self.layoutAccessibility() } } diff --git a/OBAKit/Trip/TripViewController.swift b/OBAKit/Trip/TripViewController.swift index d8315e468..d0ccf5110 100644 --- a/OBAKit/Trip/TripViewController.swift +++ b/OBAKit/Trip/TripViewController.swift @@ -30,7 +30,7 @@ class TripViewController: UIViewController, self.tripConvertible = tripConvertible super.init(nibName: nil, bundle: nil) - + registerTraitChangeCallback() } @@ -39,7 +39,7 @@ class TripViewController: UIViewController, self.tripConvertible = TripConvertible(arrivalDeparture: arrivalDeparture) super.init(nibName: nil, bundle: nil) - + registerTraitChangeCallback() } @@ -50,10 +50,10 @@ class TripViewController: UIViewController, deinit { enableIdleTimer() } - + private func registerTraitChangeCallback() { let sizeTraits: [UITrait] = [UITraitVerticalSizeClass.self, UITraitHorizontalSizeClass.self, UITraitPreferredContentSizeCategory.self] - registerForTraitChanges(sizeTraits) { (self: Self, previousTraitCollection: UITraitCollection) in + registerForTraitChanges(sizeTraits) { (self: Self, _) in self.updateTitleView() } } diff --git a/OBAKit/project.yml b/OBAKit/project.yml index 0079329d0..02876fcc0 100644 --- a/OBAKit/project.yml +++ b/OBAKit/project.yml @@ -13,6 +13,7 @@ targets: postBuildScripts: - path: "../scripts/swiftlint.sh" name: Swiftlint + basedOnDependencyAnalysis: false scheme: gatherCoverageData: true info: diff --git a/OBAKitCore/Extensions/CoreLocationExtensions.swift b/OBAKitCore/Extensions/CoreLocationExtensions.swift index 50e0efd59..63f9e8d27 100644 --- a/OBAKitCore/Extensions/CoreLocationExtensions.swift +++ b/OBAKitCore/Extensions/CoreLocationExtensions.swift @@ -11,7 +11,8 @@ import CoreGraphics import CoreLocation import MapKit -extension CLAuthorizationStatus: LosslessStringConvertible { +extension CLAuthorizationStatus: @retroactive CustomStringConvertible {} +extension CLAuthorizationStatus: @retroactive LosslessStringConvertible { public init?(_ description: String) { nil } public var description: String { diff --git a/OBAKitCore/Models/Region.swift b/OBAKitCore/Models/Region.swift index 83934c0c1..947c3b0e2 100644 --- a/OBAKitCore/Models/Region.swift +++ b/OBAKitCore/Models/Region.swift @@ -45,7 +45,7 @@ public class Region: NSObject, Identifiable, Codable { /// The base URL for making OBA REST API requests. public let OBABaseURL: URL - + /// The base URL for sidecar server (i.e. OneBusAway.co/Obaco) REST API requests public let sidecarBaseURL: URL? diff --git a/OBAKitCore/Models/UserData/UserDataStore.swift b/OBAKitCore/Models/UserData/UserDataStore.swift index 9e788e89f..1650e16aa 100644 --- a/OBAKitCore/Models/UserData/UserDataStore.swift +++ b/OBAKitCore/Models/UserData/UserDataStore.swift @@ -563,10 +563,7 @@ public class UserDefaultsStore: NSObject, UserDataStore, StopPreferencesStore { // MARK: - Private Helpers private func upsert(bookmark: Bookmark) { - if - let existing = findBookmark(id: bookmark.id), - let index = bookmarks.firstIndex(of: existing) - { + if let existing = findBookmark(id: bookmark.id), let index = bookmarks.firstIndex(of: existing) { bookmarks.remove(at: index) bookmarks.insert(bookmark, at: index) } diff --git a/OBAKitCore/Network/ObacoAPIService.swift b/OBAKitCore/Network/ObacoAPIService.swift index 29b029e24..ddbf6e592 100644 --- a/OBAKitCore/Network/ObacoAPIService.swift +++ b/OBAKitCore/Network/ObacoAPIService.swift @@ -17,7 +17,7 @@ public protocol ObacoServiceDelegate: NSObjectProtocol { /// API service client for the Obaco (`alerts.onebusaway.org`) service. /// /// Obaco provides services like weather, trip status, and alarms to the iOS app. -public actor ObacoAPIService: APIService { +public actor ObacoAPIService: @preconcurrency APIService { public let configuration: APIServiceConfiguration public nonisolated let dataLoader: URLDataLoader diff --git a/OBAKitCore/Network/RESTAPIService/RESTAPIService+GetAgencyAlerts.swift b/OBAKitCore/Network/RESTAPIService/RESTAPIService+GetAgencyAlerts.swift index 6fe6d4504..6995dabe6 100644 --- a/OBAKitCore/Network/RESTAPIService/RESTAPIService+GetAgencyAlerts.swift +++ b/OBAKitCore/Network/RESTAPIService/RESTAPIService+GetAgencyAlerts.swift @@ -28,7 +28,7 @@ extension RESTAPIService { do { let (data, _) = try await self.getData(for: url) - let message = try TransitRealtime_FeedMessage(serializedData: data) + let message = try TransitRealtime_FeedMessage(serializedBytes: data) return message.entity .filter(isQualifiedAlert) .compactMap { diff --git a/OBAKitCore/Network/RESTAPIService/RESTAPIService.swift b/OBAKitCore/Network/RESTAPIService/RESTAPIService.swift index 0bd1eaa5a..5cf998311 100644 --- a/OBAKitCore/Network/RESTAPIService/RESTAPIService.swift +++ b/OBAKitCore/Network/RESTAPIService/RESTAPIService.swift @@ -9,7 +9,7 @@ import Foundation import os.log /// Makes API calls to the OBA REST service and converts the server's responses into model objects. -public actor RESTAPIService: APIService { +public actor RESTAPIService: @preconcurrency APIService { public let configuration: APIServiceConfiguration public nonisolated let dataLoader: URLDataLoader diff --git a/OBAKitTests/Helpers/OBATestCase.swift b/OBAKitTests/Helpers/OBATestCase.swift index 217c80001..97da53e2d 100644 --- a/OBAKitTests/Helpers/OBATestCase.swift +++ b/OBAKitTests/Helpers/OBATestCase.swift @@ -98,10 +98,10 @@ open class OBATestCase: XCTestCase { ) } - func stubRegions(dataLoader: MockDataLoader) { + func stubRegions(dataLoader: MockDataLoader, fixtureFile: String = "regions-v3.json") { dataLoader.mock( URLString: "https://regions.example.com/regions-v3.json", - with: Fixtures.loadData(file: "regions-v3.json") + with: Fixtures.loadData(file: fixtureFile) ) } diff --git a/OBAKitTests/Modeling/Regions Model Service Tests/RegionsModelOperationTests.swift b/OBAKitTests/Modeling/Regions Model Service Tests/RegionsModelOperationTests.swift index dbf37dabf..1af1b16de 100644 --- a/OBAKitTests/Modeling/Regions Model Service Tests/RegionsModelOperationTests.swift +++ b/OBAKitTests/Modeling/Regions Model Service Tests/RegionsModelOperationTests.swift @@ -81,4 +81,15 @@ class RegionsModelOperationTests: OBATestCase { expect(pugetSound.centerCoordinate.latitude).to(beCloseTo(47.795091214055)) expect(pugetSound.centerCoordinate.longitude).to(beCloseTo(-122.49868405298474)) } + +// WIP Fix for #777 +// func testDecoderError() async throws { +// let dataLoader = regionsAPIService.dataLoader as! MockDataLoader +// stubRegions(dataLoader: dataLoader, fixtureFile: "decoder-failure__regions-v3.json") +// +// let response = try await regionsAPIService.getRegions(apiPath: regionsAPIPath) +// +// let regions = response.list +// expect(regions.count) == 17 +// } } diff --git a/OBAKitTests/fixtures/decoder-failure__regions-v3.json b/OBAKitTests/fixtures/decoder-failure__regions-v3.json new file mode 100644 index 000000000..761074eb4 --- /dev/null +++ b/OBAKitTests/fixtures/decoder-failure__regions-v3.json @@ -0,0 +1 @@ +{"version":3,"code":200,"text":"OK","data":{"list":[{"id":0,"regionName":"Tampa Bay","sidecarBaseUrl":"https://onebusaway.co","obaBaseUrl":"https://api.tampa.onebusaway.org/api/","siriBaseUrl":"https://tampa.onebusaway.org/onebusaway-api-webapp/siri/","bounds":[{"lat":27.9769105,"lon":-82.445851,"latSpan":0.542461,"lonSpan":0.576358},{"lat":27.91925,"lon":-82.652145,"latSpan":0.47208,"lonSpan":0.39677}],"language":"en_US","contactEmail":"onebusaway@gohart.org","supportsObaDiscoveryApis":true,"supportsObaRealtimeApis":true,"supportsSiriRealtimeApis":true,"active":true,"obaVersionInfo":"2.4.15-cs|2|4|15|cs|d41e1a8978da14e98a2e19d109a23018957db7cf","twitterUrl":"https://mobile.twitter.com/OBA_tampa","facebookUrl":"","experimental":false,"stopInfoUrl":null,"open311Servers":[{"juridisctionId":"","apiKey":"937033cad3054ec58a1a8156dcdd6ad8a416af2f","baseUrl":"https://seeclickfix.com/open311/v2/"}],"otpBaseUrl":"https://otp.prod.obahart.org/otp/","otpContactEmail":"otp-tampa@onebusaway.org","supportsEmbeddedSocial":false,"supportsOtpBikeshare":true,"paymentAndroidAppId":"co.bytemark.flamingo","paymentWarningTitle":null,"paymentWarningBody":null,"paymentiOSAppStoreIdentifier":"1487465395","paymentiOSAppUrlScheme":"fb313213768708402HART","travelBehaviorDataCollectionEnabled":false,"enrollParticipantsInStudy":false},{"id":1,"regionName":"Puget Sound","sidecarBaseUrl":"https://onebusaway.co","obaBaseUrl":"https://api.pugetsound.onebusaway.org/","siriBaseUrl":"https://pugetsound.onebusaway.org/onebusaway-api-webapp/siri/","bounds":[{"lat":47.221315,"lon":-122.4051325,"latSpan":0.33704,"lonSpan":0.440483},{"lat":47.5607395,"lon":-122.1462785,"latSpan":0.743251,"lonSpan":0.720901},{"lat":47.556288,"lon":-122.4013255,"latSpan":0.090694,"lonSpan":0.126793},{"lat":47.093563,"lon":-122.701637,"latSpan":0.320892,"lonSpan":0.55098},{"lat":47.5346090123,"lon":-122.3294835,"latSpan":0.889378024643,"lonSpan":0.621109},{"lat":47.9747595,"lon":-122.8512,"latSpan":1.336481,"lonSpan":1.0904},{"lat":47.6204755,"lon":-122.335392,"latSpan":0.014397,"lonSpan":0.006356},{"lat":47.64585,"lon":-122.2963,"latSpan":0.0669,"lonSpan":0.0802},{"lat":47.9347358907,"lon":-121.993246104,"latSpan":0.68796117128,"lonSpan":0.784555996061}],"language":"en_US","contactEmail":"onebusaway@soundtransit.org","supportsObaDiscoveryApis":true,"supportsObaRealtimeApis":true,"supportsSiriRealtimeApis":true,"active":true,"obaVersionInfo":"2.4.16-cs|2|4|16|cs|955724797b2ff207b04c0c80d32c952c085bf0c7","twitterUrl":"https://mobile.twitter.com/oba_pugetsound","facebookUrl":"https://www.facebook.com/pages/OneBusAway/216091804930","experimental":false,"stopInfoUrl":null,"open311Servers":[],"otpBaseUrl":"https://otp.prod.sound.obaweb.org/otp/routers/default/","otpContactEmail":"otp-pugetsound@onebusaway.org","supportsEmbeddedSocial":false,"supportsOtpBikeshare":false,"paymentAndroidAppId":"co.bytemark.tgt","paymentWarningTitle":"Check before you buy!","paymentWarningBody":"The mobile fare payment app for Puget Sound does not support all transit service shown in OneBusAway. Please check that a ticket is eligible for your agency and route before you purchase!","paymentiOSAppStoreIdentifier":"1131345078","paymentiOSAppUrlScheme":"co.bytemark.tgt","travelBehaviorDataCollectionEnabled":false,"enrollParticipantsInStudy":false},{"id":2,"regionName":"MTA New York","sidecarBaseUrl":"https://onebusaway.co","obaBaseUrl":"https://bustime.mta.info/","siriBaseUrl":"https://bustime.mta.info/api/","bounds":[{"lat":40.707678,"lon":-74.017681,"latSpan":0.40939,"lonSpan":0.468666},{"lat":40.8192825,"lon":-73.899082,"latSpan":0.228707,"lonSpan":0.231468}],"language":"en_US","contactEmail":"MTABusTime@mtahq.org","supportsObaDiscoveryApis":true,"supportsObaRealtimeApis":false,"supportsSiriRealtimeApis":true,"active":true,"obaVersionInfo":"","twitterUrl":"https://mobile.twitter.com/nyctbusstop","facebookUrl":"https://www.facebook.com/pages/MTA-New-York-City-Transit/232635164606","experimental":false,"stopInfoUrl":null,"open311Servers":[],"otpBaseUrl":null,"otpContactEmail":null,"supportsEmbeddedSocial":false,"supportsOtpBikeshare":false,"paymentAndroidAppId":null,"paymentWarningTitle":null,"paymentWarningBody":null,"paymentiOSAppStoreIdentifier":null,"paymentiOSAppUrlScheme":null,"travelBehaviorDataCollectionEnabled":false,"enrollParticipantsInStudy":false},{"id":3,"regionName":"Atlanta","sidecarBaseUrl":"https://onebusaway.co","obaBaseUrl":"https://atlanta.onebusaway.org/api/","siriBaseUrl":null,"bounds":[{"lat":33.790179768105,"lon":-84.394832162125,"latSpan":0.002537628407,"lonSpan":0.016058977127},{"lat":33.848592517665,"lon":-84.361894869147,"latSpan":0.006806584026,"lonSpan":0.035245473959},{"lat":34.224449084986,"lon":-84.484198860319,"latSpan":0.066268268418,"lonSpan":0.051677923063},{"lat":33.787847522847,"lon":-84.310827462409,"latSpan":0.026695928047,"lonSpan":0.028927748099},{"lat":33.8079649176,"lon":-84.34070523855,"latSpan":0.8443565224,"lonSpan":0.8666740199},{"lat":33.784206183479,"lon":-84.363855524655,"latSpan":0.030916824823,"lonSpan":0.084162950689},{"lat":33.8105225,"lon":-84.379668,"latSpan":0.587429,"lonSpan":0.58204}],"language":"en_US","contactEmail":"onebusaway@atlantaregional.com","supportsObaDiscoveryApis":true,"supportsObaRealtimeApis":true,"supportsSiriRealtimeApis":false,"active":false,"obaVersionInfo":"1.1.14-SNAPSHOT|1|1|14|SNAPSHOT|440e5cb692a1ed195de7f0686d69f5ceecbe9a41","twitterUrl":"https://mobile.twitter.com/OBA_atlanta","facebookUrl":"https://www.facebook.com/pages/ObaAtlanta/136662306506627","experimental":false,"stopInfoUrl":null,"open311Servers":[],"otpBaseUrl":"https://opentrip.atlantaregion.com/otp","otpContactEmail":"onebusaway@atlantaregional.com","supportsEmbeddedSocial":false,"supportsOtpBikeshare":false,"paymentAndroidAppId":null,"paymentWarningTitle":null,"paymentWarningBody":null,"paymentiOSAppStoreIdentifier":null,"paymentiOSAppUrlScheme":null,"travelBehaviorDataCollectionEnabled":false,"enrollParticipantsInStudy":false},{"id":4,"regionName":"Washington, D.C.","sidecarBaseUrl":"https://onebusaway.co","obaBaseUrl":"https://buseta.wmata.com/onebusaway-api-webapp/","siriBaseUrl":"https://buseta.wmata.com/onebusaway-api-webapp/siri/","bounds":[{"lat":38.8950925,"lon":-77.059196,"latSpan":0.592797,"lonSpan":0.78052}],"language":"en_US","contactEmail":"feedback@wmata.com","supportsObaDiscoveryApis":true,"supportsObaRealtimeApis":true,"supportsSiriRealtimeApis":true,"active":true,"obaVersionInfo":"2.4.3.11-cs|2|4|3|11|cs|bc4131416026b12ec676b89cf8c7bf8e92bd41db","twitterUrl":"https://mobile.twitter.com/Metrobusinfo","facebookUrl":"","experimental":false,"stopInfoUrl":null,"open311Servers":[],"otpBaseUrl":null,"otpContactEmail":null,"supportsEmbeddedSocial":false,"supportsOtpBikeshare":false,"paymentAndroidAppId":null,"paymentWarningTitle":null,"paymentWarningBody":null,"paymentiOSAppStoreIdentifier":null,"paymentiOSAppUrlScheme":null,"travelBehaviorDataCollectionEnabled":false,"enrollParticipantsInStudy":false},{"id":5,"regionName":"York","sidecarBaseUrl":"https://onebusaway.co","obaBaseUrl":"https://oba.yrt.ca/","siriBaseUrl":null,"bounds":[{"lat":44.0248945,"lon":-79.43752,"latSpan":0.608963,"lonSpan":0.493296}],"language":"en_CA","contactEmail":"transitinfo@york.ca","supportsObaDiscoveryApis":true,"supportsObaRealtimeApis":true,"supportsSiriRealtimeApis":false,"active":false,"obaVersionInfo":"1.1.7|1|1|7||c8ee3d4906dd55ecafdd124f31f39c0f54a37b52","twitterUrl":"https://mobile.twitter.com/YRTViva","facebookUrl":"https://www.facebook.com/198178906967045","experimental":false,"stopInfoUrl":null,"open311Servers":[],"otpBaseUrl":null,"otpContactEmail":null,"supportsEmbeddedSocial":false,"supportsOtpBikeshare":false,"paymentAndroidAppId":null,"paymentWarningTitle":null,"paymentWarningBody":null,"paymentiOSAppStoreIdentifier":null,"paymentiOSAppUrlScheme":null,"travelBehaviorDataCollectionEnabled":false,"enrollParticipantsInStudy":false},{"id":6,"regionName":"Bear Transit (beta)","sidecarBaseUrl":"https://onebusaway.co","obaBaseUrl":"https://bt.v-a.io/onebusaway/","siriBaseUrl":null,"bounds":[{"lat":37.8917275,"lon":-122.2895775,"latSpan":0.049293,"lonSpan":0.101815}],"language":"en_US","contactEmail":"bear-transit@v-a.io","supportsObaDiscoveryApis":true,"supportsObaRealtimeApis":true,"supportsSiriRealtimeApis":false,"active":false,"obaVersionInfo":"1.1.7|1|1|7|d3bbb9109a652359845bdee516dc2cbd1ba35e49","twitterUrl":"https://mobile.twitter.com/CalParking","facebookUrl":"https://www.facebook.com/pages/Bear-Transit/109669175726418","experimental":true,"stopInfoUrl":null,"open311Servers":[],"otpBaseUrl":null,"otpContactEmail":null,"supportsEmbeddedSocial":false,"supportsOtpBikeshare":false,"paymentAndroidAppId":null,"paymentWarningTitle":null,"paymentWarningBody":null,"paymentiOSAppStoreIdentifier":null,"paymentiOSAppUrlScheme":null,"travelBehaviorDataCollectionEnabled":false,"enrollParticipantsInStudy":false},{"id":7,"regionName":"Boston (beta)","sidecarBaseUrl":"https://onebusaway.co","obaBaseUrl":"https://app.dev.mbta.obaweb.org/onebusaway-api-webapp/","siriBaseUrl":null,"bounds":[{"lat":42.367244,"lon":-71.023894,"latSpan":0.01491,"lonSpan":0.011874},{"lat":42.1893185,"lon":-71.210252,"latSpan":1.217037,"lonSpan":1.169272}],"language":"en_US","contactEmail":"sbrown@camsys.com","supportsObaDiscoveryApis":true,"supportsObaRealtimeApis":true,"supportsSiriRealtimeApis":false,"active":false,"obaVersionInfo":"","twitterUrl":"","facebookUrl":"","experimental":true,"stopInfoUrl":null,"open311Servers":[],"otpBaseUrl":null,"otpContactEmail":null,"supportsEmbeddedSocial":false,"supportsOtpBikeshare":false,"paymentAndroidAppId":null,"paymentWarningTitle":null,"paymentWarningBody":null,"paymentiOSAppStoreIdentifier":null,"paymentiOSAppUrlScheme":null,"travelBehaviorDataCollectionEnabled":false,"enrollParticipantsInStudy":false},{"id":8,"regionName":"Lappeenranta (beta)","sidecarBaseUrl":"https://onebusaway.co","obaBaseUrl":"https://194.89.230.196:8080/","siriBaseUrl":null,"bounds":[{"lat":61.059994159164,"lon":28.197898391354,"latSpan":0.073034664706,"lonSpan":0.267485233652}],"language":"fi_FI","contactEmail":"obasupport@octo3.fi","supportsObaDiscoveryApis":true,"supportsObaRealtimeApis":true,"supportsSiriRealtimeApis":false,"active":false,"obaVersionInfo":"1.1.13|1|1|13||ef9f836500eafee955381b17799b3105b525e93b","twitterUrl":"","facebookUrl":"","experimental":true,"stopInfoUrl":null,"open311Servers":[],"otpBaseUrl":null,"otpContactEmail":null,"supportsEmbeddedSocial":false,"supportsOtpBikeshare":false,"paymentAndroidAppId":null,"paymentWarningTitle":null,"paymentWarningBody":null,"paymentiOSAppStoreIdentifier":null,"paymentiOSAppUrlScheme":null,"travelBehaviorDataCollectionEnabled":false,"enrollParticipantsInStudy":false},{"id":9,"regionName":"Rogue Valley, Oregon","sidecarBaseUrl":"https://onebusaway.co","obaBaseUrl":"https://oba.rvtd.org/onebusaway-api-webapp/","siriBaseUrl":null,"bounds":[{"lat":42.309802394046,"lon":-122.8202635,"latSpan":0.267975211908,"lonSpan":0.297453}],"language":"en_US","contactEmail":"info@rvtd.org","supportsObaDiscoveryApis":true,"supportsObaRealtimeApis":true,"supportsSiriRealtimeApis":false,"active":false,"obaVersionInfo":"","twitterUrl":"","facebookUrl":"","experimental":false,"stopInfoUrl":null,"open311Servers":[],"otpBaseUrl":null,"otpContactEmail":null,"supportsEmbeddedSocial":false,"supportsOtpBikeshare":false,"paymentAndroidAppId":null,"paymentWarningTitle":null,"paymentWarningBody":null,"paymentiOSAppStoreIdentifier":null,"paymentiOSAppUrlScheme":null,"travelBehaviorDataCollectionEnabled":false,"enrollParticipantsInStudy":false},{"id":10,"regionName":"San Joaquin RTD (beta)","sidecarBaseUrl":"https://onebusaway.co","obaBaseUrl":"https://www.obartd.com/onebusaway-api-webapp/","siriBaseUrl":null,"bounds":[{"lat":37.9337345,"lon":-121.3456095,"latSpan":0.398403,"lonSpan":0.422097}],"language":"en_US","contactEmail":"jespejo@sanjoaquinrtd.com","supportsObaDiscoveryApis":true,"supportsObaRealtimeApis":true,"supportsSiriRealtimeApis":false,"active":false,"obaVersionInfo":"1.1.12-SNAPSHOT|1|1|12|SNAPSHOT|","twitterUrl":"","facebookUrl":"","experimental":true,"stopInfoUrl":null,"open311Servers":[],"otpBaseUrl":null,"otpContactEmail":null,"supportsEmbeddedSocial":false,"supportsOtpBikeshare":false,"paymentAndroidAppId":null,"paymentWarningTitle":null,"paymentWarningBody":null,"paymentiOSAppStoreIdentifier":null,"paymentiOSAppUrlScheme":null,"travelBehaviorDataCollectionEnabled":false,"enrollParticipantsInStudy":false},{"id":11,"regionName":"San Diego","sidecarBaseUrl":"https://onebusaway.co","obaBaseUrl":"https://realtime.sdmts.com/api/","siriBaseUrl":null,"bounds":[{"lat":32.731591,"lon":-117.1896335,"latSpan":0.001164,"lonSpan":0.027427},{"lat":33.072767523113,"lon":-117.231638246234,"latSpan":0.710512073981,"lonSpan":0.723725293599},{"lat":32.899852971266,"lon":-116.731428752804,"latSpan":0.714067658917,"lonSpan":1.093941754416}],"language":"en_US","contactEmail":"customerfeedback@sdmts.com","supportsObaDiscoveryApis":true,"supportsObaRealtimeApis":true,"supportsSiriRealtimeApis":false,"active":true,"obaVersionInfo":"2.0.0-SNAPSHOT|2|0|0|SNAPSHOT|4470690f8ac2b983d758d57e4c66274a861dc014","twitterUrl":"","facebookUrl":"","experimental":false,"stopInfoUrl":null,"open311Servers":[],"otpBaseUrl":"https://realtime.sdmts.com:9091/otp","otpContactEmail":"customerfeedback@sdmts.com","supportsEmbeddedSocial":false,"supportsOtpBikeshare":false,"paymentAndroidAppId":"org.sdmts.pronto","paymentWarningTitle":null,"paymentWarningBody":null,"paymentiOSAppStoreIdentifier":"1577230742","paymentiOSAppUrlScheme":"org.sdmts.pronto","travelBehaviorDataCollectionEnabled":false,"enrollParticipantsInStudy":false},{"id":12,"regionName":"Spokane","sidecarBaseUrl":"https://onebusaway.co","obaBaseUrl":"https://www.oba4spokane.com/api/","siriBaseUrl":null,"bounds":[{"lat":47.622654,"lon":-117.397625,"latSpan":0.291966,"lonSpan":0.61626}],"language":"en_US","contactEmail":"oba4spokanetransit@gmail.com","supportsObaDiscoveryApis":true,"supportsObaRealtimeApis":true,"supportsSiriRealtimeApis":false,"active":true,"obaVersionInfo":"2.0.0-SNAPSHOT|2|0|0|SNAPSHOT|58e1687c091f6c591fca74c76c13d61a8c0dc630","twitterUrl":"","facebookUrl":"","experimental":false,"stopInfoUrl":null,"open311Servers":[],"otpBaseUrl":null,"otpContactEmail":null,"supportsEmbeddedSocial":false,"supportsOtpBikeshare":false,"paymentAndroidAppId":null,"paymentWarningTitle":null,"paymentWarningBody":null,"paymentiOSAppStoreIdentifier":null,"paymentiOSAppUrlScheme":null,"travelBehaviorDataCollectionEnabled":false,"enrollParticipantsInStudy":false},{"id":13,"regionName":"Mayaguez (beta)","sidecarBaseUrl":"https://onebusaway.co","obaBaseUrl":"https://www.proba.live/","siriBaseUrl":null,"bounds":[{"lat":18.205351924732,"lon":-67.103292785416,"latSpan":0.09467721936,"lonSpan":0.142184672684}],"language":"en_US","contactEmail":"oba.uprm@gmail.com","supportsObaDiscoveryApis":true,"supportsObaRealtimeApis":true,"supportsSiriRealtimeApis":false,"active":true,"obaVersionInfo":"2.1.0","twitterUrl":"","facebookUrl":"","experimental":true,"stopInfoUrl":null,"open311Servers":[],"otpBaseUrl":null,"otpContactEmail":null,"supportsEmbeddedSocial":false,"supportsOtpBikeshare":false,"paymentAndroidAppId":null,"paymentWarningTitle":null,"paymentWarningBody":null,"paymentiOSAppStoreIdentifier":null,"paymentiOSAppUrlScheme":null,"travelBehaviorDataCollectionEnabled":false,"enrollParticipantsInStudy":false},{"id":14,"regionName":"Victoria, Texas (beta)","sidecarBaseUrl":"https://onebusaway.co","obaBaseUrl":"https://oba.gcrpc.org/onebusaway-api-webapp","siriBaseUrl":null,"bounds":[{"lat":28.822814,"lon":-96.9861015,"latSpan":0.109128,"lonSpan":0.072009}],"language":"en_US","contactEmail":"vtxtransit@gcrpc.org","supportsObaDiscoveryApis":true,"supportsObaRealtimeApis":true,"supportsSiriRealtimeApis":false,"active":true,"obaVersionInfo":"2.0.0-SNAPSHOT|2|0|0|SNAPSHOT|","twitterUrl":"https://mobile.twitter.com/Vtx_transit","facebookUrl":"https://www.facebook.com/GCRPC","experimental":true,"stopInfoUrl":null,"open311Servers":[],"otpBaseUrl":null,"otpContactEmail":null,"supportsEmbeddedSocial":false,"supportsOtpBikeshare":false,"paymentAndroidAppId":null,"paymentWarningTitle":null,"paymentWarningBody":null,"paymentiOSAppStoreIdentifier":null,"paymentiOSAppUrlScheme":null,"travelBehaviorDataCollectionEnabled":false,"enrollParticipantsInStudy":false},{"id":15,"regionName":"Adelaide Metro","sidecarBaseUrl":"https://onebusaway.co","obaBaseUrl":"https://transit.nautilus-tech.com.au/onebusaway-api-webapp/","siriBaseUrl":null,"bounds":[{"lat":-34.89224,"lon":138.615665,"latSpan":0.595454,"lonSpan":0.264442},{"lat":-34.9438135,"lon":138.5605365,"latSpan":0.073201,"lonSpan":0.097425},{"lat":-35.096154776118,"lon":138.8118255,"latSpan":0.399942447764,"lonSpan":0.448523},{"lat":-34.833098,"lon":138.621111224813,"latSpan":0.52411,"lonSpan":0.285071550373},{"lat":-35.128531,"lon":138.529113,"latSpan":0.421384,"lonSpan":0.167594},{"lat":-35.1519865,"lon":138.535168737546,"latSpan":0.374473,"lonSpan":0.179082524908},{"lat":-34.8541185,"lon":138.6073665,"latSpan":0.444403,"lonSpan":0.257747},{"lat":-34.991378,"lon":138.751375265954,"latSpan":0.185458,"lonSpan":0.329393468091},{"lat":-34.932278,"lon":138.6044295,"latSpan":0.170316,"lonSpan":0.115521}],"language":"en_AU","contactEmail":"transit@nautilus-tech.com.au","supportsObaDiscoveryApis":true,"supportsObaRealtimeApis":true,"supportsSiriRealtimeApis":false,"active":true,"obaVersionInfo":"2.1.16|2|1|16|c2155acf754a4dc81ca6c03654dc7409bc5de45f","twitterUrl":"","facebookUrl":"","experimental":false,"stopInfoUrl":null,"open311Servers":[],"otpBaseUrl":"https://otp.nautilus-tech.com.au/otp/","otpContactEmail":"transit@nautilus-tech.com.au","supportsEmbeddedSocial":false,"supportsOtpBikeshare":false,"paymentAndroidAppId":null,"paymentWarningTitle":null,"paymentWarningBody":null,"paymentiOSAppStoreIdentifier":null,"paymentiOSAppUrlScheme":null,"travelBehaviorDataCollectionEnabled":false,"enrollParticipantsInStudy":false},{"id":16,"regionName":"Davis, CA","sidecarBaseUrl":"https://onebusaway.co","obaBaseUrl":"https://unitrans-api.server.onebusawaycloud.com","siriBaseUrl":null,"bounds":[{"lat":38.5449155,"lon":-121.7443553,"latSpan":0.32,"lonSpan":0.32}],"language":"en_US","contactEmail":"unitrans@ucdavis.edu","supportsObaDiscoveryApis":true,"supportsObaRealtimeApis":true,"supportsSiriRealtimeApis":false,"active":true,"obaVersionInfo":"2.5.13-otsf|2|5|13|otsf|8628b8530d842a2f0414160913ddd0ff11c77005","twitterUrl":"","facebookUrl":"","experimental":true,"stopInfoUrl":null,"open311Servers":[{"juridisctionId":"","apiKey":"","baseUrl":""}],"otpBaseUrl":null,"otpContactEmail":null,"supportsEmbeddedSocial":false,"supportsOtpBikeshare":false,"paymentAndroidAppId":null,"paymentWarningTitle":null,"paymentWarningBody":null,"paymentiOSAppStoreIdentifier":null,"paymentiOSAppUrlScheme":null,"travelBehaviorDataCollectionEnabled":false,"enrollParticipantsInStudy":false}]}} \ No newline at end of file diff --git a/README.markdown b/README.markdown index 3e1be2f90..c5cb52ae2 100644 --- a/README.markdown +++ b/README.markdown @@ -90,6 +90,12 @@ Now, replace the file `OBAKitCore/Models/Protobuf/gtfs-realtime.proto` with the You can find the latest version of the GTFS-RT protobuf file in the https://github.com/google/transit/ repository. +### Update macOS/Xcode versions for GitHub Actions CI + +1. Find the latest version of macOS and Xcode here: https://github.com/actions/runner-images/tree/main/images/macos +2. Update the `.github/obakittests.yml` file accordingly. +3. [There's no step 3](https://www.youtube.com/watch?v=ihOxl1v5BRk). + ### Fix "Missing package product" build errors In Xcode: File Menu -> Packages -> Reset Package Caches