forked from shadowfacts/Tusker
Convert API objects to CoreData models and save them
This commit is contained in:
parent
7deb4fc5b4
commit
102fe6ed91
|
@ -12,29 +12,29 @@ import CoreData
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
|
|
||||||
@objc(AccountMO)
|
@objc(AccountMO)
|
||||||
public class AccountMO: NSManagedObject {
|
public final class AccountMO: NSManagedObject {
|
||||||
|
|
||||||
@nonobjc public class func fetchRequest() -> NSFetchRequest<AccountMO> {
|
@nonobjc public class func fetchRequest() -> NSFetchRequest<AccountMO> {
|
||||||
return NSFetchRequest<AccountMO>(entityName: "Account")
|
return NSFetchRequest<AccountMO>(entityName: "Account")
|
||||||
}
|
}
|
||||||
|
|
||||||
@NSManaged public var acct: String?
|
@NSManaged public var acct: String
|
||||||
@NSManaged public var avatar: URL?
|
@NSManaged public var avatar: URL
|
||||||
@NSManaged public var bot: Bool
|
@NSManaged public var bot: Bool
|
||||||
@NSManaged public var createdAt: Date?
|
@NSManaged public var createdAt: Date
|
||||||
@NSManaged public var displayName: String?
|
@NSManaged public var displayName: String
|
||||||
@NSManaged public var emojisData: Data?
|
@NSManaged private var emojisData: Data?
|
||||||
@NSManaged public var fieldsData: Data?
|
@NSManaged private var fieldsData: Data?
|
||||||
@NSManaged public var followersCount: Int64
|
@NSManaged public var followersCount: Int
|
||||||
@NSManaged public var followingCount: Int64
|
@NSManaged public var followingCount: Int
|
||||||
@NSManaged public var header: URL?
|
@NSManaged public var header: URL
|
||||||
@NSManaged public var id: String?
|
@NSManaged public var id: String
|
||||||
@NSManaged public var locked: Bool
|
@NSManaged public var locked: Bool
|
||||||
@NSManaged public var moved: Bool
|
@NSManaged public var moved: Bool
|
||||||
@NSManaged public var note: String?
|
@NSManaged public var note: String
|
||||||
@NSManaged public var statusesCount: Int64
|
@NSManaged public var statusesCount: Int
|
||||||
@NSManaged public var url: URL?
|
@NSManaged public var url: URL
|
||||||
@NSManaged public var username: String?
|
@NSManaged public var username: String
|
||||||
@NSManaged public var movedTo: AccountMO?
|
@NSManaged public var movedTo: AccountMO?
|
||||||
|
|
||||||
@LazilyDecoding(arrayFrom: \AccountMO.emojisData)
|
@LazilyDecoding(arrayFrom: \AccountMO.emojisData)
|
||||||
|
@ -44,3 +44,40 @@ public class AccountMO: NSManagedObject {
|
||||||
var fields: [Account.Field]
|
var fields: [Account.Field]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension AccountMO {
|
||||||
|
convenience init(apiAccount account: Pachyderm.Account, container: MastodonCachePersistentStore, context: NSManagedObjectContext) {
|
||||||
|
self.init(context: context)
|
||||||
|
self.updateFrom(apiAccount: account, container: container)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateFrom(apiAccount account: Pachyderm.Account, container: MastodonCachePersistentStore) {
|
||||||
|
guard let context = managedObjectContext else {
|
||||||
|
// we've been deleted, don't bother updating
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.acct = account.acct
|
||||||
|
self.avatar = account.avatar
|
||||||
|
self.bot = account.bot ?? false
|
||||||
|
self.createdAt = account.createdAt
|
||||||
|
self.displayName = account.displayName
|
||||||
|
self.emojis = account.emojis
|
||||||
|
self.fields = account.fields ?? []
|
||||||
|
self.followersCount = account.followersCount
|
||||||
|
self.followingCount = account.followingCount
|
||||||
|
self.header = account.header
|
||||||
|
self.id = account.id
|
||||||
|
self.locked = account.locked
|
||||||
|
self.moved = account.moved ?? false
|
||||||
|
self.note = account.note
|
||||||
|
self.statusesCount = account.statusesCount
|
||||||
|
self.url = account.url
|
||||||
|
self.username = account.username
|
||||||
|
if let movedTo = account.movedTo {
|
||||||
|
self.movedTo = container.account(for: movedTo.id, in: context) ?? AccountMO(apiAccount: movedTo, container: container, context: context)
|
||||||
|
} else {
|
||||||
|
self.movedTo = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,9 +8,12 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import CoreData
|
import CoreData
|
||||||
|
import Pachyderm
|
||||||
|
|
||||||
class MastodonCachePersistentStore: NSPersistentContainer {
|
class MastodonCachePersistentStore: NSPersistentContainer {
|
||||||
|
|
||||||
|
private(set) lazy var backgroundContext = newBackgroundContext()
|
||||||
|
|
||||||
init(for controller: MastodonController) {
|
init(for controller: MastodonController) {
|
||||||
let url = Bundle.main.url(forResource: "Tusker", withExtension: "momd")!
|
let url = Bundle.main.url(forResource: "Tusker", withExtension: "momd")!
|
||||||
let model = NSManagedObjectModel(contentsOf: url)!
|
let model = NSManagedObjectModel(contentsOf: url)!
|
||||||
|
@ -22,26 +25,54 @@ class MastodonCachePersistentStore: NSPersistentContainer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func status(for id: String) -> StatusMO? {
|
func status(for id: String, in context: NSManagedObjectContext? = nil) -> StatusMO? {
|
||||||
|
let context = context ?? viewContext
|
||||||
let request: NSFetchRequest<StatusMO> = StatusMO.fetchRequest()
|
let request: NSFetchRequest<StatusMO> = StatusMO.fetchRequest()
|
||||||
request.predicate = NSPredicate(format: "id = %@", id)
|
request.predicate = NSPredicate(format: "id = %@", id)
|
||||||
request.fetchLimit = 1
|
request.fetchLimit = 1
|
||||||
if let result = try? viewContext.fetch(request), let status = result.first {
|
if let result = try? context.fetch(request), let status = result.first {
|
||||||
return status
|
return status
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func account(for id: String) -> AccountMO? {
|
func addOrUpdate(status: Status, save: Bool = true) {
|
||||||
|
backgroundContext.perform {
|
||||||
|
if let statusMO = self.status(for: status.id, in: self.backgroundContext) {
|
||||||
|
statusMO.updateFrom(apiStatus: status, container: self)
|
||||||
|
} else {
|
||||||
|
_ = StatusMO(apiStatus: status, container: self, context: self.backgroundContext)
|
||||||
|
}
|
||||||
|
if save, self.backgroundContext.hasChanges {
|
||||||
|
try! self.backgroundContext.save()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func account(for id: String, in context: NSManagedObjectContext? = nil) -> AccountMO? {
|
||||||
|
let context = context ?? viewContext
|
||||||
let request: NSFetchRequest<AccountMO> = AccountMO.fetchRequest()
|
let request: NSFetchRequest<AccountMO> = AccountMO.fetchRequest()
|
||||||
request.predicate = NSPredicate(format: "id = %@", id)
|
request.predicate = NSPredicate(format: "id = %@", id)
|
||||||
request.fetchLimit = 1
|
request.fetchLimit = 1
|
||||||
if let result = try? viewContext.fetch(request), let account = result.first {
|
if let result = try? context.fetch(request), let account = result.first {
|
||||||
return account
|
return account
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addOrUpdate(account: Account, save: Bool = true) {
|
||||||
|
backgroundContext.perform {
|
||||||
|
if let accountMO = self.account(for: account.id, in: self.backgroundContext) {
|
||||||
|
accountMO.updateFrom(apiAccount: account, container: self)
|
||||||
|
} else {
|
||||||
|
_ = AccountMO(apiAccount: account, container: self, context: self.backgroundContext)
|
||||||
|
}
|
||||||
|
if save, self.backgroundContext.hasChanges {
|
||||||
|
try! self.backgroundContext.save()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ public final class StatusMO: NSManagedObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
@NSManaged public var application: String?
|
@NSManaged public var application: String?
|
||||||
@NSManaged public var attachmentsData: Data?
|
@NSManaged private var attachmentsData: Data?
|
||||||
@NSManaged public var bookmarked: Bool
|
@NSManaged public var bookmarked: Bool
|
||||||
@NSManaged public var content: String
|
@NSManaged public var content: String
|
||||||
@NSManaged public var createdAt: Date
|
@NSManaged public var createdAt: Date
|
||||||
|
@ -28,25 +28,25 @@ public final class StatusMO: NSManagedObject {
|
||||||
@NSManaged public var favouritesCount: Int
|
@NSManaged public var favouritesCount: Int
|
||||||
@NSManaged public var hashtagsData: Data?
|
@NSManaged public var hashtagsData: Data?
|
||||||
@NSManaged public var id: String
|
@NSManaged public var id: String
|
||||||
@NSManaged public var mentionsData: Data?
|
@NSManaged public var inReplyToAccountID: String?
|
||||||
|
@NSManaged public var inReplyToID: String?
|
||||||
|
@NSManaged private var mentionsData: Data?
|
||||||
@NSManaged public var muted: Bool
|
@NSManaged public var muted: Bool
|
||||||
@NSManaged public var pinned: Bool
|
@NSManaged public var pinned: Bool
|
||||||
@NSManaged public var reblogged: Bool
|
@NSManaged public var reblogged: Bool
|
||||||
@NSManaged public var reblogsCount: Int
|
@NSManaged public var reblogsCount: Int
|
||||||
@NSManaged public var sensitive: Bool
|
@NSManaged public var sensitive: Bool
|
||||||
@NSManaged public var uri: String
|
@NSManaged public var uri: String // todo: are both uri and url necessary?
|
||||||
@NSManaged public var url: URL?
|
@NSManaged public var url: URL?
|
||||||
@NSManaged public var visibility: String?
|
@NSManaged private var visibilityString: String
|
||||||
@NSManaged public var account: AccountMO
|
@NSManaged public var account: AccountMO
|
||||||
@NSManaged public var inReplyTo: StatusMO?
|
|
||||||
@NSManaged public var inReplyToAccount: AccountMO?
|
|
||||||
@NSManaged public var reblog: StatusMO?
|
@NSManaged public var reblog: StatusMO?
|
||||||
|
|
||||||
@LazilyDecoding(arrayFrom: \StatusMO.attachmentsData)
|
@LazilyDecoding(arrayFrom: \StatusMO.attachmentsData)
|
||||||
var attachments: [Attachment]
|
var attachments: [Attachment]
|
||||||
|
|
||||||
@LazilyDecoding(arrayFrom: \StatusMO.emojisData)
|
@LazilyDecoding(arrayFrom: \StatusMO.emojisData)
|
||||||
var emoji: [Emoji]
|
var emojis: [Emoji]
|
||||||
|
|
||||||
@LazilyDecoding(arrayFrom: \StatusMO.hashtagsData)
|
@LazilyDecoding(arrayFrom: \StatusMO.hashtagsData)
|
||||||
var hashtags: [Hashtag]
|
var hashtags: [Hashtag]
|
||||||
|
@ -54,4 +54,54 @@ public final class StatusMO: NSManagedObject {
|
||||||
@LazilyDecoding(arrayFrom: \StatusMO.mentionsData)
|
@LazilyDecoding(arrayFrom: \StatusMO.mentionsData)
|
||||||
var mentions: [Mention]
|
var mentions: [Mention]
|
||||||
|
|
||||||
|
var visibility: Status.Visibility {
|
||||||
|
get {
|
||||||
|
Pachyderm.Status.Visibility(rawValue: visibilityString) ?? .public
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
visibilityString = newValue.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension StatusMO {
|
||||||
|
convenience init(apiStatus status: Pachyderm.Status, container: MastodonCachePersistentStore, context: NSManagedObjectContext) {
|
||||||
|
self.init(context: context)
|
||||||
|
self.updateFrom(apiStatus: status, container: container)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateFrom(apiStatus status: Pachyderm.Status, container: MastodonCachePersistentStore) {
|
||||||
|
guard let context = managedObjectContext else {
|
||||||
|
// we have been deleted, don't bother updating
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.application = status.application?.name
|
||||||
|
self.attachments = status.attachments
|
||||||
|
self.bookmarked = status.bookmarked ?? false
|
||||||
|
self.content = status.content
|
||||||
|
self.createdAt = status.createdAt
|
||||||
|
self.emojis = status.emojis
|
||||||
|
self.favourited = status.favourited ?? false
|
||||||
|
self.favouritesCount = status.favouritesCount
|
||||||
|
self.hashtags = status.hashtags
|
||||||
|
self.inReplyToAccountID = status.inReplyToAccountID
|
||||||
|
self.inReplyToID = status.inReplyToID
|
||||||
|
self.id = status.id
|
||||||
|
self.mentions = status.mentions
|
||||||
|
self.muted = status.muted ?? false
|
||||||
|
self.pinned = status.pinned ?? false
|
||||||
|
self.reblogged = status.reblogged ?? false
|
||||||
|
self.reblogsCount = status.reblogsCount
|
||||||
|
self.sensitive = status.sensitive
|
||||||
|
self.uri = status.uri
|
||||||
|
self.visibility = status.visibility
|
||||||
|
self.account = container.account(for: status.account.id, in: context) ?? AccountMO(apiAccount: status.account, container: container, context: context)
|
||||||
|
if let reblog = status.reblog {
|
||||||
|
self.reblog = container.status(for: reblog.id, in: context) ?? StatusMO(apiStatus: reblog, container: container, context: context)
|
||||||
|
} else {
|
||||||
|
self.reblog = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,8 @@
|
||||||
<attribute name="favouritesCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
<attribute name="favouritesCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
<attribute name="hashtagsData" attributeType="Binary"/>
|
<attribute name="hashtagsData" attributeType="Binary"/>
|
||||||
<attribute name="id" attributeType="String"/>
|
<attribute name="id" attributeType="String"/>
|
||||||
|
<attribute name="inReplyToAccountID" optional="YES" attributeType="String"/>
|
||||||
|
<attribute name="inReplyToID" optional="YES" attributeType="String"/>
|
||||||
<attribute name="mentionsData" attributeType="Binary"/>
|
<attribute name="mentionsData" attributeType="Binary"/>
|
||||||
<attribute name="muted" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
<attribute name="muted" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
<attribute name="pinned" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
<attribute name="pinned" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
|
@ -44,10 +46,8 @@
|
||||||
<attribute name="sensitive" attributeType="Boolean" usesScalarValueType="YES"/>
|
<attribute name="sensitive" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
<attribute name="uri" attributeType="String"/>
|
<attribute name="uri" attributeType="String"/>
|
||||||
<attribute name="url" optional="YES" attributeType="URI"/>
|
<attribute name="url" optional="YES" attributeType="URI"/>
|
||||||
<attribute name="visibility" attributeType="String"/>
|
<attribute name="visibilityString" attributeType="String"/>
|
||||||
<relationship name="account" maxCount="1" deletionRule="Nullify" destinationEntity="Account"/>
|
<relationship name="account" maxCount="1" deletionRule="Nullify" destinationEntity="Account"/>
|
||||||
<relationship name="inReplyTo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status"/>
|
|
||||||
<relationship name="inReplyToAccount" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Account"/>
|
|
||||||
<relationship name="reblog" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status"/>
|
<relationship name="reblog" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status"/>
|
||||||
<uniquenessConstraints>
|
<uniquenessConstraints>
|
||||||
<uniquenessConstraint>
|
<uniquenessConstraint>
|
||||||
|
|
|
@ -59,10 +59,17 @@ class MastodonCache {
|
||||||
|
|
||||||
func add(status: Status) {
|
func add(status: Status) {
|
||||||
set(status: status, for: status.id)
|
set(status: status, for: status.id)
|
||||||
|
mastodonController?.persistentContainer.addOrUpdate(status: status)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addAll(statuses: [Status]) {
|
func addAll(statuses: [Status]) {
|
||||||
statuses.forEach(add)
|
statuses.forEach(add)
|
||||||
|
if let container = mastodonController?.persistentContainer {
|
||||||
|
statuses.forEach { container.addOrUpdate(status: $0, save: false) }
|
||||||
|
container.backgroundContext.perform {
|
||||||
|
try! container.backgroundContext.save()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Accounts
|
// MARK: - Accounts
|
||||||
|
@ -92,10 +99,17 @@ class MastodonCache {
|
||||||
|
|
||||||
func add(account: Account) {
|
func add(account: Account) {
|
||||||
set(account: account, for: account.id)
|
set(account: account, for: account.id)
|
||||||
|
mastodonController?.persistentContainer.addOrUpdate(account: account)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addAll(accounts: [Account]) {
|
func addAll(accounts: [Account]) {
|
||||||
accounts.forEach(add)
|
accounts.forEach(add)
|
||||||
|
if let container = mastodonController?.persistentContainer {
|
||||||
|
accounts.forEach { container.addOrUpdate(account: $0, save: false) }
|
||||||
|
container.backgroundContext.perform {
|
||||||
|
try! container.backgroundContext.save()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Relationships
|
// MARK: - Relationships
|
||||||
|
|
Loading…
Reference in New Issue