convert email creation to async

This commit is contained in:
Satindar Dhillon 2022-04-27 09:48:44 -07:00
parent 7cbfc96b87
commit 090c1acbfd
3 changed files with 46 additions and 42 deletions

View file

@ -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 {

View file

@ -5,7 +5,7 @@ import Models
import SwiftGraphQL
public extension DataService {
func createNewsletterEmailPublisher() -> AnyPublisher<NSManagedObjectID, BasicError> {
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()
}
}

View file

@ -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<Models.NewsletterEmail> = NewsletterEmail.fetchRequest()
fetchRequest.predicate = NSPredicate(
format: "%K == %@", #keyPath(NewsletterEmail.emailId), emailID
)
var email: NewsletterEmail?
context.performAndWait {
email = (try? context.fetch(fetchRequest))?.first
}
return email
}
}