问题
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:
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)
来源:https://stackoverflow.com/questions/44481054/swift-error-in-nskeyedarchiver