skip to Main Content

Hi I’m a beginner at programming and I followed a tutorial online to parse information from a Json file. In order to get an URL link, I created a local server that contains a json file. When I start the program I get a mistake.

Called Thread 2: signal SIGABRT for the code snippet            
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]

Google says there is a disconnected outlet, but the outlets in my code are connected.

There are also multiple error warnings, and google says there is a problem with an array and I have to set it to NSDictionary, but I never declared any variable as an array or a dictionary.

Could not cast value of type '__NSArrayM' (0x7fff80919120) to 'NSDictionary' (0x7fff809193b0).
2021-06-30 22:01:07.257592+0200 Booknerd3.0[71572:3475394] Could not cast value of type '__NSArrayM' (0x7fff80919120) to 'NSDictionary' (0x7fff809193b0).
Could not cast value of type '__NSArrayM' (0x7fff80919120) to 'NSDictionary' (0x7fff809193b0).
CoreSimulator 757.5 - Device: iPhone 8 (A2400EB4-8BA3-4297-9DF3-1743AA0BB7E4) - Runtime: iOS 14.5 (18E182) - DeviceType: iPhone 8

Json file:

{
    "books":
    [
        {
            "author": "Chinua Achebe",
            "country": "Nigeria",
            "imageLink": "https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ.book-thumb-images/ableson.jpg",
            "language": "English",
            "link": "https://en.wikipedia.org/wiki/Things_Fall_Apartn",
            "pages": 209,
            "title": "Things Fall Apart",
            "year": 1958
        },
        {
            "author": "Hans Christian Andersen",
            "country": "Denmark",
            "imageLink": "https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ.book-thumb-images/ableson2.jpg",
            "language": "Danish",
            "link": "https://en.wikipedia.org/wiki/Fairy_Tales_Told_for_Children._First_Collection.n",
            "pages": 784,
            "title": "Fairy tales",
            "year": 1836
        }
    ]
}

My view controller

import UIKit

class MainScreenViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableview: UITableView!
    
    var books: [Books]? = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        fetchArticle()
        
    }
    
    func fetchArticle(){
        let urlRequest = URLRequest(url: URL(string: "http://localhost:3000/books")!)
        
        let task = URLSession.shared.dataTask(with: urlRequest) {  (data,response,error) in

            if error != nil {
                print(error)
                return
            }
            self.books = [Books]()
            do {
                let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: AnyObject]

                if let booksFromJson = json["books"] as? [[String : AnyObject]]{
                    for bookFromJson in booksFromJson {
                        let book = Books()
                        if let title = bookFromJson["title"] as? String, let author = bookFromJson["author"] as? String, let imageLink = bookFromJson["imageLink"] as? String {

                            book.author = author
                            book.title = title
                            book.imageLink = imageLink
                        }
                        self.books?.append(book)
                    }
                }
                DispatchQueue.main.async {
                    self.tableview.reloadData()
                }

                } catch let error {
                    print(error)
                }
            }
            task.resume()
        }


        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "bookCell", for: indexPath) as! BookCell
            
            cell.title.text = self.books?[indexPath.item].title
            cell.author.text = self.books?[indexPath.item].author

            return cell
        }
        
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return self.books?.count ?? 0
        }

        func numberOfSections(in tableView: UITableView) -> Int {
            return 1
        }
    }
}

2

Answers


  1. A lot of force unwraps, assumptions about the type. Just don’t if you want to have a stable app. Most likely you’re looking for usage of Decodable. You can declare two structs to reflect the JSON schema.

    For your example it would be:

    struct BooksResponse: Decodable {
        let books: [Book]
    }
    
    struct Book: Decodable {
        let author: String
        let imageLink: URL
        let title: String
    }
    

    Which can be used in your code with few modifications.

    // This part declared somewhere
    enum JsonBooksDecodingError: Error {
        case dataCorrupted
    }
    // -- 
    
    func fetchArticle() {
        guard  let urlRequest = URLRequest(url: URL(string: "http://localhost:3000/books")) else {
            assert(false, "URL broken")
        }
    
        URLSession.shared.dataTask(with: urlRequest) {  (data,response,error) in
    
            if error != nil {
                print(error)
                return
            }
    
            do {
                let booksResponse = try handle(booksData: data)
                self.books = booksResponse.books
                
                DispatchQueue.main.async {
                    self.tableview.reloadData()
                }
            }
    
            } catch let error {
                print(error)
            }
        }
        .resume()
    }   
    
    func handle(booksData data: Data) throws -> BooksResponse {
        guard let jsonData = jsonData else {
            throw JsonBooksDecodingError.dataCorrupted
        }
    
        return try JSONDecoder().decode(BooksResponse.self, from: jsonData)
    }
    

    No force unwraps, no guessing, zero crashes :). This approach lets you have custom decoding strategies as well. You can read more about Codable (Decodable & Encodable) here

    Login or Signup to reply.
  2. Short answer your Json is a Dictionary that contains a key with a value of array of dictionary. When you decode you’re saying it will be a dictionary key of books and value of array of array of dictionary.

    if let booksFromJson = json["books"] as? [[String : AnyObject]]...
    

    an array of dictionary is

    [String:Any]
    

    more specifically in your case

    [String:[String: Any]]
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search