swiftstruct

Swift User Struct Error: Return from initializer without initializing all stored properties


I am building a profile page and I am trying to make the profile picture save even after the user signs out. This is my user struct:

struct User: Codable {
    var uid: String
    var email: String
    var firstName: String
    var lastName: String
    var signUpDate: Date
    var bio: String
    var interests: [String]
    var profilePictureURL: URL?
}

This is my profile page view(the part that is causing the error):

struct ProfileView: View {
    @ObservedObject var userViewModel: UserViewModel
    @State private var bio = UserDefaults.standard.string(forKey: "bio") ?? "Car enthusiast | Eco-friendly driver | Developer"
    @State private var interests = UserDefaults.standard.string(forKey: "interests") ?? "Travel, Coding, Green Living"
    @State private var isEditingBio = false
    @State private var isEditingInterests = false
    @State private var isEditingProfile = false
    @State private var selectedImage: UIImage? {
        didSet {
            if let selectedImage = selectedImage {
                if let imageData = selectedImage.jpegData(compressionQuality: 0.8) {
                    UserDefaults.standard.set(imageData, forKey: "profileImage")
                }
            }
        }
    }
   init(userViewModel: UserViewModel) {
    self.userViewModel = userViewModel
    if let imageData = UserDefaults.standard.data(forKey: "profileImage"),
       let image = UIImage(data: imageData) {
        _selectedImage = State(initialValue: image)
    } else {
        // Initialize with a default image if no profile image is found
        _selectedImage = State(initialValue: UIImage(systemName: "person.circle.fill")!)
    }
}

The error is occuring at the second init statement and the error is: Return from initializer without initializing all stored properties

I have tried looking at other solutions on stack overflow but I don't really understand them. I also tried initializing the other values by inputting a user:user parameter and then doing let uid=user.uid and so on and so forth for the other properties. I am confused on how to fix this. Thank you for any help!


Solution

  • Error show in second init is because you are not intialize userViewModel in it. So all you need to do is move code from second init to init(userViewModel::

    init(userViewModel: UserViewModel) {
        self.userViewModel = userViewModel
        if let imageData = UserDefaults.standard.data(forKey: "profileImage"),
           let image = UIImage(data: imageData) {
            _selectedImage = State(initialValue: image)
        } else {
            // Initialize with a default image if no profile image is found
            _selectedImage = State(initialValue: UIImage(systemName: "person.circle.fill")!)
        }
    }
    

    Also, you should use onChange modifer to observe change of @State instead of using didSet. To interact with UserDefaults in SwiftUI you should use @AppStorage too.

    struct ProfileView: View {
        @ObservedObject var userViewModel: UserViewModel
        @AppStorage("bio") private var bio: String = "Car enthusiast | Eco-friendly driver | Developer" // <- change to @AppStorage
        @AppStorage("bio") private var interests: String = "Travel, Coding, Green Living" // <- change to @AppStorage
        @State private var isEditingBio = false
        @State private var isEditingInterests = false
        @State private var isEditingProfile = false
        @State private var selectedImage: UIImage?
        
        init(userViewModel: UserViewModel) {
            self.userViewModel = userViewModel
            if let imageData = UserDefaults.standard.data(forKey: "profileImage"),
               let image = UIImage(data: imageData) {
                _selectedImage = State(initialValue: image)
            } else {
                // Initialize with a default image if no profile image is found
                _selectedImage = State(initialValue: UIImage(systemName: "person.circle.fill")!)
            }
        }
        
        var body: some View {
            ...
                .onChange(of: selectedImage) { newValue in
                    if let selectedImage = newValue {
                        if let imageData = selectedImage.jpegData(compressionQuality: 0.8) {
                            UserDefaults.standard.set(imageData, forKey: "profileImage")
                        }
                    }
                }
        }
    }