将 JSON 保存到 Realm

Saving JSON into Realm

对于我的项目,我想保存从领域中的 API 获取的数据,然后将其显示在 table 视图中。 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
}
  }

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

Invalid conversion from throwing function of type '(Data?, URLResponse?, Error?) throws -> Void' to non-throwing function type '(Data?, URLResponse?, Error?) -> Void'

'let' declarations cannot be computed properties

您放置 catch 语句的位置有误——它应该与 do { } 块一致。如果您 format/indent 您的代码(Xcode 中的 Ctrl-i),这可能更容易看到。

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
}

可能有 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)")
}

并且 Realm 对象是

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