0

对于我的项目,我想将我从 API 获取的数据保存在领域中,然后将其显示在表格视图中。JSON 将如下所示:

{"books":[{"author":"Chinua Achebe", "title":"Things Fall Apart","imageLink":"http://books.google.com/books/content?id=plk_nwEACAAJ&printsec=frontcover&img=1&zoom=5&source=gbs_api"}]}

我尝试了一些不同的方法,但我不知道如何正确解码和存储 JSON。我之前使用过这个函数来解析 JSON,但是当我添加领域代码时,我得到了错误。

我获取 JSON 的函数是:

func fetchArticle(){

let urlRequest = URLRequest(url: URL(string: "https:/mocki.io/v1/89aa9fe9-fdba-463f-99b3-5d8b6bc1d32e")!)

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)
            let realm = try! Realm()
            for books in bookFromJson {
                try! realm.write {
                    realm.add(books, update: .all)
        }
    }
    DispatchQueue.main.async {
        self.tableview.reloadData()
    }
    
} catch let error {
    print(error)
}
}
    task.resume()
}
    }
}

这是我的结构:

class Books: Object, Decodable {
    @objc dynamic var author: String?
    @objc dynamic var imageLink: String?
    @objc dynamic var title: String?
    
convenience init(author: String, imageLink: String, title: String) {
     self.init()
     self.author = author
     self.imageLink = imageLink
     self.title = title
  }

    override static func primaryKey() -> String? {
            return "author"
        }
        
        private enum CodingKeys: String, CodingKey {
            case author
            case imageLink
            case title
}
  }

这是我在函数中遇到的错误:

从 '(Data?, URLResponse?, Error?) throws -> Void' 类型的抛出函数到非抛出函数类型 '(Data?, URLResponse?, Error?) -> Void' 的无效转换

'let' 声明不能是计算属性

4

2 回答 2

1

您放置catch语句的位置有误 - 它应该与do { }块一致。Ctrl-i如果您格式化/缩进代码(在 Xcode 中),这可能更容易查看。

func fetchArticle(){
    
    let urlRequest = URLRequest(url: URL(string: "https:/mocki.io/v1/89aa9fe9-fdba-463f-99b3-5d8b6bc1d32e")!)
    
    let task = URLSession.shared.dataTask(with: urlRequest) {  (data,response,error) in
        if let error = error {
            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)
                    let realm = try! Realm()
                    for books in bookFromJson {
                        try! realm.write {
                            realm.add(books, update: .all)
                        }
                    }
                    DispatchQueue.main.async {
                        self.tableview.reloadData()
                    }
                }
            }
        } catch { //<-- no need for `let error`
            print(error)
        }
    }
    task.resume() //<-- Moved outside the declaration
}
于 2021-07-15T17:19:40.030 回答
1

可能有 100 种不同的方法可以将您的 json 映射到领域对象,但让我们保持简单。首先,我假设您传入的 json 可能是几本书,所以它看起来像这样

let jsonStringWithKey = """
    {
        "books":
            [{
                "author":"Chinua Achebe",
                "title":"Things Fall Apart",
                "imageLink":"someLink"
            },
            {
                "author":"another author",
                "title":"book title",
                "imageLink":"another link"
            }]
    }
"""

所以将其编码为数据

guard let jsonDataWithKey = jsonStringWithKey.data(using: .utf8) else { return }

然后,使用 JSONSerialization 将其映射到一个数组。请记住,顶级对象是“书籍”,AnyObject 将是所有子数据

do {
    if let json = try JSONSerialization.jsonObject(with: jsonDataWithKey) as? [String: AnyObject] {
        if let bookArray = json["books"] as? [[String:AnyObject]] {
            for eachBook in bookArray {
                let book = Book(withBookDict: eachBook)
                try! realm.write {
                    realm.add(book)
                }
            }
        }
    }
} catch {
    print("Error deserializing JSON: \(error)")
}

领域对象是

class Book: Object, Codable {
    @objc dynamic var author = ""
    @objc dynamic var title = ""
    @objc dynamic var imageLink = ""

    convenience init(withBookDict: [String: Any]) {
        self.init()

        self.author = withBookDict["author"] as? String ?? "No Author"
        self.title = withBookDict["title"] as? String ?? "No Title"
        self.imageLink = withBookDict["imageLink"] as? String ?? "No link"
    }
}

同样,有很多不同的方法来处理这个问题,所以这是可以扩展的基础知识。

作为建议,Realm Results 是具有相应事件的实时更新对象。因此,您可以做的一件巧妙的事情是使结果对象成为您的 tableView 数据源并向其添加观察者。

结果对象的工作方式与数组非常相似。

随着书籍的添加、更新或从领域中删除,结果对象将反映这些更改,并且将为每个更改触发一个事件 - 这使得保持 tableView 更新非常简单。

所以在你的 viewController

class ViewController: NSViewController {
    var bookResults: Results<PersonClass>? = nil
    @IBOutlet weak var bookTableView: NSTableView!
    var bookToken: NotificationToken?

    self.bookResults = realm.objects(Book.self)

进而

override func viewDidLoad() {
   super.viewDidLoad()

   self.bookToken = self.bookResults!.observe { changes in
      //update the tableView when bookResults change
于 2021-07-15T18:50:21.387 回答