diff --git a/apple/OmnivoreKit/Sources/App/Views/Profile/NewsletterEmailsView.swift b/apple/OmnivoreKit/Sources/App/Views/Profile/NewsletterEmailsView.swift index ae9660ffc..7e148ceef 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Profile/NewsletterEmailsView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Profile/NewsletterEmailsView.swift @@ -4,8 +4,7 @@ import Services import SwiftUI import Views -final class NewsletterEmailsViewModel: ObservableObject { - private var hasLoadedInitialEmails = false +@MainActor final class NewsletterEmailsViewModel: ObservableObject { @Published var isLoading = false @Published var emails = [NewsletterEmail]() @@ -21,29 +20,23 @@ final class NewsletterEmailsViewModel: ObservableObject { dataService.viewContext.perform { self?.emails = objectIDs.compactMap { dataService.viewContext.object(with: $0) as? NewsletterEmail } } - self?.hasLoadedInitialEmails = true } ) .store(in: &subscriptions) } - func createEmail(dataService: DataService) { + func createEmail(dataService: DataService) async { isLoading = true - dataService.createNewsletterEmailPublisher().sink( - receiveCompletion: { [weak self] _ in - self?.isLoading = false - }, - receiveValue: { [weak self] objectID in - self?.isLoading = false - dataService.viewContext.perform { - if let item = dataService.viewContext.object(with: objectID) as? NewsletterEmail { - self?.emails.insert(item, at: 0) - } + if let objectID = try? await dataService.createNewsletter() { + await dataService.viewContext.perform { [weak self] in + if let item = dataService.viewContext.object(with: objectID) as? NewsletterEmail { + self?.emails.insert(item, at: 0) } } - ) - .store(in: &subscriptions) + } + + isLoading = false } } @@ -65,7 +58,7 @@ struct NewsletterEmailsView: View { .listStyle(InsetListStyle()) #endif } - .onAppear { viewModel.loadEmails(dataService: dataService) } + .task { viewModel.loadEmails(dataService: dataService) } } private var innerBody: some View { @@ -73,7 +66,7 @@ struct NewsletterEmailsView: View { Section(footer: Text(footerText)) { Button( action: { - viewModel.createEmail(dataService: dataService) + Task { await viewModel.createEmail(dataService: dataService) } }, label: { HStack { diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/CreateNewsletterEmailMutation.swift b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/CreateNewsletterEmailMutation.swift index 4eda0576f..454a76c46 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 createNewsletter() async throws -> NSManagedObjectID { enum MutationResult { case saved(newsletterEmail: InternalNewsletterEmail) case error(errorCode: Enums.CreateNewsletterEmailErrorCode) @@ -34,32 +34,24 @@ public extension DataService { let path = appEnvironment.graphqlPath let headers = networker.defaultHeaders - return Deferred { - Future { promise in - send(mutation, to: path, headers: headers) { result in - switch result { - case let .success(payload): - if let graphqlError = payload.errors { - promise(.failure(.message(messageText: "graphql error: \(graphqlError)"))) - } + return try await withCheckedThrowingContinuation { continuation in + send(mutation, to: path, headers: headers) { [weak self] queryResult in + guard let payload = try? queryResult.get(), let self = self else { + continuation.resume(throwing: BasicError.message(messageText: "network error")) + return + } - switch payload.data { - case let .saved(newsletterEmail: newsletterEmail): - if let newsletterEmailObjectID = newsletterEmail.persist(context: self.backgroundContext) { - promise(.success(newsletterEmailObjectID)) - } else { - promise(.failure(.message(messageText: "coredata error"))) - } - case let .error(errorCode: errorCode): - promise(.failure(.message(messageText: errorCode.rawValue))) - } - case .failure: - promise(.failure(.message(messageText: "graphql error"))) + switch payload.data { + case let .saved(newsletterEmail: newsletterEmail): + if let newsletterEmailObjectID = newsletterEmail.persist(context: self.backgroundContext) { + continuation.resume(returning: newsletterEmailObjectID) + } else { + continuation.resume(throwing: BasicError.message(messageText: "CoreData error")) } + case let .error(errorCode: errorCode): + continuation.resume(throwing: BasicError.message(messageText: errorCode.rawValue)) } } } - .receive(on: DispatchQueue.main) - .eraseToAnyPublisher() } } diff --git a/apple/OmnivoreKit/Sources/Services/InternalModels/InternalNewsletterEmail.swift b/apple/OmnivoreKit/Sources/Services/InternalModels/InternalNewsletterEmail.swift index 7b2ad3e58..1ad7b9553 100644 --- a/apple/OmnivoreKit/Sources/Services/InternalModels/InternalNewsletterEmail.swift +++ b/apple/OmnivoreKit/Sources/Services/InternalModels/InternalNewsletterEmail.swift @@ -27,7 +27,9 @@ struct InternalNewsletterEmail { } func asManagedObject(inContext context: NSManagedObjectContext) -> NewsletterEmail { - let newsletterEmail = NewsletterEmail(entity: NewsletterEmail.entity(), insertInto: context) + let existingEmail = NewsletterEmail.lookup(byID: emailId, inContext: context) + let newsletterEmail = existingEmail ?? NewsletterEmail(entity: NewsletterEmail.entity(), insertInto: context) + newsletterEmail.emailId = emailId newsletterEmail.email = email newsletterEmail.confirmationCode = confirmationCode @@ -54,3 +56,20 @@ extension Sequence where Element == InternalNewsletterEmail { return result } } + +extension NewsletterEmail { + static func lookup(byID emailID: String, inContext context: NSManagedObjectContext) -> NewsletterEmail? { + let fetchRequest: NSFetchRequest = NewsletterEmail.fetchRequest() + fetchRequest.predicate = NSPredicate( + format: "%K == %@", #keyPath(NewsletterEmail.emailId), emailID + ) + + var email: NewsletterEmail? + + context.performAndWait { + email = (try? context.fetch(fetchRequest))?.first + } + + return email + } +}