ios – SwiftUI MVVM – Dealing with Momentary Modifications in Edit View With out Straight Modifying Core Information


I am growing a SwiftUI app following the MVVM sample. I’ve a ProfileDataManager that holds a Core Information mannequin for the person profile. Right here’s the construction I’m utilizing:

Person ViewModel and Person View

The UserViewModel makes use of ProfileDataManager to fetch and supply the person’s profile information:

class UserViewModel: ObservableObject {
    @Printed var userProfile: UserProfile?
    non-public var dataManager: ProfileDataManager

    init(dataManager: ProfileDataManager = .shared) {
        self.dataManager = dataManager
        dataManager.$userProfile
            .assign(to: &$userProfile)
    }
}

My UserView can entry all of the profile information:

struct UserView: View {
    @StateObject non-public var viewModel = UserViewModel()
    @State non-public var showEditProfile = false

    var physique: some View {
        // ...
        Button("Edit Profile") {
            showEditProfile = true
        }
        .sheet(isPresented: $showEditProfile) {
            if let userProfile = viewModel.userProfile {
                EditView(userProfile: userProfile)
            }
        }
    }
}

I move the Core Information mannequin UserProfile to EditView.

Edit View and Edit ViewModel

The EditView permits the person to edit their profile.

struct EditView: View {
    @Atmosphere(.presentationMode) var presentationMode
    @StateObject non-public var viewModel: EditViewModel
    @State non-public var showImagePicker = false
    @State non-public var showGenderModal = false

    init(userProfile: UserProfile) {
        _viewModel = StateObject(wrappedValue: EditViewModel(userProfile: userProfile))
    }

    var physique: some View {
        NavigationView {
            Type {
                Part(header: Textual content("Avatar")) {
                    AvatarSection(avatarData: Binding(
                        get: { viewModel.userProfile.avatarData },
                        set: { viewModel.userProfile.avatarData = $0 }
                    )) {
                        showImagePicker = true
                    }
                }

                Part(header: Textual content("Private Data")) {
                    AttributeButton(title: "Gender", worth: viewModel.userProfile.gender) {
                        showGenderModal = true
                    }
                    .sheet(isPresented: $showGenderModal) {
                        GenderModalView(gender: $viewModel.userProfile.gender)
                    }
                }
            }
            .navigationBarTitle("Edit Profile", displayMode: .inline)
            .navigationBarItems(
                main: Button("Cancel") {
                    viewModel.cancelChanges()
                    presentationMode.wrappedValue.dismiss()
                },
                trailing: Button("Save") {
                    viewModel.saveChanges()
                    presentationMode.wrappedValue.dismiss()
                }
            )
        }
    }
}

Right here’s the EditViewModel the place I deal with save and cancel performance:

class EditViewModel: ObservableObject {
    @Printed var userProfile: UserProfile
    non-public var dataManager: ProfileDataManager

    init(userProfile: UserProfile, dataManager: ProfileDataManager = .shared) {
        self.userProfile = userProfile
        self.dataManager = dataManager
    }

    func saveChanges() {
        dataManager.saveContext()
    }

    func cancelChanges() {
        dataManager.rollback()
    }
}

Drawback

Presently, modifications made in EditView are instantly mirrored in UserProfile, even earlier than clicking “Save.” In fact, it’s discarded if the person clicks on “Cancel”. However nonetheless, the attributes are binded to the UserProfile. This conduct is counter-intuitive, as an Edit Web page should not have entry to that. I need the modifications to be utilized solely after saving. Canceling ought to dismiss the view with out modifying the UserProfile. I’m saying this as a result of Canceling with out modifying something within the EditView might have an effect on the UserView information!

I tried to move a duplicate of UserProfile to EditView, however this prompted important reminiscence and CPU utilization points. I did this by making an extension clone() to NSManagedObject. Cloning the UserProfile in UserView and passing the cloned UserProfile to EditView.

NSManagedObject:

extension NSManagedObject {
    func clone() -> NSManagedObject? {
        guard let context = self.managedObjectContext else { return nil }
        let entityName = self.entity.title ?? ""
        guard let clonedObject = NSEntityDescription.insertNewObject(forEntityName: entityName, into: context) as? Self else { return nil }

        for attribute in self.entity.attributesByName {
            clonedObject.setValue(self.worth(forKey: attribute.key), forKey: attribute.key)
        }

        return clonedObject
    }
}

UserViewModel:

func cloneUserProfile() -> UserProfile? {
    return userProfile?.clone() as? UserProfile
}

func saveProfile(_ modifiedProfile: UserProfile) {
    guard let userProfile = userProfile else { return }
    userProfile.setValuesForKeys(modifiedProfile.dictionaryWithValues(forKeys: Array(modifiedProfile.entity.attributesByName.keys)))
    dataManager.saveContext()
}

UserView:

.sheet(isPresented: $showEditProfile) {
    if let clonedProfile = viewModel.cloneUserProfile() {
        EditView(userProfile: clonedProfile) { modifiedProfile in
            viewModel.saveProfile(modifiedProfile)
        }
    }
}

EditView:

trailing: Button("Save") {
    onSave(viewModel.userProfile)
    presentationMode.wrappedValue.dismiss()
}

However once more, 95% CPU Utilization and 40GB Mem Utilization…

Momentary Information for Modal Views

Right here’s an instance of how I am utilizing momentary information in modal views, just like the gender picker:

struct GenderModalView: View {
    @Atmosphere(.presentationMode) var presentationMode
    @State non-public var tempGender: String
    @Binding var gender: String?

    init(gender: Binding) {
        self._gender = gender
        self._tempGender = State(initialValue: gender.wrappedValue ?? "Different")
    }

    var physique: some View {
        NavigationView {
            Type {
                Picker("Gender", choice: $tempGender) {
                    Textual content("Male").tag("Male")
                    Textual content("Feminine").tag("Feminine")
                    Textual content("Different").tag("Different")
                }
                .pickerStyle(.segmented)

                Part {
                    Button("Take away Gender") {
                        gender = nil
                        presentationMode.wrappedValue.dismiss()
                    }
                    .foregroundColor(.purple)
                }
            }
            .navigationBarTitle("Gender", displayMode: .inline)
            .navigationBarItems(
                main: Button("Cancel") {
                    presentationMode.wrappedValue.dismiss()
                },
                trailing: Button("Save") {
                    gender = tempGender
                    presentationMode.wrappedValue.dismiss()
                }
            )
        }
    }
}

Query

  1. How ought to information be dealt with in EditView to keep away from untimely modifications to the mannequin?
  2. Am I following greatest practices in MVVM right here?
  3. Am I doing something silly?

Any recommendation on information administration, accountability separation, or efficiency optimizations could be vastly appreciated! I’m making an attempt to observe greatest practices.

My MAIN query is: How do I move the UserProfile information to the Edit (with out the Edit modifications straight affecting the storage) for manipulation, and after I “Save”, ship the brand new information again to replace it in UserProfile in a easy and environment friendly method whereas following greatest practices?

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles