Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes view controller retain cycles #395

Merged
merged 3 commits into from
Jun 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ extension AgencyAlertListViewConverters where Self: UIViewController {
func listSections(agencyAlerts: [AgencyAlert]) -> [OBAListViewSection] {
let groupedAlerts = Dictionary(grouping: agencyAlerts, by: { $0.agency?.agency.name ?? "" })
return groupedAlerts.map { group -> OBAListViewSection in
let presentAlertAction: OBAListViewAction<TransitAlertDataListViewModel> = { [weak self] item in self?.presentAlert(item) }
let viewModels: [TransitAlertDataListViewModel] = group.value.map { alert -> TransitAlertDataListViewModel in
let isUnread = application.alertsStore.isAlertUnread(alert)
return TransitAlertDataListViewModel(alert, isUnread: isUnread, forLocale: Locale.current, onSelectAction: presentAlert)
return TransitAlertDataListViewModel(alert, isUnread: isUnread, forLocale: Locale.current, onSelectAction: presentAlertAction)
}

let alerts = viewModels.uniqued.sorted(by: \.title) // remove duplicates
Expand All @@ -45,7 +46,8 @@ extension AgencyAlertListViewConverters where Self: UIViewController {
/// - sectionID: The section ID to use with `OBAListViewSection`. The default value is `service_alerts`.
/// - Returns: An `OBAListViewSection` representing the array of `ServiceAlert`s for use with OBAListView.
func listSection(serviceAlerts: [ServiceAlert], showSectionTitle: Bool, sectionID: String = "service_alerts") -> OBAListViewSection {
let items = serviceAlerts.map { TransitAlertDataListViewModel($0, isUnread: false, forLocale: .current, onSelectAction: presentAlert) }.uniqued
let onSelectAction: OBAListViewAction<TransitAlertDataListViewModel> = { [weak self] item in self?.presentAlert(item) }
let items = serviceAlerts.map { TransitAlertDataListViewModel($0, isUnread: false, forLocale: .current, onSelectAction: onSelectAction) }.uniqued
let title: String?
if showSectionTitle {
if items.count > 1 {
Expand Down
28 changes: 18 additions & 10 deletions OBAKit/Stops/StopViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,9 @@ public class StopViewController: UIViewController,

registerDefaults()

Timer.scheduledTimer(timeInterval: StopViewController.defaultTimerReloadInterval / 2.0, target: self, selector: #selector(timerFired), userInfo: nil, repeats: true)
reloadTimer = Timer.scheduledTimer(withTimeInterval: StopViewController.defaultTimerReloadInterval / 2.0, repeats: true) { [weak self] _ in
self?.timerFired()
}

navigationItem.backBarButtonItem = UIBarButtonItem.backButton
}
Expand Down Expand Up @@ -502,15 +504,21 @@ public class StopViewController: UIViewController,
let alarmAvailable = canCreateAlarm(for: arrivalDeparture)
let deepLinkingAvailable = application.features.deepLinking == .running
let highlightTimeOnDisplay = shouldHighlight(arrivalDeparture: arrivalDeparture)

let onSelectAction: OBAListViewAction<ArrivalDepartureItem> = { [unowned self] item in self.didSelectArrivalDepartureItem(item) }
let addAlarmAction: OBAListViewAction<ArrivalDepartureItem> = { [unowned self] item in self.addAlarm(viewModel: item) }
let bookmarkAction: OBAListViewAction<ArrivalDepartureItem> = { [unowned self] item in self.addBookmark(viewModel: item) }
let shareAction: OBAListViewAction<ArrivalDepartureItem> = { [unowned self] item in self.shareTripStatus(viewModel: item) }

return ArrivalDepartureItem(
arrivalDeparture: arrivalDeparture,
isAlarmAvailable: alarmAvailable,
isDeepLinkingAvailable: deepLinkingAvailable,
highlightTimeOnDisplay: highlightTimeOnDisplay,
onSelectAction: didSelectArrivalDepartureItem,
alarmAction: addAlarm,
bookmarkAction: addBookmark,
shareAction: shareTripStatus)
onSelectAction: onSelectAction,
alarmAction: addAlarmAction,
bookmarkAction: bookmarkAction,
shareAction: shareAction)
}

func sectionForGroup(groupRoute: Route?, showSectionHeader: Bool, arrDeps: [ArrivalDeparture]) -> OBAListViewSection {
Expand Down Expand Up @@ -565,22 +573,22 @@ public class StopViewController: UIViewController,
self.performPreviewStopArrival(viewModel)
}

let menuProvider: OBAListViewMenuActions.MenuProvider = { _ in
let menuProvider: OBAListViewMenuActions.MenuProvider = { [unowned self] _ in
var actions = [UIAction]()

if viewModel.isAlarmAvailable {
let alarm = UIAction(title: Strings.addAlarm, image: Icons.addAlarm) { _ in
let alarm = UIAction(title: Strings.addAlarm, image: Icons.addAlarm) { [unowned self] _ in
self.addAlarm(viewModel: viewModel)
}
actions.append(alarm)
}

let addBookmark = UIAction(title: Strings.addBookmark, image: Icons.addBookmark) { _ in
let addBookmark = UIAction(title: Strings.addBookmark, image: Icons.addBookmark) { [unowned self] _ in
self.addBookmark(viewModel: viewModel)
}
actions.append(addBookmark)

let shareTrip = UIAction(title: Strings.shareTrip, image: UIImage(systemName: "square.and.arrow.up")) { _ in
let shareTrip = UIAction(title: Strings.shareTrip, image: UIImage(systemName: "square.and.arrow.up")) { [unowned self] _ in
self.shareTripStatus(viewModel: viewModel)
}
actions.append(shareTrip)
Expand Down Expand Up @@ -734,7 +742,7 @@ public class StopViewController: UIViewController,

// All Service Alerts
if let alerts = stopArrivals?.serviceAlerts, alerts.count > 0 {
let row = OBAListRowView.DefaultViewModel(title: Strings.serviceAlerts, accessoryType: .disclosureIndicator) { _ in
let row = OBAListRowView.DefaultViewModel(title: Strings.serviceAlerts, accessoryType: .disclosureIndicator) { [unowned self] _ in
let controller = ServiceAlertListController(application: self.application, serviceAlerts: alerts)
self.application.viewRouter.navigate(to: controller, from: self)
}
Expand Down
22 changes: 12 additions & 10 deletions OBAKit/Trip/TripFloatingPanelController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ class TripFloatingPanelController: UIViewController,
}

private func serviceAlertsListSection(_ alerts: [ServiceAlert]) -> OBAListViewSection {
let action: OBAListViewAction<TransitAlertDataListViewModel> = { viewModel in
let action: OBAListViewAction<TransitAlertDataListViewModel> = { [unowned self] viewModel in
self.application.viewRouter.navigateTo(alert: viewModel.transitAlert, from: self)
}

Expand All @@ -275,19 +275,21 @@ class TripFloatingPanelController: UIViewController,

private func tripStopListSection(tripDetails: TripDetails, arrivalDeparture: ArrivalDeparture?, showHeader: Bool) -> OBAListViewSection {
var contents: [AnyOBAListViewItem] = []
let selectAdjacentTripAction: OBAListViewAction<AdjacentTripItem> = { [unowned self] item in self.onSelectAdjacentTrip(item) }

// Previous trip, if any.
if let previousTrip = tripDetails.previousTrip {
contents.append(AdjacentTripItem(order: .previous, trip: previousTrip, onSelectAction: onSelectAdjacentTrip).typeErased)
contents.append(AdjacentTripItem(order: .previous, trip: previousTrip, onSelectAction: selectAdjacentTripAction).typeErased)
}

// Stop times
let stopTimes: [AnyOBAListViewItem] = tripDetails.stopTimes.map { TripStopViewModel(stopTime: $0, arrivalDeparture: arrivalDeparture, onSelectAction: onSelectTripStop).typeErased }
let selectTripStopAction: OBAListViewAction<TripStopViewModel> = { [unowned self] item in self.onSelectTripStop(item) }
let stopTimes: [AnyOBAListViewItem] = tripDetails.stopTimes.map { TripStopViewModel(stopTime: $0, arrivalDeparture: arrivalDeparture, onSelectAction: selectTripStopAction).typeErased }
contents.append(contentsOf: stopTimes)

// Next trip, if any.
if let nextTrip = tripDetails.nextTrip {
contents.append(AdjacentTripItem(order: .next, trip: nextTrip, onSelectAction: onSelectAdjacentTrip).typeErased)
contents.append(AdjacentTripItem(order: .next, trip: nextTrip, onSelectAction: selectAdjacentTripAction).typeErased)
}

let title: String? = showHeader ? OBALoc("trip_details_controller.service_alerts_footer", value: "Trip Details", comment: "Service alerts header in the trip details controller.") : nil
Expand All @@ -298,15 +300,15 @@ class TripFloatingPanelController: UIViewController,
private func viewOnMapAction(for viewModel: TripStopViewModel) -> UIAction? {
guard parentTripViewController != nil else { return nil }

return UIAction(title: OBALoc("trip_details_controller.show_on_map", value: "Show on Map", comment: "Button that moves the map to focus on the selected stop"), image: UIImage(systemName: "mappin.circle")) { _ in
return UIAction(title: OBALoc("trip_details_controller.show_on_map", value: "Show on Map", comment: "Button that moves the map to focus on the selected stop"), image: UIImage(systemName: "mappin.circle")) { [unowned self] _ in
self.showOnMap(viewModel)
}
}

private func getWalkingDirections(for viewModel: TripStopViewModel) -> UIMenuElement? {
let appleMapsAction: UIAction?
if let appleMapsURL = AppInterop.appleMapsWalkingDirectionsURL(coordinate: viewModel.stop.coordinate) {
appleMapsAction = UIAction(title: OBALoc("stops_controller.walking_directions_apple", value: "Walking Directions (Apple Maps)", comment: "Button that launches Apple's maps.app with walking directions to this stop")) { _ in
appleMapsAction = UIAction(title: OBALoc("stops_controller.walking_directions_apple", value: "Walking Directions (Apple Maps)", comment: "Button that launches Apple's maps.app with walking directions to this stop")) { [unowned self] _ in
self.application.open(appleMapsURL, options: [:], completionHandler: nil)
}
} else {
Expand All @@ -316,7 +318,7 @@ class TripFloatingPanelController: UIViewController,
let googleMapsAction: UIAction?
#if !targetEnvironment(simulator)
if let googleMapsURL = AppInterop.googleMapsWalkingDirectionsURL(coordinate: viewModel.stop.coordinate) {
googleMapsAction = UIAction(title: OBALoc("stops_controller.walking_directions_google", value: "Walking Directions (Google Maps)", comment: "Button that launches Google Maps with walking directions to this stop")) { _ in
googleMapsAction = UIAction(title: OBALoc("stops_controller.walking_directions_google", value: "Walking Directions (Google Maps)", comment: "Button that launches Google Maps with walking directions to this stop")) { [unowned self] _ in
self.application.open(googleMapsURL, options: [:], completionHandler: nil)
}
} else {
Expand All @@ -336,7 +338,7 @@ class TripFloatingPanelController: UIViewController,
func contextMenu(_ listView: OBAListView, for item: AnyOBAListViewItem) -> OBAListViewMenuActions? {
guard let tripStop = item.as(TripStopViewModel.self) else { return nil }

let menu: OBAListViewMenuActions.MenuProvider = { _ -> UIMenu? in
let menu: OBAListViewMenuActions.MenuProvider = { [unowned self] _ -> UIMenu? in
let menuActions = [
self.viewOnMapAction(for: tripStop),
self.getWalkingDirections(for: tripStop)
Expand All @@ -345,13 +347,13 @@ class TripFloatingPanelController: UIViewController,
return UIMenu(title: tripStop.title, children: menuActions)
}

let previewProvider: OBAListViewMenuActions.PreviewProvider = { () -> UIViewController? in
let previewProvider: OBAListViewMenuActions.PreviewProvider = { [unowned self] () -> UIViewController? in
let stopVC = StopViewController(application: self.application, stopID: tripStop.stop.id)
self.currentPreviewingViewController = stopVC
return stopVC
}

let commitPreviewAction: VoidBlock = {
let commitPreviewAction: VoidBlock = { [unowned self] in
guard let vc = self.currentPreviewingViewController else { return }
(vc as? Previewable)?.exitPreviewMode()
self.application.viewRouter.navigate(to: vc, from: self)
Expand Down