UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

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      

Hacking with Swift is sponsored by Superwall

SPONSORED Superwall lets you build & test paywalls without shipping updates. Run experiments, offer sales, segment users, update locked features and more at the click of button. Best part? It's FREE for up to 250 conversions / mo and the Superwall team builds out 100% custom paywalls – free of charge.

Learn More

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.