NSTableView animation bug while using NSTableViewDiffableDataSource - swift

I am using NSTableViewDiffableDataSource. When I do a multiple delete, I see the wrong insert-animation after (flicker and moving from top or bottom). How fix it?
// I am using UUID instead of NSManagedObjectID because when I create a new NSManagedObject it first has a temporary objectID.
func configureDataSource() {
let dataSource : NSTableViewDiffableDataSource<String, UUID> = .init(tableView: tableView) { table, column, index, objectID in
let request = NSFetchRequest<Task>()
request.entity = Task.entity()
request.predicate = NSPredicate(format: "id = %#", argumentArray: [objectID])
guard let task = try? self.viewContext.fetch(request).first as? Task else {
return NSView()
}
let cell = self.create(viewFor: column, task: task)
return cell
}
dataSource.defaultRowAnimation = .effectGap
dataSource.sectionHeaderViewProvider = nil
self.dataSource = dataSource
}
func storeDidReloadContent() {
var snapshot = NSDiffableDataSourceSnapshot<String, UUID>()
snapshot.appendSections([""])
snapshot.appendItems(store.objects.compactMap{ $0.id }, toSection: "")
dataSource.apply(snapshot, animatingDifferences: false)
}
func storeDidChangeContent(with snapshot: NSDiffableDataSourceSnapshotReference) {
var newSnapshot = NSDiffableDataSourceSnapshot<String, UUID>()
newSnapshot.appendSections([""])
newSnapshot.appendItems(store.objects.compactMap{ $0.id }, toSection: "")
dataSource.apply(newSnapshot, animatingDifferences: true)
}
// class ObjectFactory
// Batch operation
public func delete(objects: [T]) {
let objectIDs = objects.compactMap{ $0.objectID }
CoreDataStorage.shared.performBackground { privateContext in
objectIDs.forEach{
let object = privateContext.object(with: $0)
privateContext.delete(object)
}
try? privateContext.save()
}
}
PS: Store class (var store) incapsulate all works with NSFetchedResultController.
ObjectFactory class incapsulate all works with NSManagedObjects.
NSFetchedResultController works with only main NSManagedObjectContext.
Batch operation in NSTableView is pain :(

Related

save string over saved string in core data

In my swift code below the code saves an item in core data. The goal is to overwrite that item. I am getting a runtime error at
CoreDataHandler.changeName(user: fetchUser!\[indexNumber\], jessica: "jo")
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
I don't know how to wrap in the index number. The goal is it to print judo then jo
import UIKit;import CoreData
class ViewController: UIViewController {
var fetchUser: [UserInfo]? = nil
var indexNumber : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
CoreDataHandler.saveObject2( name: "judo")
getText(textNo: indexNumber)
saveTheItem()
}
#objc func saveTheItem(){
CoreDataHandler.changeName(user: fetchUser![indexNumber], jessica: "jo")
}
func getText(textNo:Int) {
// first check the array bounds
let info = helpText.shareInstance.fetchText()
if info.count > textNo {
if let imageData = info[textNo].name
{
print(imageData)
} else {
// no data
print("data is empty Textss")
}
} else {
// image number is greater than array bounds
print("you are asking out of bounds")
}
}
}
class CoreDataHandler : NSManagedObject {
class func saveObject2( name: String) -> Bool {
let context = getContext()
let entity = NSEntityDescription.entity(forEntityName: "UserInfo", in: context)
let managedObject = NSManagedObject(entity: entity!, insertInto: context)
managedObject.setValue(name, forKey: "name")
do{
try context.save()
return true
}
catch {
return false
}
}
private class func getContext() -> NSManagedObjectContext{
let appD = UIApplication.shared.delegate as! AppDelegate
return appD.persistentContainer.viewContext
}
class func changeName(user: UserInfo,jessica : String) -> Bool
{
let context = getContext()
user.name = jessica
print(jessica)
do{
try context.save()
return true
}
catch{
return false
}
}
}
class helpText: UIViewController{
private class func getContext() -> NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
static let shareInstance = helpText()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
func saveName(data: String) {
let imageInstance = UserInfo(context: context)
imageInstance.name = data
do {
try context.save()
} catch {
print(error.localizedDescription)
}
}
func fetchText() -> [UserInfo] {
var fetchingImage = [UserInfo]()
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "UserInfo")
do {
fetchingImage = try context.fetch(fetchRequest) as! [UserInfo]
} catch {
print("Error while fetching the image")
}
return fetchingImage
}
}
No offense but your code is a mess.
And there is a big misunderstanding. Core Data records are unordered, there is no index. To update a record you have to fetch it by a known attribute, in your example by name, update it and save it back.
This is a simple method to do that. It searches for a record with the given name. If there is one, update the attribute with newName and save the record.
The code assumes that there is a NSManagedObject subclass UserInfo with implemented class method fetchRequest.
func changeName(_ name: String, to newName: String) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request : NSFetchRequest<UserInfo> = UserInfo.fetchRequest()
request.predicate = NSPredicate(format: "name == %#", name)
do {
let records = try context.fetch(request)
guard let foundRecord = records.first else { return }
foundRecord.name = newName
try context.save()
} catch {
print(error)
}
}
Regarding your confusing code:
Create CoreDataHandler as singleton (and it must not be a subclass of NSManagedObject). Move the Core Data related code from AppDelegate and the methods to read and write in this class.

