Swift: Error in NSKeyedArchiver

2019-07-23 04:25发布

Swift 3 iOS 10, Trying to save array with custom objects in NSKeyedArchiver, basically trying to save the table view after the user uses the buttons to switch between sections. I've tried several post to solve the issue but no luck, now I'm trying to do it myself NSCoding and NSKeyedArchiver.

Error:

Cannot convert value of type '[Blog]' to expected argument type 'NSCoder'

Error is in code in Blog.swift, Code that handles NSCoding and my Blog Objects

import UIKit

class BlogsCoding: NSObject, NSCoding {

var blogList : [Blog]

init(blogList : [Blog]) {
    self.blogList = blogList
}

convenience required init?(coder aDecoder: NSCoder) {

    guard let blogList = aDecoder.decodeObject(forKey: "blogs") as? [Blog]
        else {
            return nil
    }
    self.init (blogList : blogList)
}

func encode(with aCoder: NSCoder) {

    aCoder.encode(blogList, forKey: "blogs")
 }
}

class Blog: NSObject, NSCoding { // To conform to NSCoding

// Strings
var blogName: String?
var blogStatus1: String?
var blogStatus2: String?
var blogURL: String?
var blogID: String?
var blogType: String?
var blogDate: String?
var blogPop: String?
var blogList : [Blog] // To conform to NSCoding

override init() {

}
// Converting Strings into Objects
init(blogName bName: String,
     andBlogStatus1 bStatus1: String,
     andBlogStatus2 bStatus2: String,
     andBlogURL bURL: String,
     andBlogID bID: String,
     andBlogType bType: String,
     andBlogDate bDate: String,
     andBlogPop bPop: String,
     blogList : [Blog]) // To conform to NSCoding
{
    super.init()

    self.blogName = bName
    self.blogStatus1 = bStatus1
    self.blogStatus2 = bStatus2
    self.blogURL = bURL
    self.blogID = bID
    self.blogType = bType
    self.blogDate = bDate
    self.blogPop = bPop
    self.blogList = blogList // To conform to NSCoding
 }

 // To conform to NSCoding
 convenience required init?(coder aDecoder: NSCoder) {

    guard let blogList = aDecoder.decodeObject(forKey: "blogs") as? [Blog]
        else {
            return nil
    }
    self.init (coder : blogList) // *---* Error is here *---*
}

func encode(with aCoder: NSCoder) {

    aCoder.encode(blogList, forKey: "blogs")
 }

}

In MainController.swift - Where my tableview is

override func viewDidLoad() {

var path : String {
        let manager = FileManager.default
        let url = manager.urls(for: .documentDirectory, in: .userDomainMask).first! as NSURL
        return url.appendingPathComponent("blogs")!.path // I have a blogs.plist for this, doing it right?
    }
}

Follow Button

// Follow Button
@IBAction func followButtonClick(_ sender: UIButton!) {

// After Updating Table, Save Arrays
        var success = false
            // mainArray is array holding custom objects from json
        success = NSKeyedArchiver.archiveRootObject(mainArray, toFile: "path") // If I dont use "" I get undeclared 'path'

        if success {
            print("Saved Blogs")
        } else  {
            print("Didn't Save Blogs")
        }
}

Receiving Data from Server

