Save json to CoreData as String and use the String to create array of objects

前端 未结 2 500
醉酒成梦
醉酒成梦 2021-02-03 16:12

I am creating an app for a radio station and I want to store \"show\" objects into an array. I use a webserver to supply json data to populate the array, but I want to store thi

相关标签:
2条回答
  • 2021-02-03 16:53

    Try this it's work for me

    // Convert JSON to String
    func jsonToString(json: AnyObject)->String{
        do {
            let data1 =  try JSONSerialization.data(withJSONObject: json, options: JSONSerialization.WritingOptions.prettyPrinted) // first of all convert json to the data
            let convertedString = String(data: data1, encoding: String.Encoding.utf8) // the data will be converted to the string
            return convertedString! // <-- here is ur string
    
        } catch let myJSONError {
            print(myJSONError)
        }
    
        return ""
    }
    
    // Convert JSON String to Dict
    func convertToDictionary(text: String) -> NSDictionary!{
        if let data = text.data(using: .utf8) {
            do {
    
                return try JSONSerialization.jsonObject(with: data, options: []) as? NSDictionary
            } catch {
                print(error.localizedDescription)
            }
        }
        return nil
    }
    
    0 讨论(0)
  • 2021-02-03 17:04

    It's a bad practice to store json data or 'whole raw data' into CoreData. Instead Store the Show itself as a NSManagedObject. You can do this by converting the JSON data to an Object (which it looks like you are already doing), then creating CoreData NSManagedObjects from them.

    Realistically if you have no trouble converting the data from JSON there is no need to convert it to a string before saving to CoreData. You can simply store the Data as NSData, i.e. transformable or binary data and reconvert it later if your fetch to the server fails.

    However, thats not that reliable and much harder to work with in the long run. The data could be corrupt and/or malformed.

    In short, you need a Data Model and a JSON readable Data Structure you can Convert to your Data Model to for CoreData to manage. This will become important later when you want to allow the user to update, remove, save or filter individual Show's.

    Codable will allow you to covert from JSON to a Struct with JSONDecoder().decode(_:from:).

    ShowModelCodeable.swift

    import Foundation
    
    struct ShowModelCodeable: Codable {
        var name: String?
        var description: String?
        var producer: String?
        var thumb: String?
        var live: String?
        var banner: String?
        var id: String?
    
        enum CodingKeys: String, CodingKey {
            case name = "Name"
            case id = "ID"
            case description = "Description"
            case producer = "Producer"
            case thumb = "Thumb"
            case live = "Live"
            case banner = "Banner"
        }
    
        init(from decoder: Decoder) throws {
            let values = try decoder.container(keyedBy: CodingKeys.self)
            name = try values.decode(String.self, forKey: .name)
            description = try values.decode(String.self, forKey: .description)
            producer = try values.decode(String.self, forKey: .producer)
            thumb = try values.decode(String.self, forKey: .thumb)
            live = try values.decode(String.self, forKey: .live)
            banner = try values.decode(String.self, forKey: .banner)
            id = try values.decode(String.self, forKey: .id)
    
        }
    
        func encode(to encoder: Encoder) throws {
    
        }
    }
    

    Next, We'll need a Core Data Stack and a CoreData Entity. Its very common to create a Core Data Stack as a Class Singleton that can be accessed anywhere in your app. I've included one with basic operations:

    DatabaseController.Swift

    import Foundation
    import CoreData
    
    class DatabaseController {
    
        private init() {}
    
        //Returns the current Persistent Container for CoreData
        class func getContext () -> NSManagedObjectContext {
            return DatabaseController.persistentContainer.viewContext
        }
    
    
        static var persistentContainer: NSPersistentContainer = {
            //The container that holds both data model entities
            let container = NSPersistentContainer(name: "StackOverflow")
    
            container.loadPersistentStores(completionHandler: { (storeDescription, error) in
                if let error = error as NSError? {
                    // Replace this implementation with code to handle the error appropriately.
                    // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
    
                    /*
                     Typical reasons for an error here include:
                     * The parent directory does not exist, cannot be created, or disallows writing.
                     * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                     * The device is out of space.
                     * The store could not be migrated to the current model version.
                     Check the error message to determine what the actual problem was.
                     */
    
                    //TODO: - Add Error Handling for Core Data
    
                    fatalError("Unresolved error \(error), \(error.userInfo)")
                }
    
    
            })
            return container
        }()
    
        // MARK: - Core Data Saving support
        class func saveContext() {
            let context = self.getContext()
            if context.hasChanges {
                do {
                    try context.save()
                    print("Data Saved to Context")
                } catch {
                    // Replace this implementation with code to handle the error appropriately.
                    // fatalError() causes the application to generate a crash log and terminate.
                    //You should not use this function in a shipping application, although it may be useful during development.
                    let nserror = error as NSError
                    fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
                }
            }
        }
    
        /* Support for GRUD Operations */
    
        // GET / Fetch / Requests
        class func getAllShows() -> Array<ShowModel> {
            let all = NSFetchRequest<ShowModel>(entityName: "ShowModel")
            var allShows = [ShowModel]()
    
            do {
                let fetched = try DatabaseController.getContext().fetch(all)
                allShows = fetched
            } catch {
                let nserror = error as NSError
                //TODO: Handle Error
                print(nserror.description)
            }
    
            return allShows
        }
    
        // Get Show by uuid
        class func getShowWith(uuid: String) -> ShowModel? {
            let requested = NSFetchRequest<ShowModel>(entityName: "ShowModel")
            requested.predicate = NSPredicate(format: "uuid == %@", uuid)
    
            do {
                let fetched = try DatabaseController.getContext().fetch(requested)
    
                //fetched is an array we need to convert it to a single object
                if (fetched.count > 1) {
                    //TODO: handle duplicate records
                } else {
                    return fetched.first //only use the first object..
                }
            } catch {
                let nserror = error as NSError
                //TODO: Handle error
                print(nserror.description)
            }
    
            return nil
        }
    
        // REMOVE / Delete
        class func deleteShow(with uuid: String) -> Bool {
            let success: Bool = true
    
            let requested = NSFetchRequest<ShowModel>(entityName: "ShowModel")
            requested.predicate = NSPredicate(format: "uuid == %@", uuid)
    
    
            do {
                let fetched = try DatabaseController.getContext().fetch(requested)
                for show in fetched {
                    DatabaseController.getContext().delete(show)
                }
                return success
            } catch {
                let nserror = error as NSError
                //TODO: Handle Error
                print(nserror.description)
            }
    
            return !success
        }
    
    }
    
    // Delete ALL SHOWS From CoreData
    class func deleteAllShows() {
        do {
            let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "ShowModel")
            let deleteALL = NSBatchDeleteRequest(fetchRequest: deleteFetch)
    
            try DatabaseController.getContext().execute(deleteALL)
            DatabaseController.saveContext()
        } catch {
            print ("There is an error in deleting records")
        }
    }
    

    Finally, we need a way to get the JSON data and convert it to our Objects, then Display it. Note that when the update button is pressed, it fires getDataFromServer(). The most important line here is

    self.newShows = try JSONDecoder().decode([ShowModelCodeable].self, from: dataResponse)
    

    The Shows are being pulled down from your Server, and converted to ShowModelCodeable Objects. Once newShows is set it will run the code in didSet, here you can delete all the Objects in the context, then run addNewShowsToCoreData(_:) to create new NSManagedObjects to be saved in the context.

    I've created a basic view controller and programmatically added a tableView to manage the data. Here, Shows is your NSManagedObject array from CoreData, and newShows are new objects encoded from json that we got from the server request.

    ViewController.swift

    import Foundation
    import UIKit
    import CoreData
    
    class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
        // Properties
        var Shows:[ShowModel]?
    
        var newShows:[ShowModelCodeable]? {
            didSet {
                // Remove all Previous Records
                DatabaseController.deleteAllShows()
                // Add the new spots to Core Data Context
                self.addNewShowsToCoreData(self.newShows!)
                // Save them to Core Data
                DatabaseController.saveContext()
                // Reload the tableView
                self.reloadTableView()
            }
        }
    
        // Views
        var tableView: UITableView = {
            let v = UITableView()
            v.translatesAutoresizingMaskIntoConstraints = false
            return v
        }()
    
        lazy var updateButton: UIButton = {
            let b = UIButton()
            b.translatesAutoresizingMaskIntoConstraints = false
            b.setTitle("Update", for: .normal)
            b.setTitleColor(.black, for: .normal)
            b.isEnabled = true
            b.addTarget(self, action: #selector(getDataFromServer), for: .touchUpInside)
            return b
        }()
    
        override func viewWillAppear(_ animated: Bool) {
            self.Shows = DatabaseController.getAllShows()
        }
    
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
    
            self.tableView.delegate = self
            self.tableView.dataSource = self
            self.tableView.register(ShowCell.self, forCellReuseIdentifier: ShowCell.identifier)
    
            self.layoutSubViews()
    
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
    
        //TableView -
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return DatabaseController.getAllShows().count
        }
    
        func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
            // 100
            return ShowCell.height()
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = self.tableView.dequeueReusableCell(withIdentifier: ShowCell.identifier) as! ShowCell
    
            self.Shows = DatabaseController.getAllShows()
    
            if Shows?.count != 0 {
                if let name = Shows?[indexPath.row].name {
                    cell.nameLabel.text = name
                }
    
                if let descriptionInfo = Shows?[indexPath.row].info {
                    cell.descriptionLabel.text = descriptionInfo
                }
            } else {
                print("No shows bros")
            }
    
    
            return cell
        }
    
        func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            // Show the contents
            print(Shows?[indexPath.row] ?? "No Data For this Row.")
    
        }
    
        func reloadTableView() {
            DispatchQueue.main.async {
             self.tableView.reloadData()
            }
        }
    
    
        func layoutSubViews() {
            let guide = self.view.safeAreaLayoutGuide
            let spacing: CGFloat = 8
    
            self.view.addSubview(tableView)
            self.view.addSubview(updateButton)
    
            updateButton.topAnchor.constraint(equalTo: guide.topAnchor, constant: spacing).isActive = true
            updateButton.leftAnchor.constraint(equalTo: guide.leftAnchor, constant: spacing * 4).isActive = true
            updateButton.rightAnchor.constraint(equalTo: guide.rightAnchor, constant: spacing * -4).isActive = true
            updateButton.heightAnchor.constraint(equalToConstant: 55.0).isActive = true
    
            tableView.topAnchor.constraint(equalTo: updateButton.bottomAnchor, constant: spacing).isActive = true
            tableView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
            tableView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
            tableView.bottomAnchor.constraint(equalTo: guide.bottomAnchor, constant: spacing).isActive = true
        }
    
    
        @objc func getDataFromServer() {
            print("Updating...")
            let urlPath = "http://dogradioappdatabase.com/shows.php"
    
            guard let url = URL(string: urlPath) else {return}
    
            let task = URLSession.shared.dataTask(with: url) {
                (data, response, error) in
                    guard let dataResponse = data, error == nil else {
                            print(error?.localizedDescription ?? "Response Error")
                    return }
    
                do {
                    self.newShows = try JSONDecoder().decode([ShowModelCodeable].self, from: dataResponse)
                } catch {
                    print(error)
                }
    
            }
    
            task.resume()
        }
    
    
    
    
        func addNewShowsToCoreData(_ shows: [ShowModelCodeable]) {
    
            for show in shows {
                let entity = NSEntityDescription.entity(forEntityName: "ShowModel", in: DatabaseController.getContext())
                let newShow = NSManagedObject(entity: entity!, insertInto: DatabaseController.getContext())
    
                // Create a unique ID for the Show.
                let uuid = UUID()
                // Set the data to the entity
                newShow.setValue(show.name, forKey: "name")
                newShow.setValue(show.description, forKey: "info")
                newShow.setValue(show.producer, forKey: "producer")
                newShow.setValue(show.thumb, forKey: "thumb")
                newShow.setValue(show.live, forKey: "live")
                newShow.setValue(show.banner, forKey: "banner")
                newShow.setValue(show.id, forKey: "id")
                newShow.setValue(uuid.uuidString, forKey: "uuid")
            }
    
        }
    
    
    
    }
    

    0 讨论(0)
提交回复
热议问题