NEW: Start my new Ultimate Portfolio App course with a free Hacking with Swift+ trial! >>

Saving Images with Core Data in SwiftUI

Forums > SwiftUI

I was wondering if anyone knows how to save images with core data. I know how to save text, but I am unsure about how to save an image. Any help would be greatly appreciated. Thank you.

   

You have to convert it to Binary Data because UIImage is not a valid attribute type in Core Data.

So, something like:

let data = image.jpegData(compressionQuality: 1.0)

And then you can store data.

   

@roosterboy, Is it possible, you can send me some sample code of an image picker and saving the selected image from the image picker in core data? Similiar to the Bookworm app, I want the user to be able to select an image from an image picker and upload it to the content view as a navigation link so the user can view the image with the name and description as well as being able to delete the image. Thank you very much.

   

Hacking with Swift is sponsored by Instabug

SPONSORED Catch bugs as soon as they happen and know exactly why a crash occurred. Instabug's SDK grabs all the logs they need to fix bugs, crashes and performance issues in minutes instead of days. Get screenshots, device details, network logs, repro steps, and tons of other critical insights needed to resolve issues and prioritize product backlogs straight from your dashboard. It only takes a minute to integrate!

Get started now

Sponsor Hacking with Swift and reach the world's largest Swift community!

You seem to asked the same question twice

I give a reply here

   

I too am working on how to save an image into Core Data. I finally have a working model of an image picker that allows me to choose an image from my library or access the camera and return an image to display in my app. But I am having trouble finding how to convert that SwiftUI image into jpeg data for storage in Core Data. Unfortunatly what was proposed by @roosterboy is not SwiftUI. You would think something like that would work, but SwiftUI complains that the "Value of type 'image?' has no member 'jpegData'. At least that did not work for me. I have done something VERY similar when saving images to Core Data in regular Swift, but that does not work here. Anybody out there found a solution yet?

1      

I found a youtube video on how to save and image with core data and swiftui. It is an hour long and has no sound after the first 4 minutes. The reason it is so long is because the person can't type very fast and probably doesn't speak english very well. The video is worth it and now I am able to save images with Core Data and SwifUI. Here's the link. [https://www.youtube.com/watch?v=XxjbecMKP54]. Restart the video to see the project that is made.

   

I did reduce my code as much as possible to make it easier to understand. I hope this helps

  1. My ImagePicker
struct ImagePicker: UIViewControllerRepresentable {

    // MARK: - Environment Object
    @Environment(\.presentationMode) var presentationMode
    @Binding var image: UIImage?

    // MARK: - Coordinator Class
    class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
        let parent: ImagePicker

        init(_ parent: ImagePicker) {
            self.parent = parent
        }

        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
            if let uiImage = info[.originalImage] as? UIImage {
                parent.image = uiImage
            }
            parent.presentationMode.wrappedValue.dismiss()
        }
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        return picker
    }

    func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {

    }
}
  1. My SwiftUI Form where i select the image and save it in CoreData
struct ImageToCoreData: View {

    // Image
    @State private var image: Image?
    @State private var showImagePicker = false
    @State private var inputImage: UIImage?

    func save() {
        let pickedImage = inputImage?.jpegData(compressionQuality: 1.0)

        //  Save to Core Data
    }

    func loadImage() {
        guard let inputImage = inputImage else { return }
        image = Image(uiImage: inputImage)
    }

    // MARK: - Body
    var body: some View {

        // MARK: - Return View
        return NavigationView {
            Form {
                // Section Picture
                Section(header: Text("Picture", comment: "Section Header - Picture")) {
                    if image != nil {
                        image!
                            .resizable()
                            .scaledToFit()
                            .onTapGesture { self.showImagePicker.toggle() }
                    } else {
                        Button(action: { self.showImagePicker.toggle() }) {
                            Text("Select Image", comment: "Select Image Button")
                                .accessibility(identifier: "Select Image")
                        }
                    }
                }

                // Section Save / Reset
                Section {
                    Button(action: save) {
                        Text("Save", comment: "Save Button")
                    }
                }
            }
            .sheet(isPresented: $showImagePicker, onDismiss: loadImage) { ImagePicker(image: self.$inputImage) }
        }
    }

1      

OK, so I found my problem - I was trying to work with SwiftUI images and then convert them to UIImages to save to Core Data. That is not possible - the framework does not support it. So, I flipped my model and now work with UIImages (as both Volker88 and I'm sure Nate-TheIOSDeveloper456 does too). Stupid me! Oh well, you take yourself down a path and convince yourself it's right - but in the end there is one major problem! ;-) Anyway, thank you very much Volker88 for your example - that is really easy to understand and showed me where I went wrong.

One thing that Roosterboy above needs to make clear... "image" in his line of code must be of type UIImage. It is NOT a SwiftUI image.

Thanks everyone!

   

Have tried the advice but have discoverd this problem:

Everything works fine with inserting the Image... Displays correctly, but if view changes or app is closed image is lost. Not sure what im missing because seems nothing is saved. Should be a onDismiss save.

What Could be the problem, if it writes out: have not really found something usefull about this Output...

"[SwiftUI] Context in environment is not connected to a persistent store coordinator: <NSManagedObjectContext: 0x600000d55c00>"

What i have used:

Used the same Image Picker as Volker88.

My CoreData Model

Entities is "Person" (Codegen is class definition l Module is current Product Module)

id is "UUID"

image is "Binary Data"

Name is "String"

everywere is only "optional" selected.

My ContentView:

import SwiftUI

struct ContentView: View {
        @Environment(\.managedObjectContext) var moc

