Fetch Request Result, Core Data & Swift 3 - core-data

Now that NSFetchResultsController and NSFetchRequest are Generics in Swift 3, there have been a few changes in the initialization. I used the migrator (which was great!) and everything compiles fine. But the app crashes when I try and retrieve attributes the Swift 2 way.
I have thoroughly researched this problem.
There are few examples of how to initialize NSFetchResultsController and NSFetchRequest, but the various responses on StackOverflow are competing or currently inadequate when explaining the retrieval. Apple's documentation, too, is clear but not working.
Here is my code where Person is the Entity:
// MARK: - Initialize Fetch Results
var fetchedResultsController = NSFetchedResultsController<Person>()
func setFetchRequest() -> NSFetchRequest<Person> {
do {
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = Giver.fetchRequest()
let fetchResult = try moc?.fetch(fetchRequest)
// CANNOT FIGURE OUT WHAT GOES HERE e.g.
// person.name = fetchResult.first (this does not work)
// person.name = fetchResults (this does not work)
let sortDescriptor = SortDescriptor(key: "names", ascending: false)
fetchRequest.sortDescriptors = [sortDescriptor]
} catch {
print("Error with request: \(error)")
}
return setFetchRequest()
}
// MARK: - Retrieve Fetch Request
func getFetchRequest() -> NSFetchedResultsController<Giver> {
fetchedResultsController = NSFetchedResultsController(fetchRequest: setFetchRequest(), managedObjectContext: moc!, sectionNameKeyPath: nil, cacheName: nil)
return fetchedResultsController
}
At the moment, there is no error, save the yellow triangle indicating that "fetchResult" has not been used. I am trying to fetch a few attributes: name: String, age: Int, photo: BinaryData. How would I retrieve these attributes?
I realize Swift 3 is still in beta. Just frustrated experiencing the pangs of source-breaking changes.

Related

Update textfields to CoreData without destroying unchanged data

