Search code examples
core-dataswiftui

How to pass CoreData Object across views


So I'm working on my own SwiftUI project to try and figure out how to integrate SwiftUI with CoreData for data persistence. I've ran into a bit of trouble and I'm trying to figure out how to Fetch a User Entity that I create in the "SignUp View" or an existing user from the "Login View" and how to pass that User object onto the ContentView, which is the first View that appears once the user is logged in.

First of all I'm not sure if I should be using a @Binding property wrapper inside the ContentView in order to capture the object that is passed in from the "Login View" or "SignUp View", or if I should be using just a constant but without a wrapper (I'm guessing that @Binding is the way to go here, but I'd like some clarification on that).

Second, I'm trying to figure out why adding the @Binding wrapper forces the preview to add a comma prior to entering the selectedUser property? Also, how would I go about addressing the Previews error?

Third, I'm a bit confused about when and where I should be importing CoreData. Do I even have to import it? and if so, in which cases do I have to import CoreData?

I know it's a lot of noob questions, but I am a noob and I've been looking all over for the answers and I keep getting different answers wherever I look. Much appreciated. Thanks!

import SwiftUI

struct ContentView: View {
            
    @Environment(\.managedObjectContext) var moc
    
    @Binding var selectedUser : User //Assign logged in user to this variable...
    
    
    var body: some View {
        
            VStack{
                Spacer()
                VStack(spacing: 50){
                    
                    NavigationLink(destination: SupportView()){
                        awButton(content: "Request Support", backColor: Color(#colorLiteral(red: 0, green: 0.723585546, blue: 0.9907287955, alpha: 1)))
                            .shadow(color: Color.primary.opacity(0.5), radius: 20, x: 0, y: 20)
                            .rotation3DEffect(Angle(degrees:10), axis: (x: 10.0, y: 0, z: 0))
                    }

                    NavigationLink(destination: QuoteView()){
                        awButton(content: "Request Quote", backColor: Color(#colorLiteral(red: 0.9372549057, green: 0.3490196168, blue: 0.1921568662, alpha: 1)))
                            .shadow(color: Color.primary.opacity(0.5), radius: 20, x: 0, y: 20)
                            .rotation3DEffect(Angle(degrees:10), axis: (x: 10.0, y: 0, z: 0))
                    }

                    NavigationLink(destination: TicketView()){
                        awButton(content: "Ticket Status", backColor: Color(#colorLiteral(red: 0.4666666687, green: 0.7647058964, blue: 0.2666666806, alpha: 1)))
                            .shadow(color: Color.primary.opacity(0.5), radius: 20, x: 0, y: 20)
                            .rotation3DEffect(Angle(degrees:10), axis: (x: 10.0, y: 0, z: 0))
                    }
                }
                Spacer()
            }
            .navigationTitle("AccessWeb")
            .navigationBarItems(trailing: HStack{
                Image(systemName: "bell")
                    .font(.system(size: 30))
                Image(systemName:"person.circle")
                    .font(.system(size: 30))
                Text("Tester")
                    .font(.system(size: 20))
            })
       }
}

struct ContentView_Previews: PreviewProvider {

    static var previews: some View {
        
        ContentView(, selectedUser: <#Binding<User?>#>)
    }
}



import SwiftUI
import CoreData

struct SignUpView: View {

@State private var name: String = ""
@State private var company: String = ""
@State private var user: String = ""
@State private var pass: String = ""
@State private var photo: Image?

@State private var showingImagePicker = false
@State private var inputImage: UIImage?

@State private var isSignUpValid: Bool = false
@State private var shoudlShowSignUpAlert: Bool = false
    
@Environment(\.managedObjectContext) var moc

var body: some View {
    
    VStack{

        TextField("Company", text: $company)
            .padding(.leading)
        Divider()
            .padding(.bottom)
        
        TextField("First and Last Name", text: $name)
            .padding(.leading)
        Divider()
            .padding(.bottom, 30)
        
        TextField("Username", text: $user)
            .padding(.leading)
        Divider()
            .padding(.bottom)
        
        SecureField("Password", text: $pass)
            .padding(.leading)
            
        Divider()
            .padding(.bottom, 50)
        
        ZStack{
            Circle()
                .fill(Color.secondary)
                .frame(width: 200, height: 200)
                
            if photo != nil {
                photo?
                    .resizable()
                    .scaledToFit()
                    .clipShape(Circle())
                    .frame(width: 300, height: 300)
            } else {
                Text("Tap to select a photo")
                    .foregroundColor(.white)
                    .font(.headline)
            }
        }
        .onTapGesture {
            self.showingImagePicker = true
        }
        .padding(.bottom, 50)
        
        
        NavigationLink(
            destination: ContentView(),
            isActive: self.$isSignUpValid){
            Text("Sign Up")
                .foregroundColor(.blue)
                .onTapGesture {

                    //Pass the appropriate User data onto the ContentView...
                    
                    //TriggerLogin
                    let isLoginValid = self.user == "Tester" && self.pass == "Test1234"
                    
                    if isLoginValid {
                        self.isSignUpValid = true //trigger NavLink
                        
                        let newUser = User(context: self.moc)
                        newUser.company = company
                        newUser.name = name
                        newUser.username = user
                        newUser.password = pass
                        newUser.photo = coreDataObjectFromImages(image: (inputImage ?? UIImage(systemName:"person.circle"))!)

                        do {
                            try self.moc.save()
                        } catch {
                            print(error)
                        }

                    } else {
                        self.shoudlShowSignUpAlert = true
                    }


                }
        }
        .frame(width: 300, height: 50)
        .background(Color.green)
        .clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
    }
    .sheet(isPresented: $showingImagePicker, onDismiss: loadImage){
        ImagePicker(image: self.$inputImage)
    }
    .navigationTitle("Sign Up")
    .alert(isPresented: $shoudlShowSignUpAlert, content: {
        Alert(title: Text("Fill it out correctly!"))
    })
}

Solution

  • To your first question: no need for use a Binding here. You just declare a variable inside your ContentView of type User.

    What makes CoreData and SwiftUI so powerful together, is that ManagedObjects conform to ObservableObjects. So you can declare them as ObservedObject in your view.

    import SwiftUI
    
    struct ContentView: View {
                
        @Environment(\.managedObjectContext) var moc
        
        //@Binding var selectedUser : User
    
        @ObservedObject var selectedUser: User //<< here declare it 
    

    Third question: as CoreData is a framework like some other framework in iOS you will have to import it when you make use of it's functions, classes, structs. You can take a look at it here