Prevent sync errors from crashing the app

This commit is contained in:
Shadowfacts 2022-01-12 13:56:03 -05:00
parent 9deafa4b33
commit be788bd0a6
4 changed files with 58 additions and 15 deletions

View File

@ -19,6 +19,7 @@ extension Item {
@NSManaged public var author: String?
@NSManaged public var content: String?
@NSManaged public var id: String?
@NSManaged public var needsReadStateSync: Bool
@NSManaged public var published: Date?
@NSManaged public var read: Bool
@NSManaged public var title: String?

View File

@ -17,6 +17,7 @@
<attribute name="author" optional="YES" attributeType="String"/>
<attribute name="content" optional="YES" attributeType="String"/>
<attribute name="id" attributeType="String"/>
<attribute name="needsReadStateSync" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="published" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="read" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="title" optional="YES" attributeType="String"/>
@ -29,7 +30,7 @@
<elements>
<element name="Feed" positionX="-54" positionY="9" width="128" height="119"/>
<element name="Group" positionX="-63" positionY="-18" width="128" height="74"/>
<element name="Item" positionX="-45" positionY="63" width="128" height="149"/>
<element name="Item" positionX="-45" positionY="63" width="128" height="164"/>
<element name="SyncState" positionX="-63" positionY="90" width="128" height="44"/>
</elements>
</model>

View File

@ -55,29 +55,59 @@ class FervorController {
accessToken = token.accessToken
}
func syncAll() async {
func syncAll() async throws {
logger.info("Syncing groups and feeds")
async let groups = try! client.groups()
async let feeds = try! client.feeds()
try! await persistentContainer.sync(serverGroups: groups, serverFeeds: feeds)
async let groups = try client.groups()
async let feeds = try client.feeds()
try await persistentContainer.sync(serverGroups: groups, serverFeeds: feeds)
let lastSync = try! await persistentContainer.lastSyncDate()
let lastSync = try await persistentContainer.lastSyncDate()
logger.info("Syncing items with last sync date: \(String(describing: lastSync), privacy: .public)")
let update = try! await client.syncItems(lastSync: lastSync)
try! await persistentContainer.syncItems(update)
try! await persistentContainer.updateLastSyncDate(update.syncTimestamp)
let update = try await client.syncItems(lastSync: lastSync)
try await persistentContainer.syncItems(update)
try await persistentContainer.updateLastSyncDate(update.syncTimestamp)
}
@MainActor
func syncReadToServer() async throws {
var count = 0
for case let item as Item in self.persistentContainer.viewContext.updatedObjects {
count += 1
// todo: there should be a batch update api endpoint
for case let item as Item in persistentContainer.viewContext.updatedObjects {
let f = item.read ? client.read(item:) : client.unread(item:)
let _ = try await f(item.id!)
do {
let _ = try await f(item.id!)
count += 1
} catch {
logger.error("Failed to sync read state: \(error.localizedDescription, privacy: .public)")
item.needsReadStateSync = true
}
}
// try to sync items which failed last time
let req = Item.fetchRequest()
req.predicate = NSPredicate(format: "needsReadStateSync = YES")
if let needsSync = try? persistentContainer.viewContext.fetch(req) {
for item in needsSync {
let f = item.read ? client.read(item:) : client.unread(item:)
do {
let _ = try await f(item.id!)
count += 1
item.needsReadStateSync = false
} catch {
logger.error("Failed to sync read state again: \(error.localizedDescription, privacy: .public)")
item.needsReadStateSync = true
// todo: this should probably fail after a certain number of attempts
}
}
}
logger.info("Synced \(count, privacy: .public) read/unread to server")
try self.persistentContainer.viewContext.save()
do {
try persistentContainer.viewContext.save()
} catch {
logger.error("Failed to save view context: \(error.localizedDescription, privacy: .public)")
}
}
}

View File

@ -6,12 +6,15 @@
//
import UIKit
import OSLog
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
private(set) var fervorController: FervorController!
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "SceneDelegate")
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
@ -51,7 +54,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// This may occur due to temporary interruptions (ex. an incoming phone call).
Task(priority: .userInitiated) {
try await self.fervorController?.syncReadToServer()
do {
try await self.fervorController?.syncReadToServer()
} catch {
logger.error("Unable to sync read state to server: \(error.localizedDescription, privacy: .public)")
}
}
}
@ -74,7 +81,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
window!.rootViewController = nav
Task(priority: .userInitiated) {
await self.fervorController.syncAll()
do {
try await self.fervorController.syncAll()
} catch {
logger.error("Unable to sync from server: \(error.localizedDescription, privacy: .public)")
}
}
}