forked from shadowfacts/Tusker
Remove reference counting system
Delete statuses/accounts that haven't been fetched in a week
This commit is contained in:
parent
8cc08cf4c0
commit
d04957ba41
|
@ -29,7 +29,7 @@ class BookmarkStatusActivity: StatusActivity {
|
||||||
let request = Status.bookmark(status.id)
|
let request = Status.bookmark(status.id)
|
||||||
mastodonController.run(request) { (response) in
|
mastodonController.run(request) { (response) in
|
||||||
if case let .success(status, _) = response {
|
if case let .success(status, _) = response {
|
||||||
self.mastodonController.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false)
|
self.mastodonController.persistentContainer.addOrUpdate(status: status)
|
||||||
} else {
|
} else {
|
||||||
// todo: display error message
|
// todo: display error message
|
||||||
UINotificationFeedbackGenerator().notificationOccurred(.error)
|
UINotificationFeedbackGenerator().notificationOccurred(.error)
|
||||||
|
|
|
@ -28,7 +28,7 @@ class MuteConversationActivity: StatusActivity {
|
||||||
let request = Status.muteConversation(status.id)
|
let request = Status.muteConversation(status.id)
|
||||||
mastodonController.run(request) { (response) in
|
mastodonController.run(request) { (response) in
|
||||||
if case let .success(status, _) = response {
|
if case let .success(status, _) = response {
|
||||||
self.mastodonController.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false)
|
self.mastodonController.persistentContainer.addOrUpdate(status: status)
|
||||||
} else {
|
} else {
|
||||||
// todo: display error message
|
// todo: display error message
|
||||||
UINotificationFeedbackGenerator().notificationOccurred(.error)
|
UINotificationFeedbackGenerator().notificationOccurred(.error)
|
||||||
|
|
|
@ -28,7 +28,7 @@ class PinStatusActivity: StatusActivity {
|
||||||
let request = Status.pin(status.id)
|
let request = Status.pin(status.id)
|
||||||
mastodonController.run(request) { (response) in
|
mastodonController.run(request) { (response) in
|
||||||
if case let .success(status, _) = response {
|
if case let .success(status, _) = response {
|
||||||
self.mastodonController.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false)
|
self.mastodonController.persistentContainer.addOrUpdate(status: status)
|
||||||
} else {
|
} else {
|
||||||
// todo: display error message
|
// todo: display error message
|
||||||
UINotificationFeedbackGenerator().notificationOccurred(.error)
|
UINotificationFeedbackGenerator().notificationOccurred(.error)
|
||||||
|
|
|
@ -29,7 +29,7 @@ class UnbookmarkStatusActivity: StatusActivity {
|
||||||
let request = Status.unbookmark(status.id)
|
let request = Status.unbookmark(status.id)
|
||||||
mastodonController.run(request) { (response) in
|
mastodonController.run(request) { (response) in
|
||||||
if case let .success(status, _) = response {
|
if case let .success(status, _) = response {
|
||||||
self.mastodonController.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false)
|
self.mastodonController.persistentContainer.addOrUpdate(status: status)
|
||||||
} else {
|
} else {
|
||||||
// todo: display error message
|
// todo: display error message
|
||||||
UINotificationFeedbackGenerator().notificationOccurred(.error)
|
UINotificationFeedbackGenerator().notificationOccurred(.error)
|
||||||
|
|
|
@ -28,7 +28,7 @@ class UnmuteConversationActivity: StatusActivity {
|
||||||
let request = Status.unmuteConversation(status.id)
|
let request = Status.unmuteConversation(status.id)
|
||||||
mastodonController.run(request) { (response) in
|
mastodonController.run(request) { (response) in
|
||||||
if case let .success(status, _) = response {
|
if case let .success(status, _) = response {
|
||||||
self.mastodonController.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false)
|
self.mastodonController.persistentContainer.addOrUpdate(status: status)
|
||||||
} else {
|
} else {
|
||||||
// todo: display error message
|
// todo: display error message
|
||||||
UINotificationFeedbackGenerator().notificationOccurred(.error)
|
UINotificationFeedbackGenerator().notificationOccurred(.error)
|
||||||
|
|
|
@ -28,7 +28,7 @@ class UnpinStatusActivity: StatusActivity {
|
||||||
let request = Status.unpin(status.id)
|
let request = Status.unpin(status.id)
|
||||||
mastodonController.run(request) { (response) in
|
mastodonController.run(request) { (response) in
|
||||||
if case let .success(status, _) = response {
|
if case let .success(status, _) = response {
|
||||||
self.mastodonController.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false)
|
self.mastodonController.persistentContainer.addOrUpdate(status: status)
|
||||||
} else {
|
} else {
|
||||||
// todo: display error message
|
// todo: display error message
|
||||||
UINotificationFeedbackGenerator().notificationOccurred(.error)
|
UINotificationFeedbackGenerator().notificationOccurred(.error)
|
||||||
|
|
|
@ -137,7 +137,7 @@ class MastodonController: ObservableObject {
|
||||||
} else {
|
} else {
|
||||||
// the first time the user's account is added to the store,
|
// the first time the user's account is added to the store,
|
||||||
// increment its reference count so that it's never removed
|
// increment its reference count so that it's never removed
|
||||||
self.persistentContainer.addOrUpdate(account: account, incrementReferenceCount: true)
|
self.persistentContainer.addOrUpdate(account: account)
|
||||||
}
|
}
|
||||||
completion?(.success(account))
|
completion?(.success(account))
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,10 +32,10 @@ public final class AccountMO: NSManagedObject, AccountProtocol {
|
||||||
@NSManaged public var locked: Bool
|
@NSManaged public var locked: Bool
|
||||||
@NSManaged public var movedCD: Bool
|
@NSManaged public var movedCD: Bool
|
||||||
@NSManaged public var note: String
|
@NSManaged public var note: String
|
||||||
@NSManaged public var referenceCount: Int
|
|
||||||
@NSManaged public var statusesCount: Int
|
@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 lastFetchedAt: Date?
|
||||||
@NSManaged public var movedTo: AccountMO?
|
@NSManaged public var movedTo: AccountMO?
|
||||||
|
|
||||||
@LazilyDecoding(arrayFrom: \AccountMO.emojisData)
|
@LazilyDecoding(arrayFrom: \AccountMO.emojisData)
|
||||||
|
@ -47,30 +47,20 @@ public final class AccountMO: NSManagedObject, AccountProtocol {
|
||||||
public var bot: Bool? { botCD }
|
public var bot: Bool? { botCD }
|
||||||
public var moved: Bool? { movedCD }
|
public var moved: Bool? { movedCD }
|
||||||
|
|
||||||
func incrementReferenceCount() {
|
public override func awakeFromFetch() {
|
||||||
referenceCount += 1
|
super.awakeFromFetch()
|
||||||
}
|
|
||||||
|
|
||||||
func decrementReferenceCount() {
|
managedObjectContext?.perform {
|
||||||
referenceCount -= 1
|
self.lastFetchedAt = Date()
|
||||||
if referenceCount <= 0 {
|
|
||||||
managedObjectContext!.delete(self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func prepareForDeletion() {
|
|
||||||
super.prepareForDeletion()
|
|
||||||
movedTo?.decrementReferenceCount()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AccountMO {
|
extension AccountMO {
|
||||||
convenience init(apiAccount account: Pachyderm.Account, container: MastodonCachePersistentStore, context: NSManagedObjectContext) {
|
convenience init(apiAccount account: Pachyderm.Account, container: MastodonCachePersistentStore, context: NSManagedObjectContext) {
|
||||||
self.init(context: context)
|
self.init(context: context)
|
||||||
self.updateFrom(apiAccount: account, container: container)
|
self.updateFrom(apiAccount: account, container: container)
|
||||||
|
|
||||||
movedTo?.incrementReferenceCount()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateFrom(apiAccount account: Pachyderm.Account, container: MastodonCachePersistentStore) {
|
func updateFrom(apiAccount account: Pachyderm.Account, container: MastodonCachePersistentStore) {
|
||||||
|
|
|
@ -65,26 +65,19 @@ class MastodonCachePersistentStore: NSPersistentContainer {
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
private func upsert(status: Status, incrementReferenceCount: Bool, context: NSManagedObjectContext) -> StatusMO {
|
private func upsert(status: Status, context: NSManagedObjectContext) -> StatusMO {
|
||||||
if let statusMO = self.status(for: status.id, in: context) {
|
if let statusMO = self.status(for: status.id, in: context) {
|
||||||
statusMO.updateFrom(apiStatus: status, container: self)
|
statusMO.updateFrom(apiStatus: status, container: self)
|
||||||
if incrementReferenceCount {
|
|
||||||
statusMO.incrementReferenceCount()
|
|
||||||
}
|
|
||||||
return statusMO
|
return statusMO
|
||||||
} else {
|
} else {
|
||||||
let statusMO = StatusMO(apiStatus: status, container: self, context: context)
|
return StatusMO(apiStatus: status, container: self, context: context)
|
||||||
if incrementReferenceCount {
|
|
||||||
statusMO.incrementReferenceCount()
|
|
||||||
}
|
|
||||||
return statusMO
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addOrUpdate(status: Status, incrementReferenceCount: Bool, context: NSManagedObjectContext? = nil, completion: ((StatusMO) -> Void)? = nil) {
|
func addOrUpdate(status: Status, context: NSManagedObjectContext? = nil, completion: ((StatusMO) -> Void)? = nil) {
|
||||||
let context = context ?? backgroundContext
|
let context = context ?? backgroundContext
|
||||||
context.perform {
|
context.perform {
|
||||||
let statusMO = self.upsert(status: status, incrementReferenceCount: incrementReferenceCount, context: context)
|
let statusMO = self.upsert(status: status, context: context)
|
||||||
if context.hasChanges {
|
if context.hasChanges {
|
||||||
try! context.save()
|
try! context.save()
|
||||||
}
|
}
|
||||||
|
@ -95,7 +88,7 @@ class MastodonCachePersistentStore: NSPersistentContainer {
|
||||||
|
|
||||||
func addAll(statuses: [Status], completion: (() -> Void)? = nil) {
|
func addAll(statuses: [Status], completion: (() -> Void)? = nil) {
|
||||||
backgroundContext.perform {
|
backgroundContext.perform {
|
||||||
statuses.forEach { self.upsert(status: $0, incrementReferenceCount: true, context: self.backgroundContext) }
|
statuses.forEach { self.upsert(status: $0, context: self.backgroundContext) }
|
||||||
if self.backgroundContext.hasChanges {
|
if self.backgroundContext.hasChanges {
|
||||||
try! self.backgroundContext.save()
|
try! self.backgroundContext.save()
|
||||||
}
|
}
|
||||||
|
@ -117,25 +110,18 @@ class MastodonCachePersistentStore: NSPersistentContainer {
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
private func upsert(account: Account, incrementReferenceCount: Bool) -> AccountMO {
|
private func upsert(account: Account) -> AccountMO {
|
||||||
if let accountMO = self.account(for: account.id, in: self.backgroundContext) {
|
if let accountMO = self.account(for: account.id, in: self.backgroundContext) {
|
||||||
accountMO.updateFrom(apiAccount: account, container: self)
|
accountMO.updateFrom(apiAccount: account, container: self)
|
||||||
if incrementReferenceCount {
|
|
||||||
accountMO.incrementReferenceCount()
|
|
||||||
}
|
|
||||||
return accountMO
|
return accountMO
|
||||||
} else {
|
} else {
|
||||||
let accountMO = AccountMO(apiAccount: account, container: self, context: self.backgroundContext)
|
return AccountMO(apiAccount: account, container: self, context: self.backgroundContext)
|
||||||
if incrementReferenceCount {
|
|
||||||
accountMO.incrementReferenceCount()
|
|
||||||
}
|
|
||||||
return accountMO
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addOrUpdate(account: Account, incrementReferenceCount: Bool, completion: ((AccountMO) -> Void)? = nil) {
|
func addOrUpdate(account: Account, completion: ((AccountMO) -> Void)? = nil) {
|
||||||
backgroundContext.perform {
|
backgroundContext.perform {
|
||||||
let accountMO = self.upsert(account: account, incrementReferenceCount: incrementReferenceCount)
|
let accountMO = self.upsert(account: account)
|
||||||
if self.backgroundContext.hasChanges {
|
if self.backgroundContext.hasChanges {
|
||||||
try! self.backgroundContext.save()
|
try! self.backgroundContext.save()
|
||||||
}
|
}
|
||||||
|
@ -180,7 +166,7 @@ class MastodonCachePersistentStore: NSPersistentContainer {
|
||||||
|
|
||||||
func addAll(accounts: [Account], completion: (() -> Void)? = nil) {
|
func addAll(accounts: [Account], completion: (() -> Void)? = nil) {
|
||||||
backgroundContext.perform {
|
backgroundContext.perform {
|
||||||
accounts.forEach { self.upsert(account: $0, incrementReferenceCount: true) }
|
accounts.forEach { self.upsert(account: $0) }
|
||||||
if self.backgroundContext.hasChanges {
|
if self.backgroundContext.hasChanges {
|
||||||
try! self.backgroundContext.save()
|
try! self.backgroundContext.save()
|
||||||
}
|
}
|
||||||
|
@ -195,8 +181,8 @@ class MastodonCachePersistentStore: NSPersistentContainer {
|
||||||
// filter out mentions, otherwise we would double increment the reference count of those accounts
|
// filter out mentions, otherwise we would double increment the reference count of those accounts
|
||||||
// since the status has the same account as the notification
|
// since the status has the same account as the notification
|
||||||
let accounts = notifications.filter { $0.kind != .mention }.map { $0.account }
|
let accounts = notifications.filter { $0.kind != .mention }.map { $0.account }
|
||||||
statuses.forEach { self.upsert(status: $0, incrementReferenceCount: true, context: self.backgroundContext) }
|
statuses.forEach { self.upsert(status: $0, context: self.backgroundContext) }
|
||||||
accounts.forEach { self.upsert(account: $0, incrementReferenceCount: true) }
|
accounts.forEach { self.upsert(account: $0) }
|
||||||
if self.backgroundContext.hasChanges {
|
if self.backgroundContext.hasChanges {
|
||||||
try! self.backgroundContext.save()
|
try! self.backgroundContext.save()
|
||||||
}
|
}
|
||||||
|
@ -212,10 +198,10 @@ class MastodonCachePersistentStore: NSPersistentContainer {
|
||||||
var updatedStatuses = [String]()
|
var updatedStatuses = [String]()
|
||||||
|
|
||||||
block(self.backgroundContext, { (accounts) in
|
block(self.backgroundContext, { (accounts) in
|
||||||
accounts.forEach { self.upsert(account: $0, incrementReferenceCount: true) }
|
accounts.forEach { self.upsert(account: $0) }
|
||||||
updatedAccounts.append(contentsOf: accounts.map { $0.id })
|
updatedAccounts.append(contentsOf: accounts.map { $0.id })
|
||||||
}, { (statuses) in
|
}, { (statuses) in
|
||||||
statuses.forEach { self.upsert(status: $0, incrementReferenceCount: true, context: self.backgroundContext) }
|
statuses.forEach { self.upsert(status: $0, context: self.backgroundContext) }
|
||||||
updatedStatuses.append(contentsOf: statuses.map { $0.id })
|
updatedStatuses.append(contentsOf: statuses.map { $0.id })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,6 @@ public final class StatusMO: NSManagedObject, StatusProtocol {
|
||||||
@NSManaged private var pinnedInternal: Bool
|
@NSManaged private var pinnedInternal: Bool
|
||||||
@NSManaged public var reblogged: Bool
|
@NSManaged public var reblogged: Bool
|
||||||
@NSManaged public var reblogsCount: Int
|
@NSManaged public var reblogsCount: Int
|
||||||
@NSManaged public var referenceCount: Int
|
|
||||||
@NSManaged public var sensitive: Bool
|
@NSManaged public var sensitive: Bool
|
||||||
@NSManaged public var spoilerText: String
|
@NSManaged public var spoilerText: String
|
||||||
@NSManaged public var uri: String // todo: are both uri and url necessary?
|
@NSManaged public var uri: String // todo: are both uri and url necessary?
|
||||||
|
@ -46,6 +45,7 @@ public final class StatusMO: NSManagedObject, StatusProtocol {
|
||||||
@NSManaged public var account: AccountMO
|
@NSManaged public var account: AccountMO
|
||||||
@NSManaged public var reblog: StatusMO?
|
@NSManaged public var reblog: StatusMO?
|
||||||
@NSManaged public var localOnly: Bool
|
@NSManaged public var localOnly: Bool
|
||||||
|
@NSManaged public var lastFetchedAt: Date?
|
||||||
|
|
||||||
@LazilyDecoding(arrayFrom: \StatusMO.attachmentsData)
|
@LazilyDecoding(arrayFrom: \StatusMO.attachmentsData)
|
||||||
public var attachments: [Attachment]
|
public var attachments: [Attachment]
|
||||||
|
@ -77,32 +77,20 @@ public final class StatusMO: NSManagedObject, StatusProtocol {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func incrementReferenceCount() {
|
public override func awakeFromFetch() {
|
||||||
referenceCount += 1
|
super.awakeFromFetch()
|
||||||
}
|
|
||||||
|
|
||||||
func decrementReferenceCount() {
|
managedObjectContext?.perform {
|
||||||
referenceCount -= 1
|
self.lastFetchedAt = Date()
|
||||||
if referenceCount <= 0 {
|
|
||||||
managedObjectContext!.delete(self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func prepareForDeletion() {
|
|
||||||
super.prepareForDeletion()
|
|
||||||
reblog?.decrementReferenceCount()
|
|
||||||
account.decrementReferenceCount()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension StatusMO {
|
extension StatusMO {
|
||||||
convenience init(apiStatus status: Pachyderm.Status, container: MastodonCachePersistentStore, context: NSManagedObjectContext) {
|
convenience init(apiStatus status: Pachyderm.Status, container: MastodonCachePersistentStore, context: NSManagedObjectContext) {
|
||||||
self.init(context: context)
|
self.init(context: context)
|
||||||
self.updateFrom(apiStatus: status, container: container)
|
self.updateFrom(apiStatus: status, container: container)
|
||||||
|
|
||||||
reblog?.incrementReferenceCount()
|
|
||||||
account.incrementReferenceCount()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateFrom(apiStatus status: Pachyderm.Status, container: MastodonCachePersistentStore) {
|
func updateFrom(apiStatus status: Pachyderm.Status, container: MastodonCachePersistentStore) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21D49" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="20086" systemVersion="21E230" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||||
<entity name="Account" representedClassName="AccountMO" syncable="YES">
|
<entity name="Account" representedClassName="AccountMO" syncable="YES">
|
||||||
<attribute name="acct" attributeType="String"/>
|
<attribute name="acct" attributeType="String"/>
|
||||||
<attribute name="avatar" optional="YES" attributeType="URI"/>
|
<attribute name="avatar" optional="YES" attributeType="URI"/>
|
||||||
|
@ -12,10 +12,10 @@
|
||||||
<attribute name="followingCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
<attribute name="followingCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
<attribute name="header" optional="YES" attributeType="URI"/>
|
<attribute name="header" optional="YES" attributeType="URI"/>
|
||||||
<attribute name="id" attributeType="String"/>
|
<attribute name="id" attributeType="String"/>
|
||||||
|
<attribute name="lastFetchedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
<attribute name="locked" attributeType="Boolean" usesScalarValueType="YES"/>
|
<attribute name="locked" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
<attribute name="movedCD" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
<attribute name="movedCD" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
<attribute name="note" attributeType="String"/>
|
<attribute name="note" attributeType="String"/>
|
||||||
<attribute name="referenceCount" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="statusesCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
<attribute name="statusesCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
<attribute name="url" attributeType="URI"/>
|
<attribute name="url" attributeType="URI"/>
|
||||||
<attribute name="username" attributeType="String"/>
|
<attribute name="username" attributeType="String"/>
|
||||||
|
@ -54,6 +54,7 @@
|
||||||
<attribute name="id" attributeType="String"/>
|
<attribute name="id" attributeType="String"/>
|
||||||
<attribute name="inReplyToAccountID" optional="YES" attributeType="String"/>
|
<attribute name="inReplyToAccountID" optional="YES" attributeType="String"/>
|
||||||
<attribute name="inReplyToID" optional="YES" attributeType="String"/>
|
<attribute name="inReplyToID" optional="YES" attributeType="String"/>
|
||||||
|
<attribute name="lastFetchedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
<attribute name="localOnly" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
<attribute name="localOnly" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
<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"/>
|
||||||
|
@ -61,7 +62,6 @@
|
||||||
<attribute name="pollData" optional="YES" attributeType="Binary"/>
|
<attribute name="pollData" optional="YES" attributeType="Binary"/>
|
||||||
<attribute name="reblogged" attributeType="Boolean" usesScalarValueType="YES"/>
|
<attribute name="reblogged" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
<attribute name="reblogsCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
<attribute name="reblogsCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
<attribute name="referenceCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="sensitive" attributeType="Boolean" usesScalarValueType="YES"/>
|
<attribute name="sensitive" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||||
<attribute name="spoilerText" attributeType="String"/>
|
<attribute name="spoilerText" attributeType="String"/>
|
||||||
<attribute name="uri" attributeType="String"/>
|
<attribute name="uri" attributeType="String"/>
|
||||||
|
@ -76,7 +76,7 @@
|
||||||
</uniquenessConstraints>
|
</uniquenessConstraints>
|
||||||
</entity>
|
</entity>
|
||||||
<elements>
|
<elements>
|
||||||
<element name="Account" positionX="169.21875" positionY="78.9609375" width="128" height="329"/>
|
<element name="Account" positionX="169.21875" positionY="78.9609375" width="128" height="343"/>
|
||||||
<element name="Relationship" positionX="63" positionY="135" width="128" height="208"/>
|
<element name="Relationship" positionX="63" positionY="135" width="128" height="208"/>
|
||||||
<element name="Status" positionX="-63" positionY="-18" width="128" height="449"/>
|
<element name="Status" positionX="-63" positionY="-18" width="128" height="449"/>
|
||||||
</elements>
|
</elements>
|
||||||
|
|
|
@ -10,6 +10,7 @@ import UIKit
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
import CrashReporter
|
import CrashReporter
|
||||||
import MessageUI
|
import MessageUI
|
||||||
|
import CoreData
|
||||||
|
|
||||||
class MainSceneDelegate: UIResponder, UIWindowSceneDelegate {
|
class MainSceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
|
|
||||||
|
@ -120,7 +121,22 @@ class MainSceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
rootVC.sceneDidEnterBackground()
|
rootVC.sceneDidEnterBackground()
|
||||||
}
|
}
|
||||||
|
|
||||||
try? scene.session.mastodonController?.persistentContainer.viewContext.save()
|
if let context = scene.session.mastodonController?.persistentContainer.viewContext {
|
||||||
|
var minDate = Date()
|
||||||
|
minDate.addTimeInterval(-7 * 24 * 60 * 60)
|
||||||
|
|
||||||
|
let statusReq: NSFetchRequest<NSFetchRequestResult> = StatusMO.fetchRequest()
|
||||||
|
statusReq.predicate = NSPredicate(format: "(lastFetchedAt = nil) OR (lastFetchedAt < %@)", minDate as NSDate)
|
||||||
|
let deleteStatusReq = NSBatchDeleteRequest(fetchRequest: statusReq)
|
||||||
|
_ = try? context.execute(deleteStatusReq)
|
||||||
|
|
||||||
|
let accountReq: NSFetchRequest<NSFetchRequestResult> = AccountMO.fetchRequest()
|
||||||
|
accountReq.predicate = NSPredicate(format: "(lastFetchedAt = nil) OR (lastFetchedAt < %@)", minDate as NSDate)
|
||||||
|
let deleteAccountReq = NSBatchDeleteRequest(fetchRequest: accountReq)
|
||||||
|
_ = try? context.execute(deleteAccountReq)
|
||||||
|
|
||||||
|
try? context.save()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handlePendingCrashReport(_ report: PLCrashReport, session: UISceneSession) {
|
private func handlePendingCrashReport(_ report: PLCrashReport, session: UISceneSession) {
|
||||||
|
|
|
@ -134,7 +134,7 @@ class BookmarksTableViewController: EnhancedTableViewController {
|
||||||
let request = Status.unbookmark(status.id)
|
let request = Status.unbookmark(status.id)
|
||||||
self.mastodonController.run(request) { (response) in
|
self.mastodonController.run(request) { (response) in
|
||||||
guard case let .success(newStatus, _) = response else { fatalError() }
|
guard case let .success(newStatus, _) = response else { fatalError() }
|
||||||
self.mastodonController.persistentContainer.addOrUpdate(status: newStatus, incrementReferenceCount: false)
|
self.mastodonController.persistentContainer.addOrUpdate(status: newStatus)
|
||||||
self.statuses.remove(at: indexPath.row)
|
self.statuses.remove(at: indexPath.row)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,14 +52,6 @@ class ConversationTableViewController: EnhancedTableViewController {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
|
||||||
guard let persistentContainer = mastodonController?.persistentContainer else { return }
|
|
||||||
let snapshot = dataSource.snapshot()
|
|
||||||
for case let .status(id: id, state: _) in snapshot.itemIdentifiers {
|
|
||||||
persistentContainer.status(for: id)?.decrementReferenceCount()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
@ -149,7 +141,7 @@ class ConversationTableViewController: EnhancedTableViewController {
|
||||||
switch response {
|
switch response {
|
||||||
case let .success(status, _):
|
case let .success(status, _):
|
||||||
let viewContext = self.mastodonController.persistentContainer.viewContext
|
let viewContext = self.mastodonController.persistentContainer.viewContext
|
||||||
self.mastodonController.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false, context: viewContext) { (statusMO) in
|
self.mastodonController.persistentContainer.addOrUpdate(status: status, context: viewContext) { (statusMO) in
|
||||||
self.mainStatusLoaded(statusMO)
|
self.mainStatusLoaded(statusMO)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,8 +161,6 @@ class ConversationTableViewController: EnhancedTableViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func mainStatusLoaded(_ mainStatus: StatusMO) {
|
private func mainStatusLoaded(_ mainStatus: StatusMO) {
|
||||||
mainStatus.incrementReferenceCount()
|
|
||||||
|
|
||||||
let mainStatusItem = Item.status(id: mainStatusID, state: mainStatusState)
|
let mainStatusItem = Item.status(id: mainStatusID, state: mainStatusState)
|
||||||
|
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
||||||
|
|
|
@ -57,12 +57,6 @@ class ProfileViewController: UIPageViewController {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
|
||||||
if let accountID = accountID {
|
|
||||||
mastodonController.persistentContainer.account(for: accountID)?.decrementReferenceCount()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
@ -122,7 +116,7 @@ class ProfileViewController: UIPageViewController {
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
switch response {
|
switch response {
|
||||||
case .success(let account, _):
|
case .success(let account, _):
|
||||||
self.mastodonController.persistentContainer.addOrUpdate(account: account, incrementReferenceCount: true) { (account) in
|
self.mastodonController.persistentContainer.addOrUpdate(account: account) { (account) in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.updateAccountUI()
|
self.updateAccountUI()
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,24 +172,9 @@ class SearchResultsViewController: EnhancedTableViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func showSearchResults(_ results: SearchResults) {
|
private func showSearchResults(_ results: SearchResults) {
|
||||||
let oldSnapshot = self.dataSource.snapshot()
|
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
||||||
|
|
||||||
self.mastodonController.persistentContainer.performBatchUpdates({ (context, addAccounts, addStatuses) in
|
self.mastodonController.persistentContainer.performBatchUpdates({ (context, addAccounts, addStatuses) in
|
||||||
if oldSnapshot.indexOfSection(.accounts) != nil {
|
|
||||||
oldSnapshot.itemIdentifiers(inSection: .accounts).forEach { (item) in
|
|
||||||
guard case let .account(id) = item else { return }
|
|
||||||
self.mastodonController.persistentContainer.account(for: id, in: context)?.decrementReferenceCount()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if oldSnapshot.indexOfSection(.statuses) != nil {
|
|
||||||
oldSnapshot.itemIdentifiers(inSection: .statuses).forEach { (item) in
|
|
||||||
guard case let .status(id, _) = item else { return }
|
|
||||||
self.mastodonController.persistentContainer.status(for: id, in: context)?.decrementReferenceCount()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let resultTypes = self.resultTypes
|
let resultTypes = self.resultTypes
|
||||||
if !results.accounts.isEmpty && (resultTypes == nil || resultTypes!.contains(.accounts)) {
|
if !results.accounts.isEmpty && (resultTypes == nil || resultTypes!.contains(.accounts)) {
|
||||||
snapshot.appendSections([.accounts])
|
snapshot.appendSections([.accounts])
|
||||||
|
|
|
@ -60,16 +60,6 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
|
||||||
if let accountIDs = self.accountIDs, let container = mastodonController?.persistentContainer {
|
|
||||||
container.backgroundContext.perform {
|
|
||||||
for id in accountIDs {
|
|
||||||
container.account(for: id, in: container.backgroundContext)?.decrementReferenceCount()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
@ -83,11 +73,7 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
|
||||||
|
|
||||||
tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: CGFloat.leastNormalMagnitude))
|
tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: CGFloat.leastNormalMagnitude))
|
||||||
|
|
||||||
if let accountIDs = accountIDs {
|
if accountIDs == nil {
|
||||||
accountIDs.forEach { (id) in
|
|
||||||
self.mastodonController.persistentContainer.account(for: id)?.incrementReferenceCount()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// account IDs haven't been set, so perform a request to load them
|
// account IDs haven't been set, so perform a request to load them
|
||||||
guard let status = mastodonController.persistentContainer.status(for: statusID) else {
|
guard let status = mastodonController.persistentContainer.status(for: statusID) else {
|
||||||
fatalError("Missing cached status \(statusID)")
|
fatalError("Missing cached status \(statusID)")
|
||||||
|
|
|
@ -42,18 +42,6 @@ class TimelineTableViewController: DiffableTimelineLikeTableViewController<Timel
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
|
||||||
guard let persistentContainer = mastodonController?.persistentContainer,
|
|
||||||
let dataSource = dataSource else { return }
|
|
||||||
// decrement reference counts of any statuses we still have
|
|
||||||
// if the app is currently being quit, this will not affect the persisted data because
|
|
||||||
// the persistent container would already have been saved in SceneDelegate.sceneDidEnterBackground(_:)
|
|
||||||
// todo: remove the whole reference count system
|
|
||||||
for case let .status(id: id, state: _) in dataSource.snapshot().itemIdentifiers {
|
|
||||||
persistentContainer.status(for: id)?.decrementReferenceCount()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
@ -245,12 +233,6 @@ class TimelineTableViewController: DiffableTimelineLikeTableViewController<Timel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func willRemoveItems(_ items: [Item]) {
|
|
||||||
for case let .status(id: id, state: _) in items {
|
|
||||||
mastodonController.persistentContainer.status(for: id)?.decrementReferenceCount()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||||
super.scrollViewWillBeginDragging(scrollView)
|
super.scrollViewWillBeginDragging(scrollView)
|
||||||
|
|
||||||
|
|
|
@ -136,7 +136,7 @@ extension MenuPreviewProvider {
|
||||||
let request = (bookmarked ? Status.unbookmark : Status.bookmark)(status.id)
|
let request = (bookmarked ? Status.unbookmark : Status.bookmark)(status.id)
|
||||||
self.mastodonController?.run(request) { (response) in
|
self.mastodonController?.run(request) { (response) in
|
||||||
if case let .success(status, _) = response {
|
if case let .success(status, _) = response {
|
||||||
self.mastodonController?.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false)
|
self.mastodonController?.persistentContainer.addOrUpdate(status: status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
@ -158,7 +158,7 @@ extension MenuPreviewProvider {
|
||||||
let request = (muted ? Status.unmuteConversation : Status.muteConversation)(status.id)
|
let request = (muted ? Status.unmuteConversation : Status.muteConversation)(status.id)
|
||||||
self.mastodonController?.run(request) { (response) in
|
self.mastodonController?.run(request) { (response) in
|
||||||
if case let .success(status, _) = response {
|
if case let .success(status, _) = response {
|
||||||
self.mastodonController?.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false)
|
self.mastodonController?.persistentContainer.addOrUpdate(status: status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
@ -174,7 +174,7 @@ extension MenuPreviewProvider {
|
||||||
self.mastodonController?.run(request, completion: { [weak self] (response) in
|
self.mastodonController?.run(request, completion: { [weak self] (response) in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
if case let .success(status, _) = response {
|
if case let .success(status, _) = response {
|
||||||
self.mastodonController?.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false)
|
self.mastodonController?.persistentContainer.addOrUpdate(status: status)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
|
@ -189,7 +189,7 @@ extension MenuPreviewProvider {
|
||||||
if case let .success(status, _) = response {
|
if case let .success(status, _) = response {
|
||||||
// todo: this shouldn't really use the viewContext, but for some reason saving the
|
// todo: this shouldn't really use the viewContext, but for some reason saving the
|
||||||
// backgroundContext with the new version of the status isn't updating the viewContext
|
// backgroundContext with the new version of the status isn't updating the viewContext
|
||||||
mastodonController.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false, context: mastodonController.persistentContainer.viewContext)
|
mastodonController.persistentContainer.addOrUpdate(status: status, context: mastodonController.persistentContainer.viewContext)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}), at: 0)
|
}), at: 0)
|
||||||
|
@ -257,7 +257,7 @@ extension MenuPreviewProvider {
|
||||||
actions.append(UIWindowScene.ActivationAction { (_) in
|
actions.append(UIWindowScene.ActivationAction { (_) in
|
||||||
return .init(userActivity: activity(), options: options, preview: nil)
|
return .init(userActivity: activity(), options: options, preview: nil)
|
||||||
})
|
})
|
||||||
} else if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
} else if UIApplication.shared.supportsMultipleScenes {
|
||||||
actions.append(createAction(identifier: "new_window", title: "Open in New Window", systemImageName: "rectangle.badge.plus", handler: { (_) in
|
actions.append(createAction(identifier: "new_window", title: "Open in New Window", systemImageName: "rectangle.badge.plus", handler: { (_) in
|
||||||
UIApplication.shared.requestSceneSessionActivation(nil, userActivity: activity(), options: nil, errorHandler: nil)
|
UIApplication.shared.requestSceneSessionActivation(nil, userActivity: activity(), options: nil, errorHandler: nil)
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -393,7 +393,7 @@ class BaseStatusTableViewCell: UITableViewCell, MenuPreviewProvider {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
if case let .success(newStatus, _) = response {
|
if case let .success(newStatus, _) = response {
|
||||||
self.favorited = newStatus.favourited ?? false
|
self.favorited = newStatus.favourited ?? false
|
||||||
self.mastodonController.persistentContainer.addOrUpdate(status: newStatus, incrementReferenceCount: false)
|
self.mastodonController.persistentContainer.addOrUpdate(status: newStatus)
|
||||||
UIImpactFeedbackGenerator(style: .light).impactOccurred()
|
UIImpactFeedbackGenerator(style: .light).impactOccurred()
|
||||||
} else {
|
} else {
|
||||||
self.favorited = oldValue
|
self.favorited = oldValue
|
||||||
|
@ -435,7 +435,7 @@ class BaseStatusTableViewCell: UITableViewCell, MenuPreviewProvider {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
if case let .success(newStatus, _) = response {
|
if case let .success(newStatus, _) = response {
|
||||||
self.reblogged = newStatus.reblogged ?? false
|
self.reblogged = newStatus.reblogged ?? false
|
||||||
self.mastodonController.persistentContainer.addOrUpdate(status: newStatus, incrementReferenceCount: false)
|
self.mastodonController.persistentContainer.addOrUpdate(status: newStatus)
|
||||||
UIImpactFeedbackGenerator(style: .light).impactOccurred()
|
UIImpactFeedbackGenerator(style: .light).impactOccurred()
|
||||||
} else {
|
} else {
|
||||||
self.reblogged = oldValue
|
self.reblogged = oldValue
|
||||||
|
|
|
@ -293,7 +293,7 @@ extension TimelineStatusTableViewCell: TableViewSwipeActionProvider {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
completion(true)
|
completion(true)
|
||||||
mastodonController.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false)
|
mastodonController.persistentContainer.addOrUpdate(status: status)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -320,7 +320,7 @@ extension TimelineStatusTableViewCell: TableViewSwipeActionProvider {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
completion(true)
|
completion(true)
|
||||||
mastodonController.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false)
|
mastodonController.persistentContainer.addOrUpdate(status: status)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue