Get 'NSInvalidArgumentException' 'Попытка вставить объект списка, не относящийся к свойствам, при сохранении массива типов классов в UserDefaults

В приведенном ниже TableViewController я пытаюсь сохранить массив типа класса (playList) в UserDefaults, а затем получить его в каждой ячейке таблицы. Но при открытии этого представления таблицы происходит сбой приложения с сообщением «Завершение работы приложения из-за необработанного исключения« NSInvalidArgumentException», причина: «Попытка вставить объект списка, не являющийся свойством ( " LocalMusicPlayer.SongData" ) для ключа playList'", но если данные представляют собой просто массив строк, это работает.

Просто зайдите на UserDefaults сегодня, хотите спросить, не поддерживается ли он для хранения данных типа класса или должен быть задействован какой-то дополнительный метод, такой как defaults.object(forKey: "playList")?

Другой вопрос заключается в том, что в реальной реализации, если я должен поставить инициализацию UserDefaults операций в init() некоторых других файлов, но не в viewDidLoad() представления. Ваша помощь приветствуется!

/// Ну, ниже мой файл SongData и TableView.

/// 9.4, 2019, задействуем NSObject и NSCoding, сейчас работает.

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
    }
}

/// Табличное представление

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
    }
}

person ChuckZHB    schedule 03.09.2019    source источник
comment
@vadian, да, я нашел этот ответ, пытается подключиться к моему коду, но пока безрезультатно, поэтому я поднимаю этот вопрос, мой плохой.   -  person ChuckZHB    schedule 03.09.2019
comment
Вы должны сериализовать свой класс в Data. Поскольку экземпляр UIImage включен, я бы рекомендовал сделать класс подклассом NSObject, принять NS(Secure)Coding и реализовать методы протокола. Это описано в дубликате. Здесь много других связанных вопросов   -  person vadian    schedule 03.09.2019
comment
@vadian, ну, моя цель - постоянно сохранять музыкальный плейлист в моем приложении. И я думаю, что идея хранить только уникальный URL-адрес может быть идеей. Поскольку UserDefaults может не иметь возможности размера для UIImage, я мог бы получить UIImage и AlbumName... откуда url = . В приведенном выше примере только первая попытка проверить, работает ли UserDefaults.   -  person ChuckZHB    schedule 03.09.2019
comment
Извините, я полагаю, что прокомментирую этот первоначальный вопрос, но без достаточной репутации :( Сегодня я наследую NSObject и NSCoding для своего класса SongData и реализую методы протокола, теперь он работает, я могу получить название песни, обложку... из декодера . Всего 1 вопрос, идеально ли сохранять обложку музыки в UserDefaults?Например, сотни песен с обложкой могут занимать большой размер, если бы UserDefaults могла нормально с этим справиться и не повлиять на производительность приложения?   -  person ChuckZHB    schedule 04.09.2019
comment
UserDefaults для больших наборов данных — плохая практика. Есть CoreData, MYSQL или другие сторонние базы данных.   -  person vadian    schedule 04.09.2019
comment
Да, я думаю использовать Core Data и SQLite вчера, но сегодня утром я сначала попробую метод NSCoding. Посмотрю, как реализовать их в моем приложении после завершения всего дизайна viewController. Спасибо за это.   -  person ChuckZHB    schedule 04.09.2019