BLACK FRIDAY: Save 50% on all my Swift books and bundles! >>

Day 77 Challenge: Not saving to documentDirectory

Forums > 100 Days of SwiftUI

Have been working on Day 77 Challenge which asks to create a contact app to practice using PHPickerViewController, saving to documentDirectory, encoding/decoding to/from JSON and more...

I have the app working however it will not save data to the documentDirectory. The save function does not throw any errors but when I save some contacts and quit the app the saved contacts are not loaded.

Any help would be appreciated.

import Foundation
import SwiftUI

struct Contact: Codable, Comparable, Identifiable {
    let id: UUID
    let name: String
    let picture: Data

    static func <(lhs: Contact, rhs: Contact) -> Bool {
        lhs.name < rhs.name
    }
}
import SwiftUI

struct ContentView: View {
    @State private var picture: Image?
    @State private var contacts = [Contact]()

    @State private var showingAddContact = false

    let savePath = FileManager.documentsDirectory.appendingPathComponent("SavedContacts")

    var body: some View {
        NavigationView {
            List {
                Section {
                    ForEach(contacts.sorted(), id: \.id) { contact in
                        NavigationLink {
                            DetailView(contact: contact)
                        } label: {
                            HStack {
                                convertPicture(contact: contact)
                                    .resizable()
                                    .scaledToFit()
                                    .frame(width: 75, height: 75)

                                Spacer()

                                Text( contact.name)
                            }
                        }
                    }
                }
            }
            .navigationTitle("Contact Keeper")
            .toolbar {
                Button {
                    showingAddContact = true
                } label: {
                    Image(systemName: "plus.circle.fill")
                }
            }
            .sheet(isPresented: $showingAddContact) {
                AddView(contacts: $contacts)
            }
        }
    }

    init() {
        do {
            let contactData = try Data(contentsOf: savePath)
            contacts = try JSONDecoder().decode([Contact].self, from: contactData)
        } catch {
            contacts = []
        }
    }
}
import SwiftUI

struct AddView: View {
    @Environment(\.dismiss) var dismiss

    @State private var picture: Image?
    @State private var inputImage: UIImage?

    @State private var contactName = ""
    @Binding var contacts: [Contact]

    @State private var showingImagePicker = false

    let savePath = FileManager.documentsDirectory.appendingPathComponent("SavedContacts")

    var body: some View {
        NavigationView {
            Form {
                Section {
                    TextField("Contact Name", text: $contactName)
                        .textInputAutocapitalization(.never)
                        .disableAutocorrection(true)
                }

                Section {
                    ZStack {
                        Text("Click here to select an image")

                        picture?
                            .resizable()
                            .scaledToFit()
                            .frame(width: 300, height: 300)
                    }
                    .onTapGesture {
                        showingImagePicker = true
                    }
                }
            }
            .toolbar {
                Button {
                    save()
                    dismiss()
                } label: {
                    Image(systemName: "square.and.arrow.down.fill")
                }
            }
        }
        .sheet(isPresented: $showingImagePicker) {
            ImagePicker(image: $inputImage)
        }
        .onChange(of: inputImage) { _ in loadImage() }
    }

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

    func save() {
        do {
            if let jpegData = inputImage?.jpegData(compressionQuality: 0.8) {
                let newContact = Contact(id: UUID(), name: contactName, picture: jpegData)
                contacts.append(newContact)

                let contactData = try JSONEncoder().encode(contacts)
                try contactData.write(to: savePath, options: [.atomicWrite, .completeFileProtection])
            }
        } catch {
            print("Unable to save your data")
        }
    }
}
import SwiftUI

struct DetailView: View {
    var contact: Contact

    var body: some View {
        VStack {
            convertPicture(contact: contact)
                .resizable()
                .scaledToFit()
                .frame(width: 400, height: 400)

            Text(contact.name)
                .font(.headline.bold())
        }
    }
}
import SwiftUI

func convertPicture(contact: Contact) -> Image {
    let pictureData = contact.picture
    let uiPicture = UIImage(data: pictureData)
    let image = Image(uiImage: uiPicture!)
    return image
}
import PhotosUI
import SwiftUI

struct ImagePicker: UIViewControllerRepresentable {
    @Binding var image: UIImage?

    class Coordinator: NSObject, PHPickerViewControllerDelegate {
        var parent: ImagePicker

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

        func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
            // Tell the picker to go away
            picker.dismiss(animated: true)

            // Exit if no selection was made
            guard let provider = results.first?.itemProvider else { return }

            // If this has an image we can use, use it
            if provider.canLoadObject(ofClass: UIImage.self) {
                provider.loadObject(ofClass: UIImage.self) { image, _ in
                    self.parent.image = image as? UIImage
                }
            }
        }

    }
    func makeUIViewController(context: Context) -> PHPickerViewController {
        var config = PHPickerConfiguration()
        config.filter = .images

        let picker = PHPickerViewController(configuration: config)
        picker.delegate = context.coordinator
        return picker
    }

    func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {

    }

    func makeCoordinator() -> Coordinator {
        Coordinator(parent: self)
    }
}
import UIKit

class ImageSaver: NSObject {
    var successHandler: (() -> Void)?
    var errorHandler: ((Error) -> Void)?

    func writeToPhotoAlbum(image: UIImage) {
        UIImageWriteToSavedPhotosAlbum(image, self, #selector(saveCompleted), nil)
    }

    @objc func saveCompleted(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
        if let error = error {
            errorHandler?(error)
        } else {
            successHandler?()
        }
    }
}
import Foundation

extension FileManager {
    static var documentsDirectory: URL {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)

        return paths[0]
    }
}

3      

Save 50% in my WWDC sale.

SAVE 50% All our books and bundles are half price for Black Friday, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

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.