Search code examples
iosswiftnsuserdefaultsexc-bad-accessnskeyedarchiver

Swift: self.init (coder : aDecoder) is crashing app with EXC_BAD_ACCESS


Error is crashing app when using NSCoder and NSKeyArchiver.

enter image description here 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")
    }
}

Solution

  • 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.