Skip to content

Commit

Permalink
Merge pull request #658 from OneBusAway/ualch9/PR-635
Browse files Browse the repository at this point in the history
Fixes some concurrency related crashes
  • Loading branch information
ualch9 authored Mar 30, 2023
2 parents d0c459d + f5dfd28 commit 1be686b
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 47 deletions.
13 changes: 8 additions & 5 deletions OBAKit/Controls/AlertPresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,25 @@ class AlertPresenter: NSObject {
/// Displays an error message to the user
/// - Parameter error: The error to show to the user.
/// - Parameter presentingController: The view controller that will act as the host for the presented error alert UI.
public class func show(error: Error, presentingController: UIViewController) {
show(errorMessage: error.localizedDescription, presentingController: presentingController)
@MainActor
public class func show(error: Error, presentingController: UIViewController) async {
await show(errorMessage: error.localizedDescription, presentingController: presentingController)
}

/// Displays an error message to the user.
/// - Parameter errorMessage: The error message that will be shown.
/// - Parameter presentingController: The view controller that will act as the host for the presented error alert UI.
public class func show(errorMessage: String, presentingController: UIViewController) {
showDismissableAlert(title: Strings.error, message: errorMessage, presentingController: presentingController)
@MainActor
public class func show(errorMessage: String, presentingController: UIViewController) async {
await showDismissableAlert(title: Strings.error, message: errorMessage, presentingController: presentingController)
}

/// Displays an alert with a Dismiss button, presented from `presentingController`.
/// - Parameter title: Optional alert title.
/// - Parameter message: Optional alert message.
/// - Parameter presentingController: The presenting view controller.
public class func showDismissableAlert(title: String?, message: String?, presentingController: UIViewController) {
@MainActor
public class func showDismissableAlert(title: String?, message: String?, presentingController: UIViewController) async {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: Strings.dismiss, style: .default, handler: nil))
presentingController.present(alert, animated: true, completion: nil)
Expand Down
11 changes: 8 additions & 3 deletions OBAKit/Mapping/MapRegionManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,9 @@ public class MapRegionManager: NSObject,

mapView.delegate = self

renderRegionsOnMap()
Task { @MainActor [weak self] in
await self?.renderRegionsOnMap()
}
}

deinit {
Expand Down Expand Up @@ -632,14 +634,17 @@ public class MapRegionManager: NSObject,
// MARK: - Regions

public func regionsService(_ service: RegionsService, updatedRegionsList regions: [Region]) {
renderRegionsOnMap()
Task { @MainActor [weak self] in
await self?.renderRegionsOnMap()
}
}

public func regionsService(_ service: RegionsService, updatedRegion region: Region) {
mapView.setVisibleMapRect(region.serviceRect, animated: true)
}

private func renderRegionsOnMap() {
@MainActor
private func renderRegionsOnMap() async {
mapView.updateAnnotations(with: application.regionsService.regions)
}
}
Expand Down
63 changes: 36 additions & 27 deletions OBAKit/Mapping/MapViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -520,40 +520,49 @@ class MapViewController: UIViewController,
}

public func mapRegionManager(_ manager: MapRegionManager, noSearchResults response: SearchResponse) {
AlertPresenter.show(errorMessage: OBALoc("map_controller.no_search_results_found", value: "No search results were found.", comment: "A generic message shown when the user's search query produces no search results."), presentingController: self)
Task { @MainActor [weak self] in
guard let self else { return }
await AlertPresenter.show(errorMessage: OBALoc("map_controller.no_search_results_found", value: "No search results were found.", comment: "A generic message shown when the user's search query produces no search results."), presentingController: self)
}
}

public func mapRegionManager(_ manager: MapRegionManager, disambiguateSearch response: SearchResponse) {
let searchResults = SearchResultsController(searchResponse: response, application: application, delegate: self)
let nav = UINavigationController(rootViewController: searchResults)
application.viewRouter.present(nav, from: self, isModal: true)
Task { @MainActor [weak self] in
guard let self else { return }
let searchResults = SearchResultsController(searchResponse: response, application: application, delegate: self)
let nav = UINavigationController(rootViewController: searchResults)
application.viewRouter.present(nav, from: self, isModal: true)
}
}

@MainActor
public func mapRegionManager(_ manager: MapRegionManager, showSearchResult response: SearchResponse) {
guard let result = response.results.first else { return }

statusOverlay.isHidden = true

switch result {
case let result as MKMapItem:
let mapItemController = MapItemViewController(application: application, mapItem: result, delegate: self)
showSemiModalPanel(childController: mapItemController)
case let result as StopsForRoute:
let routeStopController = RouteStopsViewController(application: application, stopsForRoute: result, delegate: self)
showSemiModalPanel(childController: routeStopController)
case let result as Stop:
show(stop: result)
case let result as VehicleStatus:
if let convertible = TripConvertible(vehicleStatus: result) {
let tripController = TripViewController(application: application, tripConvertible: convertible)
application.viewRouter.navigate(to: tripController, from: self)
}
else {
let msg = OBALoc("map_controller.vehicle_not_on_trip_error", value: "The vehicle you chose doesn't appear to be on a trip right now, which means we don't know how to show it to you.", comment: "This message appears when a searched-for vehicle doesn't have an assigned trip.")
AlertPresenter.show(errorMessage: msg, presentingController: self)
Task { @MainActor [weak self] in
guard let self, let result = response.results.first else { return }

statusOverlay.isHidden = true

switch result {
case let result as MKMapItem:
let mapItemController = MapItemViewController(application: application, mapItem: result, delegate: self)
showSemiModalPanel(childController: mapItemController)
case let result as StopsForRoute:
let routeStopController = RouteStopsViewController(application: application, stopsForRoute: result, delegate: self)
showSemiModalPanel(childController: routeStopController)
case let result as Stop:
show(stop: result)
case let result as VehicleStatus:
if let convertible = TripConvertible(vehicleStatus: result) {
let tripController = TripViewController(application: application, tripConvertible: convertible)
application.viewRouter.navigate(to: tripController, from: self)
}
else {
let msg = OBALoc("map_controller.vehicle_not_on_trip_error", value: "The vehicle you chose doesn't appear to be on a trip right now, which means we don't know how to show it to you.", comment: "This message appears when a searched-for vehicle doesn't have an assigned trip.")
await AlertPresenter.show(errorMessage: msg, presentingController: self)
}
default:
fatalError()
}
default:
fatalError()
}
}

Expand Down
6 changes: 4 additions & 2 deletions OBAKit/PushNotifications/PushService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,10 @@ public class PushService: NSObject {
}

private func displayMessage(_ message: String) {
if let presentingController = delegate?.pushServicePresentingController(self) {
AlertPresenter.showDismissableAlert(title: message, message: nil, presentingController: presentingController)
Task { @MainActor in
if let presentingController = delegate?.pushServicePresentingController(self) {
await AlertPresenter.showDismissableAlert(title: message, message: nil, presentingController: presentingController)
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion OBAKit/Reporting/StopProblemViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ class StopProblemViewController: FormViewController {
} catch {
await MainActor.run {
ProgressHUD.dismiss()
AlertPresenter.show(error: error, presentingController: self)
}
await AlertPresenter.show(error: error, presentingController: self)
}
}
}
2 changes: 1 addition & 1 deletion OBAKit/Reporting/VehicleProblemViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ class VehicleProblemViewController: FormViewController {
} catch {
await MainActor.run {
ProgressHUD.dismiss()
AlertPresenter.show(error: error, presentingController: self)
}
await AlertPresenter.show(error: error, presentingController: self)
}
}
}
10 changes: 8 additions & 2 deletions OBAKit/Settings/MoreViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,10 @@ public class MoreViewController: UIViewController,
controller.dismiss(animated: true, completion: nil)

if let error = error {
AlertPresenter.show(error: error, presentingController: self)
Task { @MainActor [weak self] in
guard let self else { return }
await AlertPresenter.show(error: error, presentingController: self)
}
}
}

Expand Down Expand Up @@ -305,7 +308,10 @@ public class MoreViewController: UIViewController,
}

public func farePayments(_ farePayments: FarePayments, present error: Error) {
AlertPresenter.show(error: error, presentingController: self)
Task { @MainActor [weak self] in
guard let self else { return }
await AlertPresenter.show(error: error, presentingController: self)
}
}

// MARK: - Regions Service Delegate
Expand Down
5 changes: 4 additions & 1 deletion OBAKit/Settings/SettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,10 @@ class SettingsViewController: FormViewController {
let activity = UIActivityViewController(activityItems: [xmlPath], applicationActivities: nil)
self.present(activity, animated: true, completion: nil)
} catch let ex {
AlertPresenter.show(error: ex, presentingController: self)
Task { @MainActor [weak self] in
guard let self else { return }
await AlertPresenter.show(error: ex, presentingController: self)
}
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion OBAKit/Stops/StopViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -966,7 +966,9 @@ public class StopViewController: UIViewController,

func alarmBuilder(_ alarmBuilder: AlarmBuilder, error: Error) {
ProgressHUD.dismiss()
AlertPresenter.show(error: error, presentingController: self)
Task { @MainActor in
await AlertPresenter.show(error: error, presentingController: self)
}
}

// MARK: - Bookmarks
Expand Down
5 changes: 1 addition & 4 deletions OBAKitCore/Location/Regions/RegionsService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -345,10 +345,7 @@ public class RegionsService: NSObject, LocationServiceDelegate {
return
}

// FIXME: Audit which delegates are doing stuff on background thread, when they should be on Main.
await MainActor.run {
self.regions = regions
}
self.regions = regions
}

/// Fetches the current list of `Region`s from the network.
Expand Down

0 comments on commit 1be686b

Please sign in to comment.