0

my scenario, I am loading JSON Data into CoreData, after that I am fetching into Tableview. Now, Each and every tableview cell have swipe with Delete and Edit button. If I click delete I need to remove data from coredata and tableview both place.

My JSON Structure

   class displyDataClass {
    var name : String
    var username : String
    var email : String

init(name : String,username : String,email :String) {
    self.name = name
    self.username = username
    self.email = email
   }
}

JSON Load Into CoreData

import UIKit
import CoreData

class ViewController: UIViewController ,UITableViewDelegate,UITableViewDataSource{

var displayDatasssss = [displyDataClass]()
var context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

@IBOutlet weak var tableView: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()
    print("hai")

    // Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return displayDatasssss.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell1") as! TableViewCell1





    let request = NSFetchRequest<NSFetchRequestResult>(entityName: "User")
    cell.label.text = displayDatasssss[indexPath.row].email


    let _:AppDelegate = (UIApplication.shared.delegate as! AppDelegate)
    let context:NSManagedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    let newUser = NSEntityDescription.insertNewObject(forEntityName: "User", into: context) as NSManagedObject
    newUser.setValue(cell.label.text, forKey: "name")

    do {
        try context.save()
    } catch {}
    print(newUser)
    print("Object Saved.")


    let myStringValue = cell.label.text
    request.predicate = NSPredicate (format: "name == %@", myStringValue!)
    do
    {
        let result = try context.fetch(request)
        if result.count > 0
        {
            let nameData = (result[0] as AnyObject).value(forKey: "name") as! String
            print(nameData)

        }
    }
    catch {
        //handle error
        print(error)
    }

    return cell
}

@IBAction func tap(_ sender: Any) {
    let url = "http://jsonplaceholder.typicode.com/users"
    var request = URLRequest(url: URL(string: url)!)
    request.httpMethod = "GET"
    let configuration = URLSessionConfiguration.default
    let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
    let task = session.dataTask(with: request){(data, response,error)in
        if (error != nil){
            print("Error")
        }
        else{
            do{
                // Array of Data
                let fetchData = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as! NSArray

                for eachData in fetchData {

                    let eachdataitem = eachData as! [String : Any]
                    let name = eachdataitem["name"]as! String
                    let username = eachdataitem["username"]as! String

                    let email = eachdataitem["email"]as! String
                    self.displayDatasssss.append(displyDataClass(name: name, username: username,email : email))
                }
                self.tableView.reloadData()
            }
            catch{
                print("Error 2")
            }

        }
    }
    task.resume()

    }
}



class displyDataClass {
    var name : String
    var username : String
    var email : String

init(name : String,username : String,email :String) {
    self.name = name
    self.username = username
    self.email = email
   }
}

Below code For delete

// delete action two
        let deleteAction = UITableViewRowAction(style: .default, title: "Delete", handler: { (action, indexPath) in
            print("Delete tapped")

            // remove the deleted item from the model
            let appDel:AppDelegate = UIApplication.shared.delegate as! AppDelegate
            let managedObjectContext = appDel.persistentContainer.viewContext
            managedObjectContext.delete(self.displayDatasssssindexPath.row])
            self.milestoneTitles.remove(at: indexPath.row)
            do {
                try managedObjectContext.save()
            } catch _ {
            }

            self.tableView.deleteRows(at: [indexPath], with: .automatic)
           return [editAction, deleteAction]
       }
