| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- import ConnectIQ
- import CoreData
- import SwiftUI
- extension ContactImage {
- @Observable final class StateModel: BaseStateModel<Provider>, ContactImageManagerDelegate {
- @ObservationIgnored @Injected() var contactImageStorage: ContactImageStorage!
- @ObservationIgnored @Injected() var contactImageManager: ContactImageManager!
- var contactImageEntries = [ContactImageEntry]()
- var units: GlucoseUnits = .mmolL
- // Help Sheet
- var isHelpSheetPresented: Bool = false
- var helpSheetDetent = PresentationDetent.large
- // Current state for live preview
- var state = ContactImageState()
- /// Subscribes to updates and initializes data fetching.
- override func subscribe() {
- units = settingsManager.settings.units
- contactImageManager.delegate = self
- Task {
- /// Initial fetch to fill the ContactImageEntry array
- await fetchContactImageEntriesAndUpdateUI()
- // Initial state update is needed for preview
- await contactImageManager.updateContactImageState()
- }
- }
- func contactImageManagerDidUpdateState(_ state: ContactImageState) {
- Task { @MainActor in
- self.state = state
- }
- }
- /// Fetches all ContactImageEntries and validates them against iOS Contacts.
- func fetchContactImageEntriesAndUpdateUI() async {
- // 1. Get all entries from Core Data
- let cdEntries = await contactImageStorage.fetchContactImageEntries()
- // 2. Validate entries against iOS Contacts
- let validatedEntries = await validateEntries(cdEntries)
- // 3. Update UI with validated entries
- await MainActor.run {
- self.contactImageEntries = validatedEntries
- }
- }
- /// Validates entries against iOS Contacts and removes invalid ones
- private func validateEntries(_ entries: [ContactImageEntry]) async -> [ContactImageEntry] {
- var validated: [ContactImageEntry] = []
- for entry in entries {
- if let contactId = entry.contactId {
- // Check if contact still exists in iOS Contacts
- let exists = await contactImageManager.validateContactExists(withIdentifier: contactId)
- if exists {
- validated.append(entry)
- } else {
- // Contact was deleted in iOS, remove from Core Data
- if let objectID = entry.managedObjectID {
- await contactImageStorage.deleteContactImageEntry(objectID)
- debugPrint("Removed orphaned contact entry: \(entry.name)")
- }
- }
- }
- }
- return validated
- }
- /// Creates a new contact in Apple Contacts and saves it to Core Data.
- /// - Parameters:
- /// - entry: The ContactImageEntry to be saved.
- /// - name: The name of the contact.
- func createAndSaveContactImage(entry: ContactImageEntry, name: String) async {
- // 1. Check for contact access permissions.
- let hasAccess = await contactImageManager.requestAccess()
- guard hasAccess else {
- debugPrint("\(DebuggingIdentifiers.failed) No access to contacts.")
- return
- }
- // 2. Create the contact and retrieve its `identifier`.
- guard let contactId = await contactImageManager.createContact(name: name) else {
- debugPrint("\(DebuggingIdentifiers.failed) Failed to create contact.")
- return
- }
- // 3. Update the entry with the `contactId`.
- var updatedEntry = entry
- updatedEntry.contactId = contactId
- updatedEntry.name = name
- // 4. Save the contact to Core Data.
- await addContactImageEntry(updatedEntry)
- // 5. Update ContactImageState and set the image for the newly created contact
- await contactImageManager.updateContactImageState()
- await contactImageManager.setImageForContact(contactId: contactId)
- }
- /// Adds a ContactImageEntry to Core Data.
- /// - Parameter entry: The ContactImageEntry to be saved.
- func addContactImageEntry(_ entry: ContactImageEntry) async {
- await contactImageStorage.storeContactImageEntry(entry)
- await fetchContactImageEntriesAndUpdateUI()
- }
- /// Deletes a contact from Apple Contacts and Core Data.
- /// - Parameter entry: The ContactImageEntry representing the contact to be deleted.
- func deleteContact(entry: ContactImageEntry) async {
- guard let contactId = entry.contactId else {
- debugPrint("\(DebuggingIdentifiers.failed) Contact does not have a valid ID.")
- return
- }
- // 1. Attempt to delete the contact from Apple Contacts.
- let contactDeleted = await contactImageManager.deleteContact(withIdentifier: contactId)
- if contactDeleted {
- debugPrint("\(DebuggingIdentifiers.succeeded) Contact successfully deleted from Apple Contacts: \(contactId)")
- } else {
- debugPrint("\(DebuggingIdentifiers.failed) Failed to delete contact from Apple Contacts. Check if it exists.")
- }
- // 2. Delete the entry from Core Data.
- if let objectID = entry.managedObjectID {
- await deleteContactImage(objectID: objectID)
- }
- }
- /// Deletes a Core Data entry.
- /// - Parameter objectID: The Managed Object ID of the entry to be deleted.
- func deleteContactImage(objectID: NSManagedObjectID) async {
- await contactImageStorage.deleteContactImageEntry(objectID)
- await fetchContactImageEntriesAndUpdateUI()
- }
- /// Updates a contact in Apple Contacts and Core Data.
- /// - Parameters:
- /// - entry: The ContactImageEntry to be updated.
- func updateContact(with entry: ContactImageEntry) async {
- guard let contactId = entry.contactId else {
- debugPrint("\(DebuggingIdentifiers.failed) Contact does not have a valid ID.")
- return
- }
- // 1. Update the entry in Core Data.
- await updateContactImage(entry)
- // 2. Update the contact in Apple Contacts.
- /// Update name
- let contactUpdated = await contactImageManager
- .updateContact(withIdentifier: contactId, newName: entry.name) // TODO: - Probably not needed anymore
- guard contactUpdated else {
- debugPrint("\(DebuggingIdentifiers.failed) Failed to update contact.")
- return
- }
- /// Update state and image
- await contactImageManager.updateContactImageState()
- await contactImageManager.setImageForContact(contactId: contactId)
- }
- /// Updates a Core Data entry.
- /// - Parameter entry: The updated ContactImageEntry.
- func updateContactImage(_ entry: ContactImageEntry) async {
- await contactImageStorage.updateContactImageEntry(entry)
- await fetchContactImageEntriesAndUpdateUI()
- }
- }
- }
|