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

Day 72 Unable to load saved data.

Forums > 100 Days of SwiftUI

I just copy all the code from this course and try to run it, but it always print 'Unable to load saved data.' at the begining.

For debug, I added some prints like this:


    func loadData() {
        let filename = getDocumentsDirectory().appendingPathComponent("SavedPlaces")
        do {
            let data = try Data(contentsOf: filename)
            print("get data")
            locations = try JSONDecoder().decode([CodableMKPointAnnotation].self, from: data)
        } catch {
            print("Unable to load saved data.")
        }
    }

    func saveData() {
        do {
            let filename = getDocumentsDirectory().appendingPathComponent("SavedPlaces")
            let data = try JSONEncoder().encode(self.locations)
            try data.write(to: filename, options: [.atomicWrite, .completeFileProtection])
        } catch {
            print("Unable to save data.")
        }
        print("Saved data")
    }

After that, it can print 'Saved data' and 'get data', but it always print "Unable to load saved data."

For the CodableMKPointAnnotation:

class CodableMKPointAnnotation: MKPointAnnotation, Codable {
    enum CodingKeys: CodingKey {
        case title, subtitle, latitude, longitude
    }

    override init() {
        super.init()
    }

    public required init(from decoder: Decoder) throws {
        super.init()

        let container = try decoder.container(keyedBy: CodingKeys.self)
        title = try container.decode(String.self, forKey: .title)
        subtitle = try container.decode(String.self, forKey: .subtitle)

        let latitude = try container.decode(CLLocationDegrees.self, forKey: .latitude)
        let longitude = try container.decode(CLLocationDegrees.self, forKey: .longitude)
        coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(title, forKey: .title)
        try container.encode(subtitle, forKey: .subtitle)
        try container.encode(coordinate.latitude, forKey: .latitude)
        try container.encode(coordinate.longitude, forKey: .longitude)
    }
}

So, how can I fix the problem?

4      

Sorry, no real solution from my side. I do have the same problem here...

It seems that the document directory has always a different app container as part of the URL and therefore the file cannot be found when the app is restarted.

First run: file:///Users/philipp/Library/Developer/CoreSimulator/Devices/818683A7-14FE-4823-8D3D-A1A36F233E92/data/Containers/Data/Application/FF1F169F-FD18-4377-9372-53F790666E07/Documents/

Next run: file:///Users/philipp/Library/Developer/CoreSimulator/Devices/818683A7-14FE-4823-8D3D-A1A36F233E92/data/Containers/Data/Application/D9718266-6B7D-460D-BDDD-12BE9A8BB4E6/Documents/

=> It seems that everytime the app is restarted from Xcode, it is reinstalled and therefore gets a fresh container.

But this is going to erase the document!?! This is not how I would expect it to work... even updating the App should never erase the content in the Documents directory

3      

Ohhh... I found the issue and it is not related to Xcode :-)

The problem appears if you add a location without modifying the title and subtitle (=you keep the placeholder value Unknown value). You will generate a corrupted "SavedPlaces" file.

If you checkout the CodableMKPointAnnotation you see, that we encode the data "title" and "subtitle" from our base class (MKPointAnnotation). They are defined as String?... but when we decode a file we are asking the container to decode a String.self... which is crashing if the value encoded was nil.

There are various solutions to fix this:

  1. Make sure the the title/subtitle you encode are never nil
try container.encode(title ?? "", forKey: .title)
try container.encode(subtitle ?? "", forKey: .subtitle)
  1. Make sure you handle the case when you decode: String? instead of String
title = try container.decode(String?.self, forKey: .title)
subtitle = try container.decode(String?.self, forKey: .subtitle)

11      

Yet another solution is to give subtitle a default value. Simply add one line

newLocation.subtitle = "Example description"

to the plus Button in ContentView.

6      

Yes! Thanks a lot people, I was trying to debug this and I was nowhere near the solution. Onwards to the challenges!

3      

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

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

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.