From ababa4b42847a7481ab9bcff8f07e1bbb3887f8d Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sun, 15 Jan 2023 11:30:34 -0500 Subject: [PATCH] Add more logging around state restoration crash --- .../Timeline/TimelineViewController.swift | 59 +++++++++++++++---- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/Tusker/Screens/Timeline/TimelineViewController.swift b/Tusker/Screens/Timeline/TimelineViewController.swift index c63a8db3..4c1ac563 100644 --- a/Tusker/Screens/Timeline/TimelineViewController.swift +++ b/Tusker/Screens/Timeline/TimelineViewController.swift @@ -9,6 +9,7 @@ import UIKit import Pachyderm import Combine +import Sentry class TimelineViewController: UIViewController, TimelineLikeCollectionViewController, CollectionViewController, RefreshableViewController { let timeline: Timeline @@ -335,7 +336,7 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro } loadViewIfNeeded() var loaded = false - await controller.restoreInitial { + await controller.restoreInitial { @MainActor in let hasStatusesToRestore = await loadStatusesToRestore(position: position) if hasStatusesToRestore { applyItemsToRestore(position: position) @@ -351,26 +352,45 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro guard !unloaded.isEmpty else { return true } - let statuses = await withTaskGroup(of: Status?.self) { group -> [Status] in + let results = await withTaskGroup(of: (String, Result).self) { group -> [(String, Result)] in for id in unloaded { group.addTask { do { let (status, _) = try await self.mastodonController.run(Client.getStatus(id: id)) - return status + return (id, .success(status)) } catch { - print(error) - return nil + return (id, .failure(error)) } } } - return await group.reduce(into: []) { partialResult, status in - if let status { - partialResult.append(status) - } + return await group.reduce(into: []) { partialResult, result in + partialResult.append(result) + } + } + var statuses = [Status]() + for (id, result) in results { + switch result { + case .success(let status): + statuses.append(status) + case .failure(let error): + let crumb = Breadcrumb(level: .error, category: "TimelineViewController") + crumb.message = "Error loading status" + crumb.data = [ + "error": String(describing: error), + "id": id + ] + SentrySDK.addBreadcrumb(crumb: crumb) } } await mastodonController.persistentContainer.addAll(statuses: statuses, in: mastodonController.persistentContainer.viewContext) + let crumb = Breadcrumb(level: .info, category: "TimelineViewController") + crumb.message = "Original position statusIDs" + crumb.data = [ + "statusIDs": position.statusIDs, + ] + SentrySDK.addBreadcrumb(crumb: crumb) + // update the timeline position in case some statuses couldn't be loaded if let center = position.centerStatusID { let nearestLoadedStatusToCenter = position.statusIDs[position.statusIDs.firstIndex(of: center)!...].first(where: { id in @@ -383,9 +403,17 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro !unloaded.contains(id) || statuses.contains(where: { $0.id == id }) } + let crumb2 = Breadcrumb(level: .info, category: "TimelineViewController") + crumb2.message = "Filtered position statusIDs" + crumb2.data = [ + "statusIDs": position.statusIDs, + ] + SentrySDK.addBreadcrumb(crumb: crumb2) + return !position.statusIDs.isEmpty } + @MainActor private func applyItemsToRestore(position: TimelinePosition) { var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.statuses]) @@ -393,6 +421,12 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro let centerStatusID = position.centerStatusID let items = position.statusIDs.map { Item.status(id: $0, collapseState: .unknown, filterState: .unknown) } snapshot.appendItems(items, toSection: .statuses) + let crumb = Breadcrumb(level: .info, category: "TimelineViewController") + crumb.message = "Restoring statuses" + crumb.data = [ + "statusIDs": position.statusIDs + ] + SentrySDK.addBreadcrumb(crumb: crumb) dataSource.apply(snapshot, animatingDifferences: false) { if let centerStatusID, let index = statusIDs.firstIndex(of: centerStatusID), @@ -426,7 +460,12 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro private func filterResult(state: FilterState, statusID: String) -> (Filterer.Result, NSAttributedString?) { let status = { - let status = self.mastodonController.persistentContainer.status(for: statusID)! + guard let status = self.mastodonController.persistentContainer.status(for: statusID) else { + let crumb = Breadcrumb(level: .fatal, category: "TimelineViewController") + crumb.message = "Looking up status \(statusID)" + SentrySDK.addBreadcrumb(crumb: crumb) + preconditionFailure("Missing status for filtering") + } // if the status is a reblog of another one, filter based on that one if let reblogged = status.reblog { return (reblogged, true)