        @FetchRequest(entity: Person.entity(), sortDescriptors: []) var person: FetchedResults<Person>

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

        func save() {
            let pickedImage = inputImage?.jpegData(compressionQuality: 1.0)

// The part were saving of the image should take place.... But i dont know why it doesnt want to work!
//                try? self.moc.save()
//                print("Image is saved")

            let person = Person(context: self.moc)
            person.image = Data()

            try? self.moc.save()
            print("Image is saved")
         }

        func loadImage() {
            guard let inputImage = inputImage else { return }
            image = Image(uiImage: inputImage)
        }

        var body: some View {
            VStack {
                VStack{
                    if image != nil {
                        image?
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                        .frame(width: 150, height: 150)
                        .clipShape(Circle())
                        .shadow(radius: 10)

                    } else {
 // Backup if no image is selected
                        VStack{
                            Image(systemName: "photo")
                                .resizable()
                                .aspectRatio(contentMode: .fill)
                                .frame(width: 150, height: 150)
                                .clipShape(Circle())
                                .shadow(radius: 10)
                        }
                    }
                }.padding(.bottom)
                 .onTapGesture { self.showingImagePicker = true
                }.sheet(isPresented: $showingImagePicker, onDismiss: loadImage){
                    ImagePicker(image: self.$inputImage)
    //                    .environment(\.managedObjectContext, self.moc)

                }

            }
        }
    }

Since it is the same topic also some sample code from this forum, I didn create a new Topic. Also have watched the 2 Core Data Tutorials, that are 1 hour long but did not really solve the problem.

I cannot find what is missing, that it saves the inserted picture. Thanks.

   

@Dkmnn

It seems like your Core Data stack is not completely set up. This:

Context in environment is not connected to a persistent store coordinator:

Tells you that you have a context but it does not have persistent store coordinator. This coordinator has the responsibility to "talk" to the SQL database and save the data.

Can you post your code where you are initializing Core Data for your app?

   

When run on different iPhone.... Sometimes it also writes out:

"[2014:82893] [VKDefault] TextureAtlasPage: Atlas page destroyed with outstanding references.: Assertion with expression - _textureRefs == 0 : Failed in file - /Library/Caches/com.apple.xbs/Sources/VectorKit_Sim/VectorKit-1606.34.10.29.27/src/TextureAtlas.cpp line - 604"

not sure what part im missing.

Using the Pace 369.xcdatamodeld with the Entities "Person" (Codegen is class definition l Module is current Product Module).

Also im not creating NSManagedObject subclasses (not sure if this matters..)

edited: SceneDelegate (automatic created by Xcode)

AppDelegate (automatic created by Xcode)

Im not sure with the saving... if it is correctly compressed and then saved... saving should happen "ondismiss"... Assume that here somewere is my mistake.

    func save() {
        let pickedImage = inputImage?.jpegData(compressionQuality: 0.80)

        let person = Person(context: self.moc)
        person.image = Data()

        try? self.moc.save()
        print("Image is saved")
     }

   

@Volker88 , your code is perfect. How do you take the image selected and save it to Core Data? I'm just getting into this and having a hard time finding a simple solution to take a photo and save it to CoreData during a set up of an app.

   

Once you have the image selected you just need to convert it to Data and then assign it to your CoreData Model and save it.

In the below example the Entity is "Person" and does have an "image" property of type "Binary Data"

func save() {
        let pickedImage = inputImage?.jpegData(compressionQuality: 0.80)

        let person = Person(context: self.moc)
        person.image = pickedImage

        try? self.moc.save()
        print("Image is saved")
     }

   

Rather than saving your image directly to CoreData, why not store the file path of the image and save the image to a documents library or photo library?

   

@StPaddyHall That's exactly what I thought. However, the downside of it might be a lack of reliability, since someone might move or delete the image directly from photo library and the CoreData DB within your app won't know about this and will still keep the old path. Therefore the best solution seems to be saving image as a Binary Data and selecting "Allows External Storage" attribute. Thanks to it, you won't hold the big image files in the DB which has significant impact on DB performance, plus the image is saved internally by CoreData, hence the user is not able to move/delete it. I'd love to hear Paul Hudson take on it !

1      

Hacking with Swift is sponsored by Instabug

SPONSORED Catch bugs as soon as they happen and know exactly why a crash occurred. Instabug's SDK grabs all the logs they need to fix bugs, crashes and performance issues in minutes instead of days. Get screenshots, device details, network logs, repro steps, and tons of other critical insights needed to resolve issues and prioritize product backlogs straight from your dashboard. It only takes a minute to integrate!

Get started now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.