Sorting a Realm Results object in the background thread

I am trying to sort a Realm Results instance in a background thread. But I am getting 'Realm accessed from incorrect thread.' exception. What am I doing wrong here?.
I'm using this function to filter and update the table with the result as the text in the search bar text field changes.
Thanks in advance.
var previousSearchWork?
func getInvoicesFor(searchedTerm: String, completion: #escaping ([Invoice]) -> Void) {
previousSearchWork?.cancel()
let newSearchWork = DispatchWorkItem {
guard let realm = try? Realm() else { return }
var filteredInvoices = [Invoice]()
if searchedTerm.first!.isLetter { // searching by customer name
let predicate = NSPredicate(format: "name BEGINSWITH[cd] %# || name CONTAINS[cd] %#", searchedTerm, searchedTerm)
let invoices = realm.objects(Invoice.self).filter(predicate)
filteredInvoices = invoices.sorted {
$0.name!.levenshteinDistance(searchedTerm) < $1.name!.levenshteinDistance(searchedTerm)
}
} else { // searching by id
// ...
}
completion(filteredInvoices)
}
previousSearchWork = newSearchWork
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + .milliseconds(30), execute: newSearchWork)
}
As #Jay has mentioned in a reply to the original question:
... that Realm is on a background thread so the objects are on that thread; what happens with [Invoice] upon completion?
Yep, it turns out I've been fetching Realm persisted objects on a background thread and send it to the caller via completion closure and then the caller tries to read them on main thread. That's what triggered the 'Realm accessed from incorrect thread'
First of all, I couldn't find a way to sort the objects without transforming it to an array of realm objects since I needed to use a custom sorting method.
All I did to fix the above function was instead of returning an array of Objects that are fetched inside a background thread, I am returning references to those objects so I can refer to them in main thread
According to my poor research, I've found two ways to pass those objects from background thread to main thread. (I went for the second way cause as to what've read, it's faster for this case.)
let backgroundQueue = DispatchQueue.global()
let mainThread = DispatchQueue.main
// Passing as ThreadSafeReferences to objects
backgroundQueue.async {
let bgRealm = try! Realm()
let myObjects = bgRealm.objects(MyObject.self)
// ......
let myObjectsArray = .....
let references: [ThreadSafeReference<MyObject>] = myObjectsArray.map { ThreadSafeReference(to: $0) }
mainThread.async {
let mainRealm = try! Realm()
let myObjectsArray: [MyObject?] = references.map { mainRealm.resolve($0) }
}
}
// Passing primaryKeys of objects
backgroundQueue.async {
let bgRealm = try! Realm()
let myObjects = bgRealm.objects(MyObject.self)
// ......
let myObjectsArray = .....
// MyObject has a property called 'id' which is the primary key
let keys: [String] = itemsArray.map { $0.id }
mainThread.async {
let mainRealm = try! Realm()
let myObjectsArray: [MyObject?] = keys.map { mainRealm.object(ofType: MyObject.self, forPrimaryKey: $0) }
}
}
After adjusting the function (and completing it for my need):
var previousSearchWork: DispatchWorkItem?
func getInvoicesFor(searchedTerm: String, completion: #escaping ([String]) -> Void) {
previousSearchWork?.cancel()
let newSearchWork = DispatchWorkItem {
autoreleasepool {
var filteredIDs = [String]()
guard let realm = try? Realm() else { return }
let allInvoices = realm.objects(Invoice.self).filter(NSPredicate(format: "dateDeleted == nil"))
if searchedTerm.first!.isLetter {
let predicate = NSPredicate(format: "name BEGINSWITH[cd] %# || name CONTAINS[cd] %#", searchedTerm, searchedTerm)
let invoices = allInvoices.filter(predicate)
filteredIDs = invoices.sorted {
$0.name!.levenshtein(searchedTerm) < $1.name!.levenshtein(searchedTerm)
}.map {$0.id}
} else {
var predicates = [NSPredicate(format: "%# IN ticket.pattern.sequences", searchedTerm)]
if searchedTerm.count > 3 {
let regex = searchedTerm.charactersSorted().reduce("*") {$0 + "\($1)*"}
let predicate = NSPredicate(format: "ticket.pattern.id LIKE %#", regex)
predicates.append(predicate)
}
let invoices = allInvoices.filter(NSCompoundPredicate(orPredicateWithSubpredicates: predicates)).sorted(byKeyPath: "dateCreated", ascending: false)
filteredIDs = Array(invoices.map {$0.id})
}
DispatchQueue.main.async {
completion(filteredIDs)
}
}
}
previousSearchWork = newSearchWork
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + .milliseconds(30), execute: newSearchWork)
}