// Retrieving Data from Server
func retrieveData() {

    let getDataURL = "http://blogexample.com/receiving.php"
    let url: NSURL = NSURL(string: getDataURL)!

    do {

        let data: Data = try Data(contentsOf: url as URL)
        jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! NSMutableArray

        // Looping through jsonArray
        for i in 0..<jsonArray.count {

            // Create Blog Object
            let bID: String = (jsonArray[i] as AnyObject).object(forKey: "id") as! String
            let bName: String = (jsonArray[i] as AnyObject).object(forKey: "blogName") as! String
            let bStatus1: String = (jsonArray[i] as AnyObject).object(forKey: "blogStatus1") as! String
            let bStatus2: String = (jsonArray[i] as AnyObject).object(forKey: "blogStatus2") as! String
            let bURL: String = (jsonArray[i] as AnyObject).object(forKey: "blogURL") as! String
            // New
            let bType: String = (jsonArray[i] as AnyObject).object(forKey: "blogType") as! String
            let bDate: String = (jsonArray[i] as AnyObject).object(forKey: "blogDate") as! String
            let bPop: String = (jsonArray[i] as AnyObject).object(forKey: "blogPop") as! String
            // NSCoding
            let blogList: NSObject = ((jsonArray[i]) as! NSObject).value(forKey: "blogList") as! NSObject

            // Add Blog Objects to Main Array
            mainArray.append(Blog(blogName: bName, andBlogStatus1: bStatus1, andBlogStatus2: bStatus2, andBlogURL: bURL, andBlogID: bID, andBlogType: bType, andBlogDate: bDate, andBlogPop: bPop, blogList: blogList as! [Blog]))

        }
    }
    catch {
        print("Error: (Retrieving Data)")
    }

Also, is 'path' defined and used right? Thank you!

1条回答
Summer. ? 凉城
2楼-- · 2019-07-23 04:59

The purpose of NSCoding is to convert each single property of the class to a property list compliant format.

First of all declare all String properties as non-optional because the custom initializer passes only non-optional parameters.

Then add the lines in the init(coder and encode(with methods (edited)

class Blog: NSObject, NSCoding { // To conform to NSCoding

    // Strings
    var blogName: String
    var blogStatus1: String
    var blogStatus2: String
    var blogURL: String
    var blogID: String
    var blogType: String
    var blogDate: String
    var blogPop: String
    var blogList : [Blog] // To conform to NSCoding

    // Converting Strings into Objects
    init(blogName bName: String,
         andBlogStatus1 bStatus1: String,
         andBlogStatus2 bStatus2: String,
         andBlogURL bURL: String,
         andBlogID bID: String,
         andBlogType bType: String,
         andBlogDate bDate: String,
         andBlogPop bPop: String,
         blogList : [Blog]) // To conform to NSCoding
    {
        self.blogName = bName
        self.blogStatus1 = bStatus1
        self.blogStatus2 = bStatus2
        self.blogURL = bURL
        self.blogID = bID
        self.blogType = bType
        self.blogDate = bDate
        self.blogPop = bPop
        self.blogList = blogList // To conform to NSCoding
        super.init()
    }

    // To conform to NSCoding
    convenience required init?(coder aDecoder: NSCoder) {
        self.blogName = aDecoder.decodeObject(forKey: "blogName") as! String
        self.blogStatus1 = aDecoder.decodeObject(forKey: "blogStatus1") as! String
        self.blogStatus2 = aDecoder.decodeObject(forKey: "blogStatus2") as! String
        self.blogURL = aDecoder.decodeObject(forKey: "blogURL") as! String
        self.blogID = aDecoder.decodeObject(forKey: "blogID") as! String
        self.blogType = aDecoder.decodeObject(forKey: "blogType") as! String
        self.blogDate = aDecoder.decodeObject(forKey: "blogDate") as! String
        self.blogPop = aDecoder.decodeObject(forKey: "blogPop") as! String
        self.blogList = aDecoder.decodeObject(forKey: "blogs") as! [Blog]
        super.init (coder : aDecoder)
    }

    func encode(with aCoder: NSCoder) {
        aCoder.encode(blogName, forKey: "blogName")
        aCoder.encode(blogStatus1, forKey: "blogStatus1")
        aCoder.encode(blogStatus2, forKey: "blogStatus2")
        aCoder.encode(blogURL, forKey: "blogURL")
        aCoder.encode(blogID, forKey: "blogID")
        aCoder.encode(blogType, forKey: "blogType")
        aCoder.encode(blogDate, forKey: "blogDate")
        aCoder.encode(blogPop, forKey: "blogPop")
        aCoder.encode(blogList, forKey: "blogs")
    }
}

However there is a potential caveat:

Encoding / decoding an array of objects of the target class may cause unexpected behavior for example an infinite loop. Consider that. You might encode the array separately.


The second issue is a typo, you pass "path" as a literal string rather than a variable path:

NSKeyedArchiver.archiveRootObject(mainArray, toFile: path)
查看更多
登录 后发表回答