Cache active account ID in CoreData

See #251
This commit is contained in:
Shadowfacts 2023-05-28 22:23:04 -07:00
parent cb5b70a23a
commit da88303a22
4 changed files with 36 additions and 12 deletions

View File

@ -23,6 +23,7 @@ class MastodonController: ObservableObject {
@available(*, message: "do something less dumb")
static var first: MastodonController { all.first!.value }
@MainActor
static func getForAccount(_ account: UserAccountInfo) -> MastodonController {
if let controller = all[account] {
return controller
@ -51,6 +52,7 @@ class MastodonController: ObservableObject {
let client: Client!
let instanceFeatures = InstanceFeatures()
@Published private(set) var accountID: String?
@Published private(set) var account: Account!
@Published private(set) var instance: Instance?
@Published private(set) var instanceInfo: InstanceInfo!
@ -69,6 +71,8 @@ class MastodonController: ObservableObject {
accountInfo != nil
}
// main-actor b/c fetchActiveAccountID and fetchActiveInstance use the viewContext
@MainActor
init(instanceURL: URL, accountInfo: UserAccountInfo?) {
self.instanceURL = instanceURL
self.accountInfo = accountInfo
@ -81,6 +85,7 @@ class MastodonController: ObservableObject {
self.client.accessToken = accountInfo?.accessToken
if !transient {
fetchActiveAccountID()
fetchActiveInstance()
}
@ -119,6 +124,7 @@ class MastodonController: ObservableObject {
.store(in: &cancellables)
}
@MainActor
convenience init(instanceURL: URL, transient: Bool) {
precondition(transient, "account info must be provided if transient is false")
self.init(instanceURL: instanceURL, accountInfo: nil)
@ -242,13 +248,14 @@ class MastodonController: ObservableObject {
DispatchQueue.main.async {
self.account = account
}
self.persistentContainer.backgroundContext.perform {
if let accountMO = self.persistentContainer.account(for: account.id, in: self.persistentContainer.backgroundContext) {
let context = self.persistentContainer.backgroundContext
context.perform {
if let accountMO = self.persistentContainer.account(for: account.id, in: context) {
accountMO.updateFrom(apiAccount: account, container: self.persistentContainer)
accountMO.active = true
} else {
// the first time the user's account is added to the store,
// increment its reference count so that it's never removed
self.persistentContainer.addOrUpdate(account: account)
let account = self.persistentContainer.addOrUpdateSynchronously(account: account, in: context)
account.active = true
}
completion?(.success(account))
}
@ -367,14 +374,20 @@ class MastodonController: ObservableObject {
}
}
@MainActor
private func fetchActiveAccountID() {
let req = AccountMO.fetchRequest()
req.predicate = NSPredicate(format: "active = YES")
if let activeAccount = try? persistentContainer.viewContext.fetch(req).first {
accountID = activeAccount.id
}
}
@MainActor
private func fetchActiveInstance() {
persistentContainer.performBackgroundTask { context in
if let activeInstance = try? context.fetch(ActiveInstance.fetchRequest()).first {
let info = InstanceInfo(activeInstance: activeInstance)
DispatchQueue.main.async {
self.instanceInfo = info
}
}
if let activeInstance = try? persistentContainer.viewContext.fetch(ActiveInstance.fetchRequest()).first {
let info = InstanceInfo(activeInstance: activeInstance)
self.instanceInfo = info
}
}

View File

@ -25,6 +25,8 @@ public final class AccountMO: NSManagedObject, AccountProtocol {
}
@NSManaged public var acct: String
/// Whether this AccountMO is the active (logged-in) account.
@NSManaged public var active: Bool
@NSManaged public var avatar: URL?
@NSManaged public var botCD: Bool
@NSManaged public var createdAt: Date

View File

@ -311,6 +311,14 @@ class MastodonCachePersistentStore: NSPersistentCloudKitContainer {
}
}
/// The caller is responsible for calling this on a queue appropriate for `context`.
func addOrUpdateSynchronously(account: Account, in context: NSManagedObjectContext) -> AccountMO {
let accountMO = self.upsert(account: account, in: context)
self.save(context: context)
self.accountSubject.send(account.id)
return accountMO
}
func relationship(forAccount id: String, in context: NSManagedObjectContext? = nil) -> RelationshipMO? {
let context = context ?? viewContext
let request: NSFetchRequest<RelationshipMO> = RelationshipMO.fetchRequest()

View File

@ -2,6 +2,7 @@
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21754" systemVersion="22D49" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="Account" representedClassName="AccountMO" syncable="YES">
<attribute name="acct" attributeType="String"/>
<attribute name="active" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="avatar" optional="YES" attributeType="URI"/>
<attribute name="botCD" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>