0

In below TableViewController, I'm trying to store a class type array(playList) to UserDefaults then retrieve it in each table cell. But as opening this table View, app is crashed with `"Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object ( "LocalMusicPlayer.SongData" ) for key playList'", but if the data is simply string array, it works.

Just come to UserDefaults today, want to ask if it is not supported for storing class type data or some extra method like defaults.object(forKey: "playList") should be involved?

Another question is that in real implementation, if I should put initialization of UserDefaults operations to some other files' init() but not under viewDidLoad() of a View. Your help is appreciated!

/// Well, below is my SongData and TableView file.

/// 9.4, 2019, involve NSObject and NSCoding, it is working now.

import UIKit

class SongData: NSObject, NSCoding {

    var songName: String
    var artistName: String
    var albumName: String
    var albumArtwork: UIImage
    var url: URL

    init(songName: String, artistName: String, albumName: String, albumArtwork: UIImage, url: URL) {
        self.songName = songName
        self.artistName = artistName
        self.albumName = albumName
        self.albumArtwork = albumArtwork
        self.url = url
    }

    // MARK: -NSCoding
    func encode(with aCoder: NSCoder) {
        aCoder.encode(songName, forKey: "songName")
        aCoder.encode(artistName, forKey: "artistName")
        aCoder.encode(albumName, forKey: "albumName")
        aCoder.encode(albumArtwork, forKey: "albumArtwork")
        aCoder.encode(url, forKey: "url")
    }

    required init?(coder aDecoder: NSCoder) {
        songName = aDecoder.decodeObject(forKey: "songName") as! String
        artistName = aDecoder.decodeObject(forKey: "artistName") as! String
        albumName = aDecoder.decodeObject(forKey: "albumName") as! String
        albumArtwork = aDecoder.decodeObject(forKey: "albumArtwork") as! UIImage
        url = aDecoder.decodeObject(forKey: "url") as! URL
    }
}

/// Table View

import UIKit

class PlaylistTableViewController: UITableViewController {

    var song: SongData?
    var playList = [SongData]()
    let defaults = UserDefaults.standard

    override func viewDidLoad() {
        super.viewDidLoad()

        playList.append(SongData(songName: "You Belong With Me", artistName: "Tylor Swift", albumName: "Love", albumArtwork: UIImage(named: "Apple")!, url: URL(fileURLWithPath: "file:///private/var/mobile/Containers/Data/Application/14E1BC3D-65A6-4854-9CD6-A8740265F341/Documents/Just%20Dance.mp3")))
        playList.append(SongData(songName: "Just Dance", artistName: "Lady Gaga", albumName: "Love", albumArtwork: UIImage(named: "Apple")!, url: URL(fileURLWithPath: "file:///private/var/mobile/Containers/Data/Application/14E1BC3D-65A6-4854-9CD6-A8740265F341/Documents/Just%20Dance.mp3")))

        let encodedData = try! NSKeyedArchiver.archivedData(withRootObject: playList, requiringSecureCoding: false)
        defaults.set(encodedData, forKey: "playList")
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return playList.count
    }

    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
            return "PLAYLISTS"
    }


    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "playListLabelCell", for: indexPath)

        let decoded = defaults.object(forKey: "playList") as! Data
        let decodedTeams = try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(decoded) as! [SongData]
        cell.textLabel?.text = decodedTeams[indexPath.row].songName
        cell.detailTextLabel?.text = decodedTeams[indexPath.row].artistName
        cell.imageView?.image = decodedTeams[indexPath.row].albumArtwork

        return cell
    }
}
Zhou Haibo
  • 1,681
  • 1
  • 12
  • 32
  • @vadian, yes, I found this answer, is trying to connect with my code, but no result yet, so I raise this question, my bad. – Zhou Haibo Sep 03 '19 at 11:19
  • You have to serialize your class to `Data`. As an `UIImage` instance is included I'd recommend to make the class a subclass of `NSObject`, adopt `NS(Secure)Coding` and implement the protocol methods. This is described in the duplicate. [Here are many other related questions](https://stackoverflow.com/search?q=%7Bswift%7D+Attempt+to+insert+non-property+list+object) – vadian Sep 03 '19 at 11:21
  • @vadian, well my purpose is to persistently save music playlist in my app. And I think to only store the url, which is unique could be the idea. Because UserDefaults may not have the size capability for UIImage, then I could retrieve the UIImage, and albumName... from where url = "". Above example just a 1st try to see if UserDefaults is working. – Zhou Haibo Sep 03 '19 at 11:28
  • Sorry, I suppose to comment on that original question but without enough reputation:( Today I Inherit `NSObject` and `NSCoding` for my SongData class and implement the protocol methods, it is working now, I could retrieve the songName, artwork... from the decoder. Just 1 question, if it is ideal to save the music artwork to `UserDefaults`? e.g. hundreds of songs with artworks may take a big size, if `UserDefaults` could handle it nicely and not affect the performance of App? – Zhou Haibo Sep 04 '19 at 05:02
  • `UserDefaults` for large data sets is bad practice. There is CoreData, MYSQL or other third-party databases. – vadian Sep 04 '19 at 06:16
  • Yeah, I consider to use Core Data & SQLite on yesterday, but I try the NSCoding method firstly in this morning. Will look into about how to implement them on my App after finishing all the viewController design. Thanks for it. – Zhou Haibo Sep 04 '19 at 07:32

0 Answers0