11.2 C
Canberra
Sunday, October 26, 2025

ios – SwiftUI MVVM – Dealing with Short-term Adjustments in Edit View With out Straight Modifying Core Knowledge


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

Consumer ViewModel and Consumer View

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

class UserViewModel: ObservableObject {
    @Revealed 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 go the Core Knowledge mannequin UserProfile to EditView.

Edit View and Edit ViewModel

The EditView permits the consumer to edit their profile.

struct EditView: View {
    @Setting(.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 {
            Kind {
                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 {
    @Revealed 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()
    }
}

Downside

Presently, modifications made in EditView are instantly mirrored in UserProfile, even earlier than clicking “Save.” After all, it’s discarded if the consumer clicks on “Cancel”. However nonetheless, the attributes are binded to the UserProfile. This habits 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 go a duplicate of UserProfile to EditView, however this precipitated 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…

Short-term Knowledge for Modal Views

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

struct GenderModalView: View {
    @Setting(.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 {
            Kind {
                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 finest practices in MVVM right here?
  3. Am I doing something silly?

Any recommendation on information administration, duty separation, or efficiency optimizations could be drastically appreciated! I’m making an attempt to comply with finest practices.

My MAIN query is: How do I go 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 finest practices?

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

[td_block_social_counter facebook="tagdiv" twitter="tagdivofficial" youtube="tagdiv" style="style8 td-social-boxed td-social-font-icons" tdc_css="eyJhbGwiOnsibWFyZ2luLWJvdHRvbSI6IjM4IiwiZGlzcGxheSI6IiJ9LCJwb3J0cmFpdCI6eyJtYXJnaW4tYm90dG9tIjoiMzAiLCJkaXNwbGF5IjoiIn0sInBvcnRyYWl0X21heF93aWR0aCI6MTAxOCwicG9ydHJhaXRfbWluX3dpZHRoIjo3Njh9" custom_title="Stay Connected" block_template_id="td_block_template_8" f_header_font_family="712" f_header_font_transform="uppercase" f_header_font_weight="500" f_header_font_size="17" border_color="#dd3333"]
- Advertisement -spot_img

Latest Articles