6

I'm trying to retrieve an array of custom object in Swift, where i get the error

"UserInfo={NSDebugDescription=value for key 'root' was of unexpected class 'NSArray'. Allowed classes are '{(MusicCloud.Playlist)}"

My project is a music player, so I'm having a Song object and a Playlist object, which will have a name and list song songs.

I've already encode the object model:

class Playlist: NSObject, NSCoding{
    var name: String
    var songs: [Song]
    var image: UIImage

    override init() {
        self.name = ""
        self.songs = []
        self.image = UIImage()
    }

    init(name: String, songs: [Song], image: UIImage) {
        self.name = name
        self.songs = songs
        self.image = image
    }

    required convenience init(coder aDecoder: NSCoder) {
        let name = aDecoder.decodeObject(forKey: "name") as! String
        let songs = aDecoder.decodeObject(forKey: "songs") as! [Song]
        let image = aDecoder.decodeObject(forKey: "photo") as! UIImage
        self.init(name: name, songs: songs, image: image)
    }

    func encode(with aCoder: NSCoder) {
        aCoder.encode(name, forKey: "name")
        aCoder.encode(songs, forKey: "songs")
        aCoder.encode(image, forKey: "photo")
    }
}

Storing the playlists:

let playlist: Playlist = Playlist(name: txt.text!, songs: [], image: UIImage())
            MusicService.shared.playlists.append(playlist)
            do{
                let encodedData: Data = try NSKeyedArchiver.archivedData(withRootObject: MusicService.shared.playlists, requiringSecureCoding: false)
                self.userDefaults.set(encodedData, forKey: "playlist")
                self.userDefaults.synchronize()
            }catch let error{
                print("error when add  \(error)")
            }

And retrieving it:

do{
            let decodedPlaylists = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [Playlist.self], from: decoded!) as! [Playlist]
            print("here is decoded playlist: \(decodedPlaylists)")
            MusicService.shared.playlists = decodedPlaylists
        }catch let error{
            print("error when retrieve \(error)")
        }

Full error description:

Domain=NSCocoaErrorDomain Code=4864 "value for key 'root' was of unexpected class 'NSArray'. Allowed classes are '{(
    MusicCloud.Playlist
)}'."

Can anyone show me if I'm missing something. Many thanks!

hrtlkr29
  • 383
  • 1
  • 7
  • 21
  • 2
    You shouldn't be using `NSCoding`, use `Codable` instead. – Dávid Pásztor May 20 '19 at 09:01
  • @DávidPásztor can you explain more? I'm new to this kind of local storage, many thanks – hrtlkr29 May 20 '19 at 09:02
  • 1
    See [this](https://stackoverflow.com/questions/52366070/how-to-save-a-dictionary-of-dictionaries-to-userdefaults-intintint/52366193?r=SearchResults&s=3|36.7859#52366193) and [this](https://stackoverflow.com/questions/49925692/save-an-object-in-nsuserdefaults-and-realm/49926513#49926513) answer on how to achieve that. `NSCoding` is the legacy way of saving custom objects to the file system, while `Codable` is a new, pure Swift solution, which is more type safe, so you should be using that instead of the legacy solution. – Dávid Pásztor May 20 '19 at 09:04
  • @DávidPásztor I will have a look. Thank you very much! – hrtlkr29 May 20 '19 at 09:09

2 Answers2

8

I don't know why but changing from

let decodedPlaylists = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [Playlist.self], from: decoded!) as! [Playlist]

to

let decodedPlaylists = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(decoded!) as! [Playlist]

will fix the issue for me. So I will close my ticket here. Thank you if you're noticing!

hrtlkr29
  • 383
  • 1
  • 7
  • 21
  • `.unarchiveTopLevelObjectWithData` has been deprecated as of iOS 12.0, and `.unarchivedObject` should be used in its place. – YourManDan Aug 12 '23 at 21:08
3

try this

let decodedPlaylists = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [Playlist.self, NSArray.self], from: decoded!) as! [Playlist]

I guess,

the following decoding code contains the data structure of NSArray

let songs = aDecoder.decodeObject(forKey: "songs") as! [Song]
black_pearl
  • 2,549
  • 1
  • 23
  • 36