diff --git a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift index dc30ef228..ee77784a3 100644 --- a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift @@ -51,7 +51,15 @@ enum PDFProvider { return currentViewer } - return try? await dataService.fetchViewer() + guard let viewerObjectID = try? await dataService.fetchViewer() else { return nil } + + var result: Viewer? + + await dataService.viewContext.perform { + result = dataService.viewContext.object(with: viewerObjectID) as? Viewer + } + + return result }() if let viewer = viewer { diff --git a/apple/OmnivoreKit/Sources/App/Views/Profile/NewsletterEmailsView.swift b/apple/OmnivoreKit/Sources/App/Views/Profile/NewsletterEmailsView.swift index da89450fa..ae9660ffc 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Profile/NewsletterEmailsView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Profile/NewsletterEmailsView.swift @@ -16,9 +16,11 @@ final class NewsletterEmailsViewModel: ObservableObject { dataService.newsletterEmailsPublisher().sink( receiveCompletion: { _ in }, - receiveValue: { [weak self] result in + receiveValue: { [weak self] objectIDs in self?.isLoading = false - self?.emails = result + dataService.viewContext.perform { + self?.emails = objectIDs.compactMap { dataService.viewContext.object(with: $0) as? NewsletterEmail } + } self?.hasLoadedInitialEmails = true } ) @@ -32,9 +34,13 @@ final class NewsletterEmailsViewModel: ObservableObject { receiveCompletion: { [weak self] _ in self?.isLoading = false }, - receiveValue: { [weak self] result in + receiveValue: { [weak self] objectID in self?.isLoading = false - self?.emails.insert(result, at: 0) + dataService.viewContext.perform { + if let item = dataService.viewContext.object(with: objectID) as? NewsletterEmail { + self?.emails.insert(item, at: 0) + } + } } ) .store(in: &subscriptions) diff --git a/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift b/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift index 5727d96d2..07d967767 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift @@ -18,8 +18,21 @@ import Views } func loadProfileData(dataService: DataService) async { - guard let viewer = try? await dataService.fetchViewer() else { return } + if let currentViewer = dataService.currentViewer { + loadProfileCardData(viewer: currentViewer) + return + } + guard let viewerObjectID = try? await dataService.fetchViewer() else { return } + + await dataService.viewContext.perform { + if let viewer = dataService.viewContext.object(with: viewerObjectID) as? Viewer { + self.loadProfileCardData(viewer: viewer) + } + } + } + + private func loadProfileCardData(viewer: Viewer) { profileCardData = ProfileCardData( name: viewer.unwrappedName, username: viewer.unwrappedUsername, diff --git a/apple/OmnivoreKit/Sources/App/Views/RootView/RootViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/RootView/RootViewModel.swift index 9def7c1df..0ad448f62 100644 --- a/apple/OmnivoreKit/Sources/App/Views/RootView/RootViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/RootView/RootViewModel.swift @@ -64,9 +64,11 @@ public final class RootViewModel: ObservableObject { return } - if let username = try? await services.dataService.fetchViewer().username { - let path = linkRequestPath(username: username, requestID: linkRequestID) - webLinkPath = SafariWebLinkPath(id: UUID(), path: path) + if let viewerObjectID = try? await services.dataService.fetchViewer() { + if let viewer = services.dataService.viewContext.object(with: viewerObjectID) as? Viewer { + let path = linkRequestPath(username: viewer.unwrappedUsername, requestID: linkRequestID) + webLinkPath = SafariWebLinkPath(id: UUID(), path: path) + } } } diff --git a/apple/OmnivoreKit/Sources/Services/DataService/DataService.swift b/apple/OmnivoreKit/Sources/Services/DataService/DataService.swift index ee694d5d8..4f53eabcb 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/DataService.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/DataService.swift @@ -17,7 +17,7 @@ public final class DataService: ObservableObject { let backgroundContext: NSManagedObjectContext var subscriptions = Set() - var viewContext: NSManagedObjectContext { + public var viewContext: NSManagedObjectContext { persistentContainer.viewContext } diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/CreateNewsletterEmailMutation.swift b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/CreateNewsletterEmailMutation.swift index 38b9cd6de..4eda0576f 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/CreateNewsletterEmailMutation.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/CreateNewsletterEmailMutation.swift @@ -5,7 +5,7 @@ import Models import SwiftGraphQL public extension DataService { - func createNewsletterEmailPublisher() -> AnyPublisher { + func createNewsletterEmailPublisher() -> AnyPublisher { enum MutationResult { case saved(newsletterEmail: InternalNewsletterEmail) case error(errorCode: Enums.CreateNewsletterEmailErrorCode) @@ -45,8 +45,8 @@ public extension DataService { switch payload.data { case let .saved(newsletterEmail: newsletterEmail): - if let newsletterEmailObject = newsletterEmail.persist(context: self.persistentContainer.viewContext) { - promise(.success(newsletterEmailObject)) + if let newsletterEmailObjectID = newsletterEmail.persist(context: self.backgroundContext) { + promise(.success(newsletterEmailObjectID)) } else { promise(.failure(.message(messageText: "coredata error"))) } diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Queries/NewsletterEmailsQuery.swift b/apple/OmnivoreKit/Sources/Services/DataService/Queries/NewsletterEmailsQuery.swift index 47afa350c..1465c15e0 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Queries/NewsletterEmailsQuery.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Queries/NewsletterEmailsQuery.swift @@ -1,10 +1,11 @@ import Combine +import CoreData import Foundation import Models import SwiftGraphQL public extension DataService { - func newsletterEmailsPublisher() -> AnyPublisher<[NewsletterEmail], ServerError> { + func newsletterEmailsPublisher() -> AnyPublisher<[NSManagedObjectID], ServerError> { enum QueryResult { case success(result: [InternalNewsletterEmail]) case error(error: String) @@ -43,8 +44,8 @@ public extension DataService { case let .success(payload): switch payload.data { case let .success(result: result): - if let newsletterEmailObject = result.persist(context: self.persistentContainer.viewContext) { - promise(.success(newsletterEmailObject)) + if let newsletterEmailObjectIDs = result.persist(context: self.backgroundContext) { + promise(.success(newsletterEmailObjectIDs)) } else { promise(.failure(.unknown)) } diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Queries/ViewerFetcher.swift b/apple/OmnivoreKit/Sources/Services/DataService/Queries/ViewerFetcher.swift index b4bc29196..bde4237dc 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Queries/ViewerFetcher.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Queries/ViewerFetcher.swift @@ -6,7 +6,7 @@ import SwiftGraphQL import Utils public extension DataService { - func fetchViewer() async throws -> Viewer { + func fetchViewer() async throws -> NSManagedObjectID { let selection = Selection { ViewerInternal( userID: try $0.id(), @@ -36,8 +36,8 @@ public extension DataService { DataService.registerIntercomUser?(payload.data.userID) } - if let self = self, let viewer = payload.data.persist(context: self.persistentContainer.viewContext) { - continuation.resume(returning: viewer) + if let self = self, let viewerID = payload.data.persist(context: self.backgroundContext) { + continuation.resume(returning: viewerID) } else { continuation.resume(throwing: BasicError.message(messageText: "coredata error")) } @@ -55,21 +55,26 @@ private struct ViewerInternal { let name: String let profileImageURL: String? - func persist(context: NSManagedObjectContext) -> Viewer? { - let viewer = Viewer(context: context) - viewer.userID = userID - viewer.username = username - viewer.name = name - viewer.profileImageURL = profileImageURL + func persist(context: NSManagedObjectContext) -> NSManagedObjectID? { + var objectID: NSManagedObjectID? - do { - try context.save() - logger.debug("Viewer saved succesfully") - return viewer - } catch { - context.rollback() - logger.debug("Failed to save Viewer: \(error.localizedDescription)") - return nil + context.performAndWait { + let viewer = Viewer(context: context) + viewer.userID = userID + viewer.username = username + viewer.name = name + viewer.profileImageURL = profileImageURL + + do { + try context.save() + logger.debug("Viewer saved succesfully") + objectID = viewer.objectID + } catch { + context.rollback() + logger.debug("Failed to save Viewer: \(error.localizedDescription)") + } } + + return objectID } } diff --git a/apple/OmnivoreKit/Sources/Services/InternalModels/InternalNewsletterEmail.swift b/apple/OmnivoreKit/Sources/Services/InternalModels/InternalNewsletterEmail.swift index 26c974712..7b2ad3e58 100644 --- a/apple/OmnivoreKit/Sources/Services/InternalModels/InternalNewsletterEmail.swift +++ b/apple/OmnivoreKit/Sources/Services/InternalModels/InternalNewsletterEmail.swift @@ -7,18 +7,23 @@ struct InternalNewsletterEmail { let email: String let confirmationCode: String? - func persist(context: NSManagedObjectContext) -> NewsletterEmail? { - let newsletterEmail = asManagedObject(inContext: context) + func persist(context: NSManagedObjectContext) -> NSManagedObjectID? { + var objectID: NSManagedObjectID? - do { - try context.save() - logger.debug("NewsletterEmail saved succesfully") - return newsletterEmail - } catch { - context.rollback() - logger.debug("Failed to save NewsletterEmail: \(error.localizedDescription)") - return nil + context.performAndWait { + let newsletterEmail = asManagedObject(inContext: context) + + do { + try context.save() + logger.debug("NewsletterEmail saved succesfully") + objectID = newsletterEmail.objectID + } catch { + context.rollback() + logger.debug("Failed to save NewsletterEmail: \(error.localizedDescription)") + } } + + return objectID } func asManagedObject(inContext context: NSManagedObjectContext) -> NewsletterEmail { @@ -31,17 +36,21 @@ struct InternalNewsletterEmail { } extension Sequence where Element == InternalNewsletterEmail { - func persist(context: NSManagedObjectContext) -> [NewsletterEmail]? { - let newsletterEmails = map { $0.asManagedObject(inContext: context) } + func persist(context: NSManagedObjectContext) -> [NSManagedObjectID]? { + var result: [NSManagedObjectID]? - do { - try context.save() - logger.debug("NewsletterEmail saved succesfully") - return newsletterEmails - } catch { - context.rollback() - logger.debug("Failed to save NewsletterEmail: \(error.localizedDescription)") - return nil + context.performAndWait { + let newsletterEmails = map { $0.asManagedObject(inContext: context) } + do { + try context.save() + logger.debug("NewsletterEmail saved succesfully") + result = newsletterEmails.map(\.objectID) + } catch { + context.rollback() + logger.debug("Failed to save NewsletterEmail: \(error.localizedDescription)") + } } + + return result } }