Error is crashing app when using NSCoder and NSKeyArchiver.
I had made a recent post around NSCoder but since then I've changed my code around and got a new error and decided a new post is best.
The app is a blog reader, reading from a MYSQL database using PHP to fill a table view with custom objects in Swift using JSON. I've been trying to save mainArray so that when the user moves cells across sections (each section has an array) it can save where the user left it.
Blog.swift: Handles the Blogs custom objects
import UIKit
class Blog: NSObject, NSCoding {
var blogName: String!
var blogStatus1: String!
var blogStatus2: String!
var blogURL: String!
var blogID: String!
var blogType: String!
var blogDate: String!
var blogPop: String!
static func createBlog(from jsonObject: AnyObject) -> Blog? {
guard let bID: String = jsonObject.object(forKey: "id") as? String,
let bName: String = jsonObject.object(forKey: "blogName") as? String,
let bStatus1: String = jsonObject.object(forKey: "blogStatus1") as? String,
let bStatus2: String = jsonObject.object(forKey: "blogStatus2") as? String,
let bURL: String = jsonObject.object(forKey: "blogURL") as? String,
let bType: String = jsonObject.object(forKey: "blogType") as? String,
let bDate: String = jsonObject.object(forKey: "blogDate") as? String,
let bPop: String = jsonObject.object(forKey: "blogPop") as? String
else {
print("Error: (Creating Blog Object)")
return nil
}
let blog = Blog()
blog.blogName = bName
blog.blogStatus1 = bStatus1
blog.blogStatus2 = bStatus2
blog.blogURL = bURL
blog.blogID = bID
blog.blogType = bType
blog.blogDate = bDate
blog.blogPop = bPop
return blog
}
// NSCoding
convenience required init?(coder aDecoder: NSCoder) {
self.init (coder : aDecoder) // *** Crashes Here ***
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
}
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")
}
}
MainController.swift - Where table view is located
var mainArray = [Blog]()
var followedArray = [Blog]()
override func viewDidLoad() {
super.viewDidLoad()
// Receiving Data from Server
retrieveData()
if let data = UserDefaults.standard.data(forKey: "mainArrayKey"),
let myBlogList = NSKeyedUnarchiver.unarchiveObject(with: data) as? [Blog] {
mainArray = myBlogList
print("mainArray: \(mainArray)")
} else {
print("Error: (Saving to UserDefaults)")
}
}
// Retrieving Data from Server
func retrieveData() {
let getDataURL = "http://example.com/receiving.php"
let url: NSURL = NSURL(string: getDataURL)!
do {
let data: Data = try Data(contentsOf: url as URL)
let jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! NSMutableArray
// Looping through jsonArray
for jsonObject in jsonArray {
if let blog = Blog.createBlog(from: jsonObject as AnyObject) {
mainArray.append(blog)
// Save to UserDefaults
let encodedData = NSKeyedArchiver.archivedData(withRootObject: mainArray)
UserDefaults.standard.set(encodedData, forKey: "mainArrayKey")
}
}
}
catch {
print("Error: (Retrieving Data)")
}
myTableView.reloadData()
// Logs
print("This is mainArray", mainArray)
// Check UserDefaults
if UserDefaults.standard.object(forKey: "mainArrayKey") != nil{
print("mainArray key exists")
}
else {
print("mainArray key does not exist")
}
}
Looks like an infinite loop to me. You call init(coder:), and the first line calls init(coder:), and the first line calls init(coder:), and so on ad infinitum.
You need to call a different initializer inside it. Try self.init() instead.
As others have stated it's indeed an infinite loop. What you need to do is change it to self.init() and also add in the following to your code. Or implement your own init that does whatever needs to be done.
override init() {
super.init()
}
Related
I'm having trouble archiving and/or unarchiving (not sure where the problem is, exactly) a set of custom classes from the iOS documents directory. The set is saved to disk (or at least it appears to be saved) because I can pull it from disk but I cannot unarchive it.
The model
final class BlockedUser: NSObject, NSSecureCoding {
static var supportsSecureCoding = true
let userId: String
let name: String
let date: Int
var timeIntervalFormatted: String?
init(userId: String, name: String, date: Int) {
self.userId = userId
self.name = name
self.date = date
}
required convenience init?(coder: NSCoder) {
guard let userId = coder.decodeObject(forKey: "userId") as? String,
let name = coder.decodeObject(forKey: "name") as? String,
let date = coder.decodeObject(forKey: "date") as? Int else {
return nil
}
self.init(userId: userId, name: name, date: date)
}
func encode(with coder: NSCoder) {
coder.encode(userId, forKey: "userId")
coder.encode(name, forKey: "name")
coder.encode(date, forKey: "date")
}
}
Writing to disk
let fm = FileManager.default
let dox = fm.urls(for: .documentDirectory, in: .userDomainMask)[0]
let dir = dox.appendingPathComponent("master.properties", isDirectory: true)
do {
let userData: [URL: Any] = [
/* Everything else in this dictionary is a primitive type (string, bool, etc.)
and reads and writes without problem from disk. The only thing I cannot
get to work is the entry below (the set of custom classes). */
dir.appendingPathComponent("blockedUsers", isDirectory: false): blockedUsers // of type Set<BlockedUser>
]
for entry in userData {
let data = try NSKeyedArchiver.archivedData(withRootObject: entry.value, requiringSecureCoding: true)
try data.write(to: entry.key, options: [.atomic])
}
} catch {
print(error)
}
Reading from disk
if let onDisk = try? Data(contentsOf: dir.appendingPathComponent("blockedUsers", isDirectory: false)) {
if let blockedUsers = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(onDisk) as? Set<BlockedUser> {
print("success")
} else {
print("file found but cannot unarchive") // where I'm currently at
}
} else {
print("file not found")
}
The problem is that you are trying to decode an object instead of decoding an integer. Check this post. Try like this:
class BlockedUser: NSObject, NSSecureCoding {
static var supportsSecureCoding = true
let userId, name: String
let date: Int
var timeIntervalFormatted: String?
init(userId: String, name: String, date: Int) {
self.userId = userId
self.name = name
self.date = date
}
func encode(with coder: NSCoder) {
coder.encode(userId, forKey: "userId")
coder.encode(name, forKey: "name")
coder.encode(date, forKey: "date")
coder.encode(timeIntervalFormatted, forKey: "timeIntervalFormatted")
}
required init?(coder: NSCoder) {
userId = coder.decodeObject(forKey: "userId") as? String ?? ""
name = coder.decodeObject(forKey: "name") as? String ?? ""
date = coder.decodeInteger(forKey: "date")
timeIntervalFormatted = coder.decodeObject(forKey: "timeIntervalFormatted") as? String
}
}
I am getting this exception but I can't figure out where I'm going wrong. I am writing an extension to attempt to save this struct from the NewsAPISwiftLibrary to NSUserDefaults: NewsAPISource
This is the code to my extension:
extension NewsAPISource {
init?(data: NSData) {
if let coding = NSKeyedUnarchiver.unarchiveObject(with: data as Data) as? Encoding {
id = coding.id
name = coding.name
sourceDescription = coding.sourceDescription
url = coding.url
category = coding.category
language = coding.language
country = coding.country
sortBysAvailable = coding.sortBysAvailable as [SortBy]
} else {
return nil
}
}
func encode() -> NSData {
return NSKeyedArchiver.archivedData(withRootObject: Encoding(self)) as NSData
}
private class Encoding: NSObject, NSCoding {
let id: SourceId?
let name: String?
let sourceDescription: String?
let url: String?
let category: NewsAPISwift.Category?
let language: Language?
let country: Country?
let sortBysAvailable: [SortBy]
init(_ source: NewsAPISource) {
id = source.id
name = source.name
sourceDescription = source.sourceDescription
url = source.url
category = source.category
language = source.language
country = source.country
sortBysAvailable = source.sortBysAvailable
}
required init?(coder aDecoder: NSCoder) {
id = aDecoder.decodeObject(forKey: "id") as? SourceId
name = aDecoder.decodeObject(forKey: "name") as? String
sourceDescription = aDecoder.decodeObject(forKey:"sourceDescription") as? String
url = aDecoder.decodeObject(forKey: "url") as? String
category = aDecoder.decodeObject(forKey: "category") as? NewsAPISwift.Category
language = aDecoder.decodeObject(forKey: "language") as? Language
country = aDecoder.decodeObject(forKey: "country") as? Country
if let sorts = aDecoder.decodeObject(forKey: "sortBysAvailable") as? [SortBy] {
self.sortBysAvailable = sorts
} else {
return nil
}
}
func encode(with aCoder: NSCoder) {
aCoder.encode(id, forKey: "id")
aCoder.encode(name, forKey: "name")
aCoder.encode(sourceDescription, forKey: "sourceDescription")
aCoder.encode(url, forKey: "url")
aCoder.encode(category, forKey: "category")
aCoder.encode(language, forKey: "language")
aCoder.encode(country, forKey: "country")
aCoder.encode(sortBysAvailable, forKey: "sortBysAvailable")
}
}
}
I create a new struct like this:
let emptySource = NewsAPISource(id: "nil", name: "nil", sourceDescription: "nil", url: "nil", category: .business, language: .english, country: .unitedStates, sortBysAvailable: [.top])
and attempt to encode it like this:
emptySourceArray.encode()
This is the line that causes the exception. Any ideas on what I'm doing incorrectly?
The full error is:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x60800005f4d0'
I know this is a common error people post here but I can't find a post that matches to what I'm doing even if its the same fundamentally. I'm new to Swift and just trying to find my way, thank you.
The first time I open my app, a blog reader app that reads from a MYSQL database, it works as intended, I can follow the blogs that I chose and unfollow. When I follow a blog/cell it saves to User Defaults using KeyArchiver but when I double tap the home button to clear the app from memory and reopen the app, it crashes.
Something wrong is going on in my loadUserDefaults because I set up breakpoints and it crashes at this line self.followedIdentifiers = Set(UserDefaults.standard.stringArray(forKey: "followedID")!)
I know I have an optional but why is it crashing/ coming back nil if I saved it with saveUserDefaults. Is it not saving? or am I not loading it correctly?
The error is this
fatal error: unexpectedly found nil while unwrapping an Optional value
Code: This is MainController.swift
var mainArray = [Blog]()
var followedArray = [Blog]()
var filteredArray = [Blog]()
var followedIdentifiers = Set<String>()
override func viewDidLoad() {
super.viewDidLoad()
// Receiving Data from Server
retrieveDataFromServer()
// NSCoding - Unarchiving Data (followedID)
loadUserDefaults()
}
// NSCoding: Archiving UserDefaults
func saveUserDefaults() {
// Saving to UserDefaults
let encodedData = NSKeyedArchiver.archivedData(withRootObject: self.followedIdentifiers)
UserDefaults.standard.setValue(encodedData, forKey: "followedID")
UserDefaults.standard.synchronize()
}
// NSCoding: Unarchiving UserDefaults
func loadUserDefaults() { // --- Crash is Here ---
// Unarchiving Data
if let data = UserDefaults.standard.data(forKey: "followedID"), let myFollowedList = NSKeyedUnarchiver.unarchiveObject(with: data) as? Set<String> {
self.followedIdentifiers = myFollowedList
self.followedIdentifiers = Set(UserDefaults.standard.stringArray(forKey: "followedID")!) // CRASH
} else {
print("Error/ Empty: (Loading UserDefaults (followedID))")
}
}
// Retrieving Data from Server
func retrieveDataFromServer() {
let getDataURL = "http://example.com/receiving.php"
let url: NSURL = NSURL(string: getDataURL)!
do {
let data: Data = try Data(contentsOf: url as URL)
let jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! NSMutableArray
// Clear the arrays
self.followedArray = [Blog]()
self.mainArray = [Blog]()
// Looping through jsonArray
for jsonObject in jsonArray {
if let blog = Blog(jsonObject:jsonObject as! [String : Any]) {
// Check if identifiers match
if followedIdentifiers.contains(blog.blogID) {
self.followedArray.append(blog)
} else {
self.mainArray.append(blog)
}
}
}
} catch {
print("Error: (Retrieving Data)")
}
myTableView.reloadData()
}
This is Blog.swift which handles all the blogs objects and NSCoding
class Blog: NSObject, NSCoding {
var blogName: String
var blogStatus1: String
var blogStatus2: String
var blogURL: String
var blogID: String
var blogType: String
var blogDate: String
var blogPop: String
private init (name: String,status1: String,status2: String,url: String,id: String,type: String,date: String,pop: String) {
blogName = name
blogStatus1 = status1
blogStatus2 = status2
blogURL = url
blogID = id
blogType = type
blogDate = date
blogPop = pop
super.init()
}
convenience init?(jsonObject: [String:Any]) {
guard let bID = jsonObject["id"] as? String,
let bName = jsonObject["blogName"] as? String,
let bStatus1 = jsonObject["blogStatus1"] as? String,
let bStatus2 = jsonObject["blogStatus2"] as? String,
let bURL = jsonObject["blogURL"] as? String,
let bType = jsonObject["blogType"] as? String,
let bDate = jsonObject["blogDate"] as? String,
let bPop = jsonObject["blogPop"] as? String
else {
print("Error: (Creating Blog Object)")
return nil
}
self.init(name: bName, status1: bStatus1, status2: bStatus2, url: bURL, id: bID, type: bType, date: bDate, pop: bPop)
}
convenience required init?(coder aDecoder: NSCoder) {
guard let blogName = aDecoder.decodeObject(forKey: "blogName") as? String,
let blogStatus1 = aDecoder.decodeObject(forKey: "blogStatus1") as? String,
let blogStatus2 = aDecoder.decodeObject(forKey: "blogStatus2") as? String,
let blogURL = aDecoder.decodeObject(forKey: "blogURL") as? String,
let blogID = aDecoder.decodeObject(forKey: "blogID") as? String,
let blogType = aDecoder.decodeObject(forKey: "blogType") as? String,
let blogDate = aDecoder.decodeObject(forKey: "blogDate") as? String,
let blogPop = aDecoder.decodeObject(forKey: "blogPop") as? String else {
print("Error: (Creating Blog Object)")
return nil
}
self.init(name: blogName, status1: blogStatus1, status2: blogStatus2, url: blogURL, id: blogID, type: blogType, date: blogDate, pop: blogPop)
}
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")
}
}
I used File Manager to save my archived array and it was the best choice, so easy and simple. Using Swift 3. Credit to #GuyKogus, #Paulw11 & #MartinR
Used to save my object
NSKeyedArchiver.archiveRootObject(myObject, toFile: filePath)
Used to load my object
if let myFollowedList = NSKeyedUnarchiver.unarchiveObject(withFile: filePath) as? Set<String> {
myObject = myFollowedList
}
Using Swift 3, blog reader app reading from a MYSQL database using JSON and PHP. User has the ability to save the blog they want to keep getting updates from using a follow button (as well as unfollow button). Instead of saving the entire array, just trying to save the followed blogs ID so the app just finds the blogs id and shows that specific blog that the user followed.
This is the error I'm getting when loading the user defaults
Initializer for conditional binding must have Optional type, not 'Void' (aka '()')
This error is in func loadUserDefaults() in MainController.swift at first line if let data = UserDefaults..
After the user clicks the follow button, I move the cells between arrays, between sections in the tableview and then I call saveUserDefaults()
This is MainController.swift
var mainArray = [Blog]()
var followedArray = [Blog]()
var filteredArray = [Blog]()
var followedIdentifiers = Set<String>()
override func viewDidLoad() {
super.viewDidLoad()
// Receiving Data from Server
retrieveDataFromServer()
// NSCoding - Unarchiving Data (followedID)
loadUserDefaults()
}
// NSCoding: Archiving UserDefaults
func saveUserDefaults() {
// Saving to UserDefaults
let encodedData = NSKeyedArchiver.archivedData(withRootObject: self.followedIdentifiers)
UserDefaults.standard.setValue(encodedData, forKey: "followedID")
UserDefaults.standard.synchronize()
}
// NSCoding: Unarchiving UserDefaults *** ERROR IS HERE ***
func loadUserDefaults() {
// Unarchiving Data -- ERROR: THIS FIRST LINE --
if let data = UserDefaults.standard.setValue(Array(self.followedIdentifiers), forKey: "followedID"),
let myFollowedList = NSKeyedUnarchiver.unarchiveObject(with: data) as? [Blog] {
self.followedIdentifiers = myFollowedList
self.followedIdentifiers = Set(UserDefaults.standard.stringArray(forKey: "followedID")!)
} else {
print("Error/ Empty: (Loading UserDefaults (followedID))")
}
}
// Retrieving Data from Server
func retrieveDataFromServer() {
let getDataURL = "http://example.com/receiving.php"
let url: NSURL = NSURL(string: getDataURL)!
do {
let data: Data = try Data(contentsOf: url as URL)
let jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! NSMutableArray
// Clear the arrays
self.followedArray = [Blog]()
self.mainArray = [Blog]()
// Looping through jsonArray
for jsonObject in jsonArray {
if let blog = Blog(jsonObject:jsonObject as! [String : Any]) {
// Check if identifiers match
if followedIdentifiers.contains(blog.blogID) {
self.followedArray.append(blog)
} else {
self.mainArray.append(blog)
}
}
}
} catch {
print("Error: (Retrieving Data)")
}
myTableView.reloadData()
}
This is Blog.swift which handles all the blogs objects and NSCoding
class Blog: NSObject, NSCoding {
var blogName: String
var blogStatus1: String
var blogStatus2: String
var blogURL: String
var blogID: String
var blogType: String
var blogDate: String
var blogPop: String
private init (name: String,status1: String,status2: String,url: String,id: String,type: String,date: String,pop: String) {
blogName = name
blogStatus1 = status1
blogStatus2 = status2
blogURL = url
blogID = id
blogType = type
blogDate = date
blogPop = pop
super.init()
}
convenience init?(jsonObject: [String:Any]) {
guard let bID = jsonObject["id"] as? String,
let bName = jsonObject["blogName"] as? String,
let bStatus1 = jsonObject["blogStatus1"] as? String,
let bStatus2 = jsonObject["blogStatus2"] as? String,
let bURL = jsonObject["blogURL"] as? String,
let bType = jsonObject["blogType"] as? String,
let bDate = jsonObject["blogDate"] as? String,
let bPop = jsonObject["blogPop"] as? String
else {
print("Error: (Creating Blog Object)")
return nil
}
self.init(name: bName, status1: bStatus1, status2: bStatus2, url: bURL, id: bID, type: bType, date: bDate, pop: bPop)
}
convenience required init?(coder aDecoder: NSCoder) {
guard let blogName = aDecoder.decodeObject(forKey: "blogName") as? String,
let blogStatus1 = aDecoder.decodeObject(forKey: "blogStatus1") as? String,
let blogStatus2 = aDecoder.decodeObject(forKey: "blogStatus2") as? String,
let blogURL = aDecoder.decodeObject(forKey: "blogURL") as? String,
let blogID = aDecoder.decodeObject(forKey: "blogID") as? String,
let blogType = aDecoder.decodeObject(forKey: "blogType") as? String,
let blogDate = aDecoder.decodeObject(forKey: "blogDate") as? String,
let blogPop = aDecoder.decodeObject(forKey: "blogPop") as? String else {
print("Error: (Creating Blog Object)")
return nil
}
self.init(name: blogName, status1: blogStatus1, status2: blogStatus2, url: blogURL, id: blogID, type: blogType, date: blogDate, pop: blogPop)
}
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")
}
}
The line of code:
let data = UserDefaults.standard.setValue(Array(self.followedIdentifiers), forKey: "followedID")
makes no sense since setValue method doesn't return any values.
Looks like you need to replace it with (Swift 3):
let data = UserDefaults.standard.data(forKey: "followedID")
Updated with #closetCoder comment
The setValue function is a void function so it can't be assigned to the variable data since it has no return value.
Here is the documentation for UserDefaults from Apple: https://developer.apple.com/documentation/foundation/userdefaults
Error causes a crash when using NSCoder to archive custom objects in array from json.
Error causing crash:
fatal error: unexpectedly found nil while unwrapping an Optional value
Code of error: I included the full code in retrieveData()
// NSCoding *Error causes Crash here*
let blogList: NSObject = ((jsonArray[i]) as! NSObject).value(forKey: "blogList") as! NSObject
MainController.swift
// Retrieving Data from Server *Clean Code*
func retrieveData() {
let getDataURL = "http://blogtest.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
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 *Error causes Crash here*
let blogList: NSObject = ((jsonArray[i]) as! NSObject).value(forKey: "blogList") as! NSObject
// Add Blog Objects to mainArray
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)")
}
myTableView.reloadData()
}
Blog.swift - Handles the Blog custom objects
import UIKit
class Blog: NSObject, 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] // 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 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 // NSCoding
super.init()
}
// NSCoding
convenience required init?(coder aDecoder: NSCoder) {
self.init (coder : aDecoder)
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]
}
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")
}
}
This crash have nothing to with NSCoder. Check in debugger or log the value of value(forKey: "blogList"). It most likely don't exist. Can you give sample response of what you are getting in network response.
Also it's my suggestion not to use forced unwrapping instead use optional with guard as server response can not be trusted and this could start causing crash in future
You should change constructor to below
init?(id:String, info:[String:AnyObject]){
guard let name = info["blogName"] as? String, let blogStatus1 = info["blogStatus1"] as? String ... so on .. else {
return nil
}
blogName = name
.
. initialize all field like these
// for blog list
var tmpBlogList = [BlogList]()
if let tmpBlogListInfo = info["blogList"] as? [[String:AnyObject]]
{
for info in tmpBlogListInfo
{
let childId = info["blogID"] as? String
if let blog = Blog(id:childId, info) {
tmpBlogList.append(blog)
}
}
}
blogList = tmpBlogList
}
//Replace mainArray.append(Blog(blogName: bName, andBlogStatus1: bStatus1, andBlogStatus2: bStatus2, andBlogURL: bURL, andBlogID: bID, andBlogType: bType, andBlogDate: bDate, andBlogPop: bPop, blogList: blogList as! [Blog]))
if let blog = Blog(id: bID, info: blogList) {
mainArray.append(blog)
}