Use core data index to fetch a specific item from core data

My swift code below when loaded places 3 items in the core data entity named "UserName". When the user enters a number into textfield enterT I want the label labelName to display it. So when the user enters 1 the label should display jessica biel because Jesical Biel is the first name entered. Someone stated the suggestion below to solve this problem. I dont know exactly how to do this.I have added a gif below.
Convert the entered number to Int. If this succeeds pass the integer to joke and fetch the record matching the idx attribute.
https://github.com/redrock34/index-fetch
import UIKit
import CoreData
class ViewController: UIViewController,UITextFieldDelegate {
#IBOutlet var labelName : UILabel!
#IBOutlet var enterT : UITextField!
lazy var context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
override func viewDidLoad() {
super.viewDidLoad()
openDatabse()
fetchData()
enterT.delegate = self
}
func textFieldDidEndEditing(_ textField: UITextField) {
guard let index = Int(textField.text!) else {
// display an alert about invalid text
return
}
joke(at: index - 1)
}
func joke(at index : Int) {
let fetchRequest = NSFetchRequest<Users>(entityName: "Users")
fetchRequest.predicate = NSPredicate(format: "idx == %d", Int32(index))
do {
if let user = try context.fetch(fetchRequest).first {
labelName.text = user.username
}
} catch {
print("Could not fetch \(error) ")
}
}
func openDatabse()
{
let names = ["kim kardashian", "jessica biel", "Hailey Rienhart"]
for i in 0..<names.count {
let newUser = Users(context: context)
newUser.username = names[i]
newUser.idx = Int32(i + 1)
}
print("Storing Data..")
do {
try context.save()
} catch {
print("Storing data Failed", error)
}
}
func fetchData()
{
print("Fetching Data..")
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Users")
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
for data in result as! [NSManagedObject] {
let userName = data.value(forKey: "username") as! String
print("User Name is : "+userName)
}
} catch {
print("Fetching data Failed")
}
}}
Of course you have to assign values to the idx attribute and you have to assign the result of the fetch to the label.
First replace
let appDelegate = UIApplication.shared.delegate as! AppDelegate //Singlton instanc
var context:NSManagedObjectContext!
with
lazy var context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
Then replace both openDatabse and saveData with
func openDatabse()
{
let names = ["kim kardashian", "jessica biel", "Hailey Rienhart"]
for i in 0..<names.count {
let newUser = Users(context: context)
newUser.name = names[i]
newUser.idx = Int32(i + 1)
}
print("Storing Data..")
do {
try context.save()
} catch {
print("Storing data Failed", error)
}
}
Finally add a line in joke to display the value
func joke(at index : Int) {
let fetchRequest = NSFetchRequest<Users>(entityName: "Users")
fetchRequest.predicate = NSPredicate(format: "idx == %d", Int32(index))
do {
if let user = try context.fetch(fetchRequest).first {
labelName.text = user.username
}
} catch {
print("Could not fetch \(error) ")
}
}
It creates the records and assigns the proper indexes. Then entering a number in the text field should work.
But – once again – on each launch of the app the 3 records are inserted again with the same names and indexes. Be aware of that!

Facing issues while integrating core-data in chat application

We are facing issues while integrating coredata with our chat application. Please help us to resolve the issue. We tried to figure out each issue individually but sometimes it gets fixed and then shows up randomly. We are tring to fix it from last 1 week.
Our setup stack
We are using sockets library to for real time chatting. To persist the data we are using core-data. Our application is supporting iOS 8 and above so we can't use PersistenceContainer so to workoround this we are using BNRCoreDataStack [url: https://github.com/bignerdranch/CoreDataStack] which is similiar to what PersistenceContainer does.
Also to display chat we are using IGListKit and we have created viewModels to avoid sending mutable coredata objects to IGLIstkit as IGListkit works fine with immutable model. Also we have used this setup to create our own viewModels [url: https://github.com/Instagram/IGListKit/blob/master/Guides/Working%20with%20Core%20Data.md]
issues we are facing
1] Constraint validation failure
2] FetchResult controller crash issue
crash-log:
2018-07-19 21:41:36.515153+0530 Toppr Doubts[62803:2359707] *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3698.54.4/UITableView.m:2012
2018-07-19 21:41:36.517093+0530 Toppr Doubts[62803:2359707] [error] error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (60) must be equal to the number of rows contained in that section before the update (50), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null)
CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (60) must be equal to the number of rows contained in that section before the update (50), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null)
3] Illegal attempt to establish a relationship 'lastMessage' between objects in different contexts
Below is our CoreDataModel, we are not using any abstract entity for Message as we came to know this could cause lot of performance issues.
Session Entity
We are creating object in fromJSON method and setup is same for rest of the entities. Also I am sharing LastMessage Entity to show the way we are integrating relationships, again same for others
public class Session: NSManagedObject {
class func fromJSON(_ json: JSON, moc: NSManagedObjectContext) -> Session? {
if let entityDescription = NSEntityDescription.entity(forEntityName: "Session", in: moc) {
// Object creation
let object = Session(entity: entityDescription, insertInto: moc)
object.internalIdentifier = json[kSessionIdKey].int64Value
if let date = json[kSessionStartedOnKey].dateTime as NSDate? {
object.startedOn = date
}
if let date = json[kSessionEndedOnKey].dateTime as NSDate? {
object.endedOn = date
}
object.statusType = SessionStatus(rawValue: json[kSessionStatusKey].stringValue).map { $0.rawValue } ?? SessionStatus.none.rawValue
object.stateType = SessionState(rawValue: json[kSessionStateKey].stringValue).map { $0.rawValue } ?? SessionState.none.rawValue
if let ratingDict = json[kSessionRatingKey].dictionaryObject {
if let rating = ratingDict["student"] as? Int {
object.rating = rating
}
}
object.subjectID = json[kSessionSubjectKey]["id"].intValue
// Subjects are already stored need to fetch and assign
let subjectFetchRequest: NSFetchRequest<Subject> = Subject.fetchRequest()
subjectFetchRequest.predicate = NSPredicate(format: "internalIdentifier == %d", Int64(json[kSessionSubjectKey]["id"].intValue))
do {
if let subject = try moc.fetch(subjectFetchRequest).first {
object.subject = subject
}
} catch let erroe as NSError {
Logger.log.error(error)
}
// Student Object initialisation
if json[kSessionStudentKey] != JSON.null {
if let student = Student.fromJSON(json[kSessionStudentKey], moc: moc) {
object.student = student
}
}
// Tutor Object initialisation
if json[kSessionTutorKey] != JSON.null {
if let tutor = Tutor.fromJSON(json[kSessionTutorKey], moc: moc) {
object.tutor = tutor
}
}
// LastMessage Object initialisation
if json[kSessionLastMessageKey] != JSON.null {
if let lastMessage = LastMessage.fromJSON(json[kSessionLastMessageKey], moc: moc) {
object.lastMessage = lastMessage
} else {
return nil
}
}
return object
}
return nil
}
}
LastMessage
public class LastMessage: NSManagedObject, Message {
class func fromJSON(_ json: JSON, moc: NSManagedObjectContext) -> LastMessage? {
if let entityDescription = NSEntityDescription.entity(forEntityName: "LastMessage", in: moc) {
let object = LastMessage(entity: entityDescription, insertInto: moc)
object.id = json[kMessageIdKey].intValue
object.body = json[kBodyKey].stringValue
object.type = MessageType(rawValue: json[kTypeKey].stringValue) ?? MessageType.none
object.doubtTag = json[kDoubtTagKey].stringValue
if json[kAttachmentKey] != JSON.null {
if let attachment = Attachment.fromJSON(json[kAttachmentKey], moc: moc) {
object.attachment = attachment
}
}
if let date = json[kSentOnKey].dateTime as NSDate? {
object.sentOn = date
}
if json[kSentByKey] != JSON.null {
if let sentBy = SentBy.fromJSON(json[kSentByKey], moc: moc) {
object.sentBy = sentBy
}
}
object.deliveryState = DeliveryState(rawValue: json[kDeliveryStateKey].stringValue) ?? DeliveryState.none
object.sessionId = json[kSessionIdKey].intValue
return object
}
return nil
}
}
Get User State
Helps us fetch data for subject and live chat.
static func getUserState(completion:#escaping (_ success: Bool) -> Void) {
SocketManager.sharedInstance.send(eventName: .userState) { (response) in
guard !response.isError() else { return completion(false) }
// Coredatastack
guard let coreDataStack = (UIApplication.shared.delegate as! AppDelegate).coreDataStack else { return }
let wmoc = coreDataStack.newChildContext()
// Save Subject and Live Sessions
let subjects = response.result["subjects"].arrayValue.flatMap({ Subject.fromJSON($0, moc: wmoc) })
let sessions = response.result["live_sessions"].arrayValue.flatMap({ Session.fromJSON($0, moc: wmoc) })
if sessions.isNotEmpty || subjects.isNotEmpty {
CoreDataStack.batchUpdate(moc: wmoc, completion: {
NotificationCenter.default.post(name: NSNotification.Name("didUpdateUserState"), object: nil)
completion(true)
})
}
completion(false)
}
}
Get Previous Session
Helps us fetch data for inactive chats. We are getting problem in this while storing sessions
static func getPreviousSessions(isLoadingMore: Bool, completion: #escaping (_ success: Bool,_ isMore: Bool)->Void) {
guard let coreDataStack = (UIApplication.shared.delegate as! AppDelegate).coreDataStack else { return }
let wmoc = coreDataStack.newChildContext()
var sessionID = 0
// TODO: - Need to implement last sessionID from CoreData
if isLoadingMore {
// Get Sessions with Status == closed order by sessionID asc
let archiveSession = Session.filterArchivedSessions(moc: wmoc)
let sortedArchiveSession = archiveSession?.sorted(by: { $0.0.id < $0.1.id })
// Get last sessionID
if let lastSessionID = sortedArchiveSession?.first?.id {
sessionID = lastSessionID
}
}
let request: [String: Any] = [ "last_session_id": sessionID ]
SocketManager.sharedInstance.send(request, eventName: .getPreviousSessions) { (response) in
if response.result.isEmpty {
completion(false, false)
} else {
let sessions = response.result["sessions"].arrayValue.flatMap({ Session.fromJSON($0, moc: wmoc) })
if sessions.isNotEmpty {
CoreDataStack.batchUpdate(moc: wmoc)
} else {
for session in sessions { wmoc.delete(session) }
}
if let isMore = response.result["is_more_server"].bool {
completion(true, isMore)
}
}
}
}
In the above image every session is suppose to have one last message and one subject. But as you can see, there are no last messages and some session have subjects as null
CoreDataStack+Extension
To Save data directly to Store
extension CoreDataStack {
// This method will add or update a CoreData's object.
static func batchUpdate(moc: NSManagedObjectContext? = nil, completion: (()-> Void)? = nil) {
guard let moc = moc, moc.hasChanges else { return }
if #available(iOS 10.0, *) {
moc.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
}
do {
try moc.performAndWaitOrThrow {
try moc.saveContextToStoreAndWait()
DispatchQueue.main.async {
completion?()
}
}
} catch {
print("Error creating initial data: \(error)")
}
}
}
NSFetchedResultsController Setup
lazy var archievedSessionFRC: NSFetchedResultsController<Session> = {
guard let coreDataStack = (UIApplication.shared.delegate as! AppDelegate).coreDataStack else { return NSFetchedResultsController() }
// Create Fetch Request
let fetchRequest: NSFetchRequest<Session> = Session.fetchRequest()
// Configure Fetch Request
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "internalIdentifier", ascending: false)]
fetchRequest.predicate = NSPredicate(format: "statusType = %#", "closed")
let archievedSessionFRC = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: coreDataStack.mainQueueContext,
sectionNameKeyPath: nil,
cacheName: nil)
archievedSessionFRC.delegate = self
return archievedSessionFRC
}()
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
do {
try archievedSessionFRC.performFetch()
if let sessions = archievedSessionFRC.fetchedObjects {
self.previousSessions = sessions
}
} catch {
let fetchError = error as NSError
print("\(fetchError), \(fetchError.localizedDescription)")
}
}
// MARK: - NSFetchedResultsControllerDelegate
extension HomeVC: NSFetchedResultsControllerDelegate {
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
self.tableView.beginUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
if let indexPath = newIndexPath {
self.tableView.insertRows(at: [indexPath], with: .automatic)
}
case .delete:
if let indexPath = indexPath {
self.tableView.deleteRows(at: [indexPath], with: .automatic)
}
case .move:
if let indexPath = indexPath , let newIndexPath = newIndexPath {
self.tableView.moveRow(at: indexPath, to: newIndexPath)
}
case .update:
if let indexPath = indexPath {
self.tableView.reloadRows(at: [indexPath], with: .automatic)
}
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
self.tableView.endUpdates()
}
}
Thanks in Advance

Fetch specific attributes from coredata swift

I am developing core data in my application.
I want to fetch name attribute from the core data.
class ViewController: UIViewController {
#IBOutlet weak var saveDataBtn:UIButton!
#IBOutlet weak var dataTxtField:UITextField!
#IBOutlet weak var dataLbl:UILabel!
var tasks: [Task] = []
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
#IBAction func saveDataBtnPressed(_sender : UIButton){
print("Save Data.")
let task = Task(context: context)
task.name = dataTxtField.text
(UIApplication.shared.delegate as! AppDelegate).saveContext()
getData()
}
func getData(){
do{
tasks = try context.fetch(Task.fetchRequest())
}catch{
print("Fetching Failed")
}
}
How can i get it?
Thanks,
In Swift 4, you can access the property directly.
do {
let tasks = try context.fetch(request)
for task in tasks {
print(task.name)
}
} catch let error {
print(error.localizedDescription)
}
UPDATED - How to delete and update an instance of an Entity.
Here are some ideas to organize the code to deal with the updating and deleting.
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
extension Task {
// to get an instance with specific name
class func instance(with name: String) -> Task? {
let request = Task.fetchRequest()
// create an NSPredicate to get the instance you want to make change
let predicate = NSPredicate(format: "name = %#", name)
request.predicate = predicate
do {
let tasks = try context.fetch(request)
return tasks.first
} catch let error {
print(error.localizedDescription)
return nil
}
}
// to update an instance with specific name
func updateName(with name: String) {
self.name = name
(UIApplication.shared.delegate as! AppDelegate).saveContext()
}
// to delete an instance
func delete() {
context.delete(self)
(UIApplication.shared.delegate as! AppDelegate).saveContext()
}
}
func howItWorks() {
guard let task = Task.instance(with: "a task's name") else { return }
task.updateName(with: "the new name")
task.delete()
}
In Swift 4.1 and 5. We will use NSPredicate to specify our required condition. NSFetchRequest has a property predicate will set our predicate here as follow.
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Users")
let predicate = NSPredicate(format: "username = %#", argumentArray: ["John"]) // Specify your condition here
// Or for integer value
// let predicate = NSPredicate(format: "age > %d", argumentArray: [10])
fetch.predicate = predicate
do {
let result = try context.fetch(fetch)
for data in result as! [NSManagedObject] {
print(data.value(forKey: "username") as! String)
print(data.value(forKey: "password") as! String)
print(data.value(forKey: "age") as! String)
}
} catch {
print("Failed")
}