Tim
  • 37
  • 6
  • 1
    Please explain more properly your question. – dahiya_boy Jan 07 '20 at 09:54
  • @dahiya_boy I am getting error `Cannot convert value of type 'displyDataClass' to expected argument type 'NSManagedObject'` at `managedObjectContext.delete(self.displayDatasssssindexPath.row])` – Tim Jan 07 '20 at 09:58
  • 1
    If you are new in iOS or have less knowledge in core data then I recommend to refer this -> https://code.tutsplus.com/tutorials/core-data-and-swift-managed-objects-and-fetch-requests--cms-25068 , and there will be more post will be available. After that if you still have any problem then we are happy to help you out. – dahiya_boy Jan 07 '20 at 10:06
  • For right now, error is saying `displyDataClass` is not the `NSManageObject`. `NSManageObject` is the object that you created the entity in the schema. Xcode internally manages the `NSManageObject` and you need to do query on that object only. One more tip, on deleting the cell from the tableView, you need to delete the datasource first. [reference](https://stackoverflow.com/questions/40156274/deleting-a-row-from-a-uitableview-in-swift-3) – dahiya_boy Jan 07 '20 at 10:11
  • @dahiya_boy I am storing JSON data into `displayDatasssss = [displyDataClass]()` and storing values into coredata now how to delete particular data from coredata using Swift – Tim Jan 07 '20 at 10:24
  • Basically the delete method is correct. But you have to use a `NSManagedObject` subclass as data source . And **never** insert new objects in `cellForRow`. That's definitely the wrong place. Apart from that please name classes and structs with starting capital letter. – vadian Jan 07 '20 at 10:42
  • @vadian Thank you so much. can you provide me alternative way of sample code? I am trying this long time but didn't get achived – Tim Jan 07 '20 at 10:43
  • Please add the `@NSManaged` properties of `User` – vadian Jan 07 '20 at 10:44
  • @vadian I have updated my question. Please check it – Tim Jan 07 '20 at 10:49
  • No, I don't need the custom class, I need the properties (attributes) of the entity `User` mentioned in `NSEntityDescription.insertNewObject(forEntityName: "User"`. Depending on the settings there must be a `NSManagedObject` subclass or they are described in the Core Data model like in dahiya's answer. – vadian Jan 07 '20 at 10:50
  • @vadian Entity name `User` and adding property attribute `id` and `name` both are String values. – Tim Jan 07 '20 at 10:53
  • @vadian anything updates? please help me on this? – Tim Jan 07 '20 at 11:13
  • Please check this coredata series: shorturl.at/ntwJT – Yogesh Patel Jan 07 '20 at 11:34

1 Answers1

0

Don't use a custom class. Use only the provided User class.

First of all declare a data source array (replacing displayDatasssss)

var users = [User]()

In the tap method load the data and insert new items in the Core Data stack. Consider that each tap on the button inserts duplicate items into the database. Older entries are not removed.

As User has only name and id properties email is assigned to id.

The items are appended to the data source array and saved in the context.

@IBAction func tap(_ sender: Any) {
    let url = "http://jsonplaceholder.typicode.com/users")!
    let task = session.dataTask(with: url){ [unowned self] (data, response,error)in
        if let error = error { print(error); return }
        do {
            // Array of Data
            let fetchData = try JSONSerialization.jsonObject(with: data!) as! [[String:Any]]    
            for eachDataItem in fetchData {                   
                let name = eachdataitem["name"] as! String
                let email = eachdataitem["email"] as! String
                let newUser = User(context: self.context)
                newUser.name = name
                newUser.id = email
                self.users.append(newUser)
            }
            DispatchQueue.main.async {
               self.tableView.reloadData()
            }
            try self.context.save()
       } catch{
           print("Error 2", error)
       }
    }
    task.resume()
}

In viewDidLoad fetch the data from CoreData and reload the table view

override func viewDidLoad() {
    super.viewDidLoad()
    do {
       let request : NSFetchRequest<User> = User.fetchRequest()
       users = try context.fetch(request)
       tableView.reloadData()
    } catch { print(error) }
}

In cellForRow assign the property value(s) to the labels, nothing else

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell1") as! TableViewCell1 
    let user = users[indexPath.row]
    cell.label.text = user.name  
    return cell
}

The delete method is quite similar to yours

let deleteAction = UITableViewRowAction(style: .default, title: "Delete", handler: { [unowned self] (action, indexPath) in
    print("Delete tapped")

    // remove the deleted item from the model
    let objectToDelete = self.users.remove(at: indexPath.row)
    self.context.delete(objectToDelete)
    do {
        try self.context.save()
        self.tableView.deleteRows(at: [indexPath], with: .automatic)
    } catch {
       print(error)
    }
}
return [editAction, deleteAction]

Note: Print always errors, don't ignore them or print only meaningless literal strings

vadian
  • 274,689
  • 30
  • 353
  • 361
  • your a great master of programmer. Awesome. I would like to learn from you lot. :) – Tim Jan 07 '20 at 11:20
  • instead of `JSONSerialization` if I use `JSONDecoder` then how to append the values `let result = try JSONDecoder().decode(User.self, from:data) let status = result.status if status == true { let item = result.data let newUser = User(context: self.context) newUser.id = ?? newUser.milestone = ??` – Tim Jan 07 '20 at 12:24
  • To decode the JSON directly into `User` you have to adopt `Decodable` to the class. That's not trivial and another question. And there is no key `status` in the JSON anyway. The root object is the user array. – vadian Jan 07 '20 at 12:38
  • I am bit confused. Everything I am done. But `let result = try JSONDecoder().decode(User, from:data)` if I do it is showing error. How to update this `let result = try JSONDecoder().decode(User.self, from:data) let status = result.status if status == true { let item = result.data let newUser = User(context: self.context) newUser.id = newUser.name = self.users.append(newUser)` – Tim Jan 07 '20 at 12:53
  • This code cannot work for 3 reasons: #1) `User` does not conform to `Decodable`, #2) if it did the object to decode is actually `[User].self` and #3) once again there is no key `status` in the JSON at given URL – vadian Jan 07 '20 at 12:56
  • `status` okey I will remove it. But what I need to do if I need to use Decodable. I have structure with JSON properties. how to achieve this. please provide some code hint – Tim Jan 07 '20 at 13:06
  • Please, this is beyond this question *how to delete...* Please read https://stackoverflow.com/questions/44450114/how-to-use-swift-4-codable-in-core-data to make `User` conform to `(De)Codable` – vadian Jan 07 '20 at 13:07