use a background context for core data ops in dataservice
This commit is contained in:
@ -15,8 +15,6 @@ public class PersistentContainer: NSPersistentContainer {
|
||||
container.viewContext.automaticallyMergesChangesFromParent = true
|
||||
container.viewContext.name = "viewContext"
|
||||
container.viewContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
|
||||
container.viewContext.undoManager = nil
|
||||
container.viewContext.shouldDeleteInaccessibleFaults = true
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
@ -194,17 +194,21 @@ public extension FeedItemDep {
|
||||
|
||||
public extension Sequence where Element == FeedItemDep {
|
||||
func persist(context: NSManagedObjectContext) -> [LinkedItem]? {
|
||||
let linkedItems = map { $0.asManagedObject(inContext: context) }
|
||||
var result: [LinkedItem]?
|
||||
|
||||
do {
|
||||
try context.save()
|
||||
logger.debug("LinkedItems saved succesfully")
|
||||
return linkedItems
|
||||
} catch {
|
||||
context.rollback()
|
||||
logger.debug("Failed to save LinkedItems: \(error.localizedDescription)")
|
||||
return nil
|
||||
context.performAndWait {
|
||||
let linkedItems = map { $0.asManagedObject(inContext: context) }
|
||||
|
||||
do {
|
||||
try context.save()
|
||||
result = linkedItems
|
||||
logger.debug("LinkedItems saved succesfully")
|
||||
} catch {
|
||||
context.rollback()
|
||||
logger.debug("Failed to save LinkedItems: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,7 +219,13 @@ public extension LinkedItem {
|
||||
format: "id == %@", itemID
|
||||
)
|
||||
|
||||
return (try? context.fetch(fetchRequest))?.first
|
||||
var item: LinkedItem?
|
||||
|
||||
context.performAndWait {
|
||||
item = (try? context.fetch(fetchRequest))?.first
|
||||
}
|
||||
|
||||
return item
|
||||
}
|
||||
|
||||
func update(
|
||||
@ -224,38 +234,42 @@ public extension LinkedItem {
|
||||
newAnchorIndex: Int? = nil,
|
||||
newIsArchivedValue: Bool? = nil
|
||||
) {
|
||||
if let newReadingProgress = newReadingProgress {
|
||||
readingProgress = newReadingProgress
|
||||
}
|
||||
context.perform {
|
||||
if let newReadingProgress = newReadingProgress {
|
||||
self.readingProgress = newReadingProgress
|
||||
}
|
||||
|
||||
if let newAnchorIndex = newAnchorIndex {
|
||||
readingProgressAnchor = Int64(newAnchorIndex)
|
||||
}
|
||||
if let newAnchorIndex = newAnchorIndex {
|
||||
self.readingProgressAnchor = Int64(newAnchorIndex)
|
||||
}
|
||||
|
||||
if let newIsArchivedValue = newIsArchivedValue {
|
||||
isArchived = newIsArchivedValue
|
||||
}
|
||||
if let newIsArchivedValue = newIsArchivedValue {
|
||||
self.isArchived = newIsArchivedValue
|
||||
}
|
||||
|
||||
guard context.hasChanges else { return }
|
||||
guard context.hasChanges else { return }
|
||||
|
||||
do {
|
||||
try context.save()
|
||||
logger.debug("LinkedItem updated succesfully")
|
||||
} catch {
|
||||
context.rollback()
|
||||
logger.debug("Failed to update LinkedItem: \(error.localizedDescription)")
|
||||
do {
|
||||
try context.save()
|
||||
logger.debug("LinkedItem updated succesfully")
|
||||
} catch {
|
||||
context.rollback()
|
||||
logger.debug("Failed to update LinkedItem: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func remove(inContext context: NSManagedObjectContext) {
|
||||
context.delete(self)
|
||||
context.perform {
|
||||
context.delete(self)
|
||||
|
||||
do {
|
||||
try context.save()
|
||||
logger.debug("LinkedItem removed")
|
||||
} catch {
|
||||
context.rollback()
|
||||
logger.debug("Failed to remove LinkedItem: \(error.localizedDescription)")
|
||||
do {
|
||||
try context.save()
|
||||
logger.debug("LinkedItem removed")
|
||||
} catch {
|
||||
context.rollback()
|
||||
logger.debug("Failed to remove LinkedItem: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@ public final class DataService: ObservableObject {
|
||||
self.networker = networker
|
||||
self.persistentContainer = PersistentContainer.make()
|
||||
self.backgroundContext = persistentContainer.newBackgroundContext()
|
||||
backgroundContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
|
||||
|
||||
persistentContainer.loadPersistentStores { _, error in
|
||||
if let error = error {
|
||||
@ -43,20 +44,22 @@ public final class DataService: ObservableObject {
|
||||
}
|
||||
|
||||
public func clearHighlights() {
|
||||
deletedHighlightsIDs.removeAll()
|
||||
backgroundContext.perform {
|
||||
self.deletedHighlightsIDs.removeAll()
|
||||
|
||||
let fetchRequest: NSFetchRequest<Models.Highlight> = Highlight.fetchRequest()
|
||||
let fetchRequest: NSFetchRequest<Models.Highlight> = Highlight.fetchRequest()
|
||||
|
||||
let highlights = (try? persistentContainer.viewContext.fetch(fetchRequest)) ?? []
|
||||
let highlights = (try? self.backgroundContext.fetch(fetchRequest)) ?? []
|
||||
|
||||
for highlight in highlights {
|
||||
persistentContainer.viewContext.delete(highlight)
|
||||
}
|
||||
for highlight in highlights {
|
||||
self.backgroundContext.delete(highlight)
|
||||
}
|
||||
|
||||
do {
|
||||
try persistentContainer.viewContext.save()
|
||||
} catch {
|
||||
logger.debug("failed to delete objects")
|
||||
do {
|
||||
try self.backgroundContext.save()
|
||||
} catch {
|
||||
logger.debug("failed to delete objects")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -44,9 +44,9 @@ public extension DataService {
|
||||
|
||||
switch payload.data {
|
||||
case let .success(linkId):
|
||||
if let linkedItem = LinkedItem.lookup(byID: itemID, inContext: self.persistentContainer.viewContext) {
|
||||
if let linkedItem = LinkedItem.lookup(byID: itemID, inContext: self.backgroundContext) {
|
||||
linkedItem.update(
|
||||
inContext: self.persistentContainer.viewContext,
|
||||
inContext: self.backgroundContext,
|
||||
newIsArchivedValue: archived
|
||||
)
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ public extension DataService {
|
||||
switch payload.data {
|
||||
case let .saved(highlight: highlight):
|
||||
_ = highlight.persist(
|
||||
context: self.persistentContainer.viewContext,
|
||||
context: self.backgroundContext,
|
||||
associatedItemID: articleId
|
||||
)
|
||||
promise(.success(highlight.encoded()))
|
||||
|
||||
@ -59,19 +59,20 @@ public extension DataService {
|
||||
}
|
||||
|
||||
func deletePersistedHighlight(objectID: String) {
|
||||
let context = persistentContainer.viewContext
|
||||
let fetchRequest: NSFetchRequest<Models.Highlight> = Highlight.fetchRequest()
|
||||
fetchRequest.predicate = NSPredicate(format: "id == %@", objectID)
|
||||
for highlight in (try? context.fetch(fetchRequest)) ?? [] {
|
||||
context.delete(highlight)
|
||||
}
|
||||
backgroundContext.perform {
|
||||
let fetchRequest: NSFetchRequest<Models.Highlight> = Highlight.fetchRequest()
|
||||
fetchRequest.predicate = NSPredicate(format: "id == %@", objectID)
|
||||
for highlight in (try? self.backgroundContext.fetch(fetchRequest)) ?? [] {
|
||||
self.backgroundContext.delete(highlight)
|
||||
}
|
||||
|
||||
do {
|
||||
try context.save()
|
||||
print("Highlight deleted succesfully")
|
||||
} catch {
|
||||
context.rollback()
|
||||
print("Failed to delete Highlight: \(error.localizedDescription)")
|
||||
do {
|
||||
try self.backgroundContext.save()
|
||||
print("Highlight deleted succesfully")
|
||||
} catch {
|
||||
self.backgroundContext.rollback()
|
||||
print("Failed to delete Highlight: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,8 +58,8 @@ public extension DataService {
|
||||
|
||||
switch payload.data {
|
||||
case let .saved(highlight: highlight):
|
||||
_ = highlight.persist(
|
||||
context: self.persistentContainer.viewContext,
|
||||
highlight.persist(
|
||||
context: self.backgroundContext,
|
||||
associatedItemID: articleId,
|
||||
oldHighlightsIds: overlapHighlightIdList
|
||||
)
|
||||
|
||||
@ -51,8 +51,8 @@ public extension DataService {
|
||||
|
||||
switch payload.data {
|
||||
case let .success(item):
|
||||
if let linkedItem = LinkedItem.lookup(byID: itemID, inContext: self.persistentContainer.viewContext) {
|
||||
linkedItem.remove(inContext: self.persistentContainer.viewContext)
|
||||
if let linkedItem = LinkedItem.lookup(byID: itemID, inContext: self.backgroundContext) {
|
||||
linkedItem.remove(inContext: self.backgroundContext)
|
||||
}
|
||||
promise(.success(item))
|
||||
case .error(errorCode: _):
|
||||
|
||||
@ -51,9 +51,9 @@ public extension DataService {
|
||||
|
||||
switch payload.data {
|
||||
case let .saved(feedItem):
|
||||
if let linkedItem = LinkedItem.lookup(byID: itemID, inContext: self.persistentContainer.viewContext) {
|
||||
if let linkedItem = LinkedItem.lookup(byID: itemID, inContext: self.backgroundContext) {
|
||||
linkedItem.update(
|
||||
inContext: self.persistentContainer.viewContext,
|
||||
inContext: self.backgroundContext,
|
||||
newReadingProgress: readingProgress,
|
||||
newAnchorIndex: anchorIndex
|
||||
)
|
||||
|
||||
@ -49,15 +49,16 @@ public extension DataService {
|
||||
|
||||
switch payload.data {
|
||||
case let .saved(highlight: highlight):
|
||||
let context = self.persistentContainer.viewContext
|
||||
let fetchRequest: NSFetchRequest<Models.Highlight> = Highlight.fetchRequest()
|
||||
fetchRequest.predicate = NSPredicate(format: "id == %@", highlight.id)
|
||||
let itemID = (try? context.fetch(fetchRequest))?.first?.linkedItemId ?? ""
|
||||
self.backgroundContext.perform {
|
||||
let fetchRequest: NSFetchRequest<Models.Highlight> = Highlight.fetchRequest()
|
||||
fetchRequest.predicate = NSPredicate(format: "id == %@", highlight.id)
|
||||
let itemID = (try? self.backgroundContext.fetch(fetchRequest))?.first?.linkedItemId ?? ""
|
||||
|
||||
_ = highlight.persist(
|
||||
context: self.persistentContainer.viewContext,
|
||||
associatedItemID: itemID
|
||||
)
|
||||
highlight.persist(
|
||||
context: self.backgroundContext,
|
||||
associatedItemID: itemID
|
||||
)
|
||||
}
|
||||
promise(.success(highlight.id))
|
||||
case let .error(errorCode: errorCode):
|
||||
promise(.failure(.message(messageText: errorCode.rawValue)))
|
||||
|
||||
@ -76,26 +76,28 @@ public extension DataService {
|
||||
|
||||
extension DataService {
|
||||
func persistArticleContent(htmlContent: String, slug: String, highlights: [InternalHighlight]) {
|
||||
let fetchRequest: NSFetchRequest<Models.LinkedItem> = LinkedItem.fetchRequest()
|
||||
fetchRequest.predicate = NSPredicate(
|
||||
format: "slug == %@", slug
|
||||
)
|
||||
backgroundContext.perform {
|
||||
let fetchRequest: NSFetchRequest<Models.LinkedItem> = LinkedItem.fetchRequest()
|
||||
fetchRequest.predicate = NSPredicate(
|
||||
format: "slug == %@", slug
|
||||
)
|
||||
|
||||
let linkedItem = try? persistentContainer.viewContext.fetch(fetchRequest).first
|
||||
let linkedItem = try? self.backgroundContext.fetch(fetchRequest).first
|
||||
|
||||
if let linkedItem = linkedItem, let linkedItemID = linkedItem.id {
|
||||
_ = highlights.map {
|
||||
$0.asManagedObject(context: persistentContainer.viewContext, associatedItemID: linkedItemID)
|
||||
if let linkedItem = linkedItem, let linkedItemID = linkedItem.id {
|
||||
_ = highlights.map {
|
||||
$0.asManagedObject(context: self.backgroundContext, associatedItemID: linkedItemID)
|
||||
}
|
||||
linkedItem.htmlContent = htmlContent
|
||||
}
|
||||
linkedItem.htmlContent = htmlContent
|
||||
}
|
||||
|
||||
do {
|
||||
try persistentContainer.viewContext.save()
|
||||
print("ArticleContent saved succesfully")
|
||||
} catch {
|
||||
persistentContainer.viewContext.rollback()
|
||||
print("Failed to save ArticleContent: \(error)")
|
||||
do {
|
||||
try self.backgroundContext.save()
|
||||
print("ArticleContent saved succesfully")
|
||||
} catch {
|
||||
self.backgroundContext.rollback()
|
||||
print("Failed to save ArticleContent: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ public extension DataService {
|
||||
switch payload.data {
|
||||
case let .success(result: result):
|
||||
// save items to coredata
|
||||
_ = result.items.persist(context: self.persistentContainer.viewContext)
|
||||
_ = result.items.persist(context: self.backgroundContext)
|
||||
promise(.success(result))
|
||||
case .error:
|
||||
promise(.failure(.unknown))
|
||||
@ -78,10 +78,16 @@ public extension DataService {
|
||||
}
|
||||
|
||||
func cachedFeedItems() -> [FeedItemDep] {
|
||||
let fetchRequest: NSFetchRequest<Models.LinkedItem> = LinkedItem.fetchRequest()
|
||||
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \LinkedItem.savedAt, ascending: false)]
|
||||
let items = (try? persistentContainer.viewContext.fetch(fetchRequest)) ?? []
|
||||
return items.map { FeedItemDep.make(from: $0) }
|
||||
var result = [FeedItemDep]()
|
||||
|
||||
backgroundContext.performAndWait {
|
||||
let fetchRequest: NSFetchRequest<Models.LinkedItem> = LinkedItem.fetchRequest()
|
||||
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \LinkedItem.savedAt, ascending: false)]
|
||||
let items = (try? backgroundContext.fetch(fetchRequest)) ?? []
|
||||
result = items.map { FeedItemDep.make(from: $0) }
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -50,25 +50,25 @@ struct InternalHighlight: Encodable {
|
||||
context: NSManagedObjectContext,
|
||||
associatedItemID: String,
|
||||
oldHighlightsIds: [String] = []
|
||||
) -> Highlight? {
|
||||
let highlight = asManagedObject(context: context, associatedItemID: associatedItemID)
|
||||
) {
|
||||
context.perform {
|
||||
_ = asManagedObject(context: context, associatedItemID: associatedItemID)
|
||||
|
||||
if !oldHighlightsIds.isEmpty {
|
||||
let fetchRequest: NSFetchRequest<Models.Highlight> = Highlight.fetchRequest()
|
||||
fetchRequest.predicate = NSPredicate(format: "id IN %@", oldHighlightsIds)
|
||||
for highlight in (try? context.fetch(fetchRequest)) ?? [] {
|
||||
context.delete(highlight)
|
||||
if !oldHighlightsIds.isEmpty {
|
||||
let fetchRequest: NSFetchRequest<Models.Highlight> = Highlight.fetchRequest()
|
||||
fetchRequest.predicate = NSPredicate(format: "id IN %@", oldHighlightsIds)
|
||||
for highlight in (try? context.fetch(fetchRequest)) ?? [] {
|
||||
context.delete(highlight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
try context.save()
|
||||
print("Highlight saved succesfully")
|
||||
return highlight
|
||||
} catch {
|
||||
context.rollback()
|
||||
print("Failed to save Highlight: \(error.localizedDescription)")
|
||||
return nil
|
||||
do {
|
||||
try context.save()
|
||||
print("Highlight saved succesfully")
|
||||
} catch {
|
||||
context.rollback()
|
||||
print("Failed to save Highlight: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user