When I change any textfield on my update form with new information ONLY that change is saved. All other previously saved data for the entity is lost in the process.
func updateDog (
id: String,
dogName: String,
activeStatus: Int16,
breed: String,
//etc (about 30 attributes below here )
)
{
let fetchDog: NSFetchRequest<Dog> = Dog.fetchRequest()
fetchDog.predicate = NSPredicate(format: "id == %#", id)
do {
let dog = try self.moc.fetch(fetchDog).first
dog?.dogName = dogName
dog?.activeStatus = activeStatus
dog?.breed = breed
//etc
if self.fetchDog(dogName: dogName)?.hasChanges ?? true {
try self.moc.save()
}
} catch
let error as NSError {
print(error)
}
There are no errors messages, but any attributes already stored, which are left unchanged during the updating are lost in the self.moc.save process. Only the edited fields are updated.
I want it to ignore any attribute that has not been altered and simply update the changed data. I've been going around in circles but cannot achieve the correct behaviour. I am not very experienced as have only been learning to code for a few months, and my advancing years probably add to my struggles to grasp the problem I'm having. Any help is gratefully recieved

coreData returning only one result

Having this weird issue, No matter what predicate I apply its only returning one result, and if I don't add any predicate its returns all the rows of the table, what I want is only return the rows with matching progIds
I have the list of programIds and based on that I want to search in TvInfo table.
.......
......
progId = [Int64(3211),Int64(16844)] // hardcored ids from tvInfo table
let tvInfoRequest: NSFetchRequest<TvInfo> = TvInfo.fetchRequest()
tvInfoRequest.predicate = NSPredicate(format: "progId == %i", argumentArray: progId)
tvInfoRequest.returnsDistinctResults = true
tvInfoRequest.resultType = .managedObjectResultType
var tvShows = [TvInfo]()
coreDataStack.managedObjectContext.performAndWait({
do{
let tvInfoResult = try self.coreDataStack.managedObjectContext.fetch(tvInfoRequest)
tvShows = tvInfoResult
} catch {
fatalError("Error fetching channels displayShow")
}
})
I thought maybe some issue with progId (type mismatch Int64), so I tried fetching using the tvShow name (with exact showNames from tvInfo table still getting only one result, (first name passed in the array)
tvInfoRequest.predicate = NSPredicate(format: "progName MATCHES %#", argumentArray: ["Supercar Superbuild","Kanahiya Bhaiya Live"])
weird thing is I used the same logic to fetch progIds from channels Table and its working fine(getting progId with exact matches) and its working for other tables also like channel Id fetching from channel tables
// fetching progIds from channel table
let tvScheduleRequest: NSFetchRequest<TvSchedule> = TvSchedule.fetchRequest()
tvScheduleRequest.predicate = NSPredicate(format: "chId == %i", argumentArray: channelIds)
tvScheduleRequest.returnsDistinctResults = true
var progId = [Int64]()
coreDataStack.managedObjectContext.performAndWait({
do{
let tvScheduleResult = try self.coreDataStack.managedObjectContext.fetch(tvScheduleRequest)
if tvScheduleResult.count > 0 {
for array in tvScheduleResult {
progId.append(array.progId)
}
}
} catch {
fatalError("Error fetching channels to display show")
}
})
PS:
Its returning 1 row so it's not an issue of context, wrong key,
thread
there are data in table as if I don't apply any predicate its
returning 100+ rows
I am using argumentArray for predicate, not single args CVarArg.
Thanks for help
In predicate try using %# instead of %i.
tvInfoRequest.predicate = NSPredicate(format: "progId == %i", argumentArray: progId).
One more thing check the variable that you are passing to predicate progId.

Need help filtering core data in Swift 3

I can't seem to find any good documentation or tutorials that pertain to filtering core data in Swift 3.
I have the following code:
let tempVar: String = "abcdef"
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
do {
/* Need code to modify the fetchRequest, so that it only pulls results where the "projectID" field is equal to whatever is stored in the tempVar variable. */
cstProjectDetails = try context.fetch(CSTProjectDetails.fetchRequest())
} catch {
print("There was an error fetching CST Project Details.")
}
Basically, I am just trying to do add a simple filter to the fetchRequest, so that it only pulls results from "cstProjectDetails" where the "projectID" field is equal to the contents of a previously set variable (in this case, "tempVar").
Any ideas?
Edit: The checked answer did the trick for me, but I had to make a quick adjustment to the code for the request initializer. Here is the code that I ended up with:
do {
let request: NSFetchRequest<CSTProjectDetails> = CSTProjectDetails.fetchRequest()
request.predicate = NSPredicate(format: "projectID == %#", cstProjectID)
cstProjectDetails = try context.fetch(request)
print(cstProjectDetails)
} catch {
print("There was an error fetching CST Project Details.")
}
You need a predicate attached to the request:
let request = CSTProjectDetails.fetchRequest()
request.predicate = NSPredicate(format: "projectID == %#", tempVar)
cstProjectDetails = try context.fetch(request)

Buggy and Slow scrolling when loading TableView images from CoreData

Problem :
The process of loading images to Table View using their Paths that who stored in Core Data DB works fine , but the user experience not going well. The scroll is slow and buggy.
Importants Notes :
For my local DB i use Core Data
Im not saving the image it self in the Core Data , only their path(the image name)
As for the table view DataSource , i use an array of type Person that contains an ID and Img name(TableView rows equal array.Count).
-This is the url im getting my Json from (Check it out) - Json Link
All the object inside the Core Data DB
As far as i know , i did all the UI Updates in the Main theard
After each check im reloading the tableview.
This are the steps that being taking in a right sequence :
Get the data using NSURLSession - DataTask. After that "parsing" it and check if each object(In a for loop) exists in the Core Data DB,and than appending his variables to the TableView datasource array , and reloading the data
1)
let request = NSMutableURLRequest(URL: dataSourceURL!)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
println("error=\(error)")
return
}
if data != nil {
let datasourceDictionary = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil) as NSDictionary
var DataArray = datasourceDictionary["descisions"] as NSArray
//Convert it to useable array
for var i = 0 ; i < DataArray.count ; i++ {
let ID_s = DataArray[i]["ID"]! as Int
//For each Object from the Json array i'm check if he is exisitng in the local DB
var ConvertetdID = Int32(ID_s)
let object : Lockdown? = self.CheckIfObjectExistInDBbyID(ConvertetdID)
//CheckIfExists - if it does , it return an object with the correct values
if object != nil {
//exists - load file from Core Data
let imgname = object!.imgPath
let photoRecord = PhotoRecord(name:"\(ConvertetdID)", url:imgname)
self.photos.append(photoRecord)
//TableView object array (photos)
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
//After each check reload the tableView
}
}
}
}
task.resume()
Method that checks if he is exists or not in Core Data DB(the method receive the ID and returns object if exists and nil if not :
func CheckIfObjectExistInDBbyID(id : Int32) -> Lockdown? {
let appDelegate =
UIApplication.sharedApplication().delegate as AppDelegate
let managedContext = appDelegate.managedObjectContext!
var request : NSFetchRequest = NSFetchRequest(entityName: "Lockdown")
request.predicate = NSPredicate(format: "id = %d", id)
var error : NSError?
request.fetchLimit = 1
var count : Int = managedContext.countForFetchRequest(request,error: &error)
if count == 0 {
// println("Error : \(error?.localizedDescription)")
println("Object \(id) Dosent exist in CoreData")
return nil
}
println("Object \(id) exist in CoreData")
let result = managedContext.executeFetchRequest(request, error: &error) as NSArray!
let lockdown = result.objectAtIndex(0) as Lockdown
println(lockdown.id)
return lockdown
}
cellForRowAtIndexPath method
let cell = tableView.dequeueReusableCellWithIdentifier("CellIdentifier", forIndexPath: indexPath) as UITableViewCelllet photoDetails = photos[indexPath.row]
cell.textLabel.text = photoDetails.name as String
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
var myPathList : NSArray = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomainMask.UserDomainMask, true)
var myPath = myPathList[0] as String
myPath = myPath.stringByAppendingPathComponent("\(photoDetails.name).png")
var image : UIImage = UIImage(contentsOfFile: myPath)!
dispatch_async(dispatch_get_main_queue()) {
cell.imageView.image = image
}
}
return cell
Any suggestions what is the cause of the problem?
You shouldn't always load the image from disk, it's slow. Instead, load the image from disk the first time and then store it in a cache (in memory). Check if the image is cached before loading it from disk.
You need to empty the cache if it gets too big or you get a memory warning.
Advanced versions can pre-cache some images for the cells that are just below the current scroll position.
You may be able to use a library for this, perhaps SDWebImage with file URLs (I haven't tried this).
The buggy part is because your async block doesn't check that the cell hasn't been reused before the image is loaded. If it has then the wrong image will appear on the reused cell (until it's replaced by another image). To fix, capture the indexPath in the block and get the cell for that indexPath once the image is loaded - if the table view returns nil then the cell is no longer visible and you don't need to do anything other than cache the image for future use.

XCode 6 Beta 6 Error in Beta 7 - Value of optional type not unwrapped

I've been trying to do a simple CoreData task, saving data. I'm sure it works in Beta 6, but errors starting appearing after updating to Beta 7.
I think I have to add '?' or '!' based on the error hint, but just not smart enough to figure out where!
#IBAction func saveItem(sender: AnyObject) {
// Reference to App Delegate
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
// Reference our moc (managed object content)
let contxt: NSManagedObjectContext = appDel.managedObjectContext!
let ent = NSEntityDescription.entityForName("List", inManagedObjectContext: contxt)
// Create instance of our data model and initialize
var newItem = Model(entity: ent, insertIntoManagedObjectContext: contxt)
// Map our attributes
newItem.item = textFieldItem.text
newItem.quanitity = textFieldQuantity.text
newItem.info = textFieldInfo.text
// Save context
contxt.save(nil)
}
The error says
Value of optional type 'NSEntityDescription?' not unwrapped; did you mean to use '!' or '?'
At the line
var newItem = Model(entity: ent, insertIntoManagedObjectContext: contxt)
Everytime I seem to have clear the error and compiles ok, clicking the 'Save' shows in the debug area
fatal error: unexpectedly found nil while unwrapping an Optional value
The error is fairly trivial, there's not much to analyze here. Try changing this:
let ent = NSEntityDescription.entityForName("List", inManagedObjectContext: context)
to this
let ent = NSEntityDescription.entityForName("List", inManagedObjectContext: context)!
As always, novices tend to overlook tell-tale signs. The error clearly states that the optional is of type NSEntityDescription. And given that there is only object of this type being instantiated in the given code, it doesn't take a genius to guess where the error lies.
Value of optional type 'NSEntityDescription?' not unwrapped; did you mean to use '!' or '?'
Also, method used here to instantiate the NSEntityDescription object is declared as follows:
class func entityForName(entityName: String, inManagedObjectContext context: NSManagedObjectContext) -> NSEntityDescription?
... the ? character clearly telling us that this method returns an optional.
I presume that the Model initializer signature is:
init(entity: NSEntityDescription, insertIntoManagedObjectContext: NSManagedObjectContext)
the compilation error happens because NSEntityDescription.entityForName returns an optional, so you have to unwrap it.
As for the runtime error, my guess is that contxt is nil, and you are passing a forced unwrapped here:
let ent = NSEntityDescription.entityForName("List", inManagedObjectContext: contxt)
To made code safer and more clear, I'd explicitly use optionals:
let contxt: NSManagedObjectContext? = appDel.managedObjectContext
if let contxt = contxt {
let ent: NSEntityDescription? = NSEntityDescription.entityForName("List", inManagedObjectContext: contxt)
// Create instance of our data model and initialize
if let ent = ent {
var newItem = Model(entity: ent, insertIntoManagedObjectContext: contxt)
}
}
and use debugger & breakpoints to check if any of the mentioned variable is nil.

Resources