Understanding error handling with CoreData - swift

I have a scenario where I expect something to happen, but it does not and I can't seem to figure out why. I am still new with throwing functions, so I am a bit curious here. Maybe someone can explain why this happen.
I have made this function:
func checkExistanceOfMovieAddition(id: Int) -> Result<SavedMovieAddition, CoreDataErrors> {
let request: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: SavedMovieAddition.entityName)
request.predicate = NSPredicate(format: "movieID == \(id)")
do {
let fetch = try context.fetch(request)
if let result = fetch.first as? SavedMovieAddition {
return.success(result)
} else {
return.failure(.additionNotFound)
}
} catch {
return.failure(.fetchFailed)
}
}
And it works like a charm. If I search for an id that does not exist I get an empty array and return my custom error type. However.. if I change the line:
request.predicate = NSPredicate(format: "movieID == \(id)")
and misspell "movieID" with "movieIDs" which does not exists in my CoreData model the app crashes with:
Thread 1: Exception: "keypath movieIDs not found in entity <NSSQLEntity SavedMovieAddition id=2>"
Here I would expect it to go the the catch block and return .fetchFailed?
Why is this not happening? But instead the app is crashing? - I thought the whole point of have a do-try-catch block was to eliminate these crashes?
Is there something I am missing?

You've had some comments with good explanations. One more thing that may be useful is that you can help to avoid the kind of error you describe by not using bare strings if you can avoid them. When possible, do something that will let the compiler check whether you've misspelled something. In this case, Swift keypaths would help.
You have
request.predicate = NSPredicate(format: "movieID == \(id)")
...which works but is a problem if you misspell the property. If you write this as
request.predicate = `NSPredicate(format: "\(#keyPath(SavedMovieAddition.movieID)) == \(id)")`
...the key path would be checked by the compiler. If you typed it as movieIDs, your code wouldn't compile and you'd get an error saying that Type 'SavedMovieAddition' has no member 'movieIDs'. You could also write it this way, if it makes more sense to you:
request.predicate = `NSPredicate(format: "%K == \(id)", #keyPath(SavedMovieAddition.movieID))`

Related

Can't populate empty dictionary in Swift

I am trying to build a flutter plugin for the Sendbird iOS SDK. For some reason whenever I try to fetch data, I get null from a NSDictionary<NSString *,NSObject *> when I know for a fact that it is not (I have created the keys and values separately).
Most likely it's the fact that being a beginner with Swift I am doing something wrong. This is my code:
let channelMetadata = NSMutableDictionary()
// metadata is the NSDictionary<NSString *,NSObject *>
channel.getAllMetaData{ (metaData, error) in
guard let metadata = metaData, error == nil else {
// Error.
return
}
channelMetadata["status"] = metadata["status"]
channelMetadata["type"] = metadata["type"]
}
js["status"] = channelMetadata["status"]
it's:
js["status"]
Which returns null. I assume this is because I am doing something wrong with the way I am populating my empty dictionary, but I don't know what, even after searching for hours. Any help would be really appreciated.

Filter to NSFetchRequest

hi i'm not really understanding how the fetch filter works can anyone help me please? So i currently have this as my fetchall function which displays all of my items within my entity
im having trouble of filtering only one attribute which is a boolean. I want it to only display attributes that are true.
thankyou!
func fetchAllItems(){
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "AllItems")
do{
let result = try managedObjectContext.fetch(request)
beastList = result as! [AllItems]
} catch { print("\(error)")
}
}
Code:
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "AllItems")
request.predicate = NSPredicate(format: "something = %#", argumentArray: [true])
Note: Replace something with your boolean field name
Better way to create request
let request : NSFetchRequest<AllItems> = AllItems.fetchRequest()
Naming convention:
It is better to name your entity in singular form, so that each row in your entity could be in singular form.
AllItems seems very generic, if you are storing cars, the entity name could be Car. The variable that stores the result of the fetch request could be cars.
Reference:
https://developer.apple.com/documentation/foundation/nspredicate
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Predicates/Articles/pSyntax.html

Better approach to querying sqlite database in swift

I'm working on a word game and have bundled a complete list of english words using a sqlite database. I'm trying to find the best way to search the database for a given string to determine if it's a word.
At this point I can get the whole database out into an array:
func fetchWords() {
if let managedObjectContext = (UIApplication.shared.delegate as? AppDelegate)?.managedObjectContext {
let wordsFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "EnglishWord")
do {
englishWords = try managedObjectContext.fetch(wordsFetch) as! [EnglishWord]
print(englishWords[123323].word)
//Prints "injustices"
} catch {
//error handling
}
}
}
Now. What I really want to do is pass in a given string and see if it exists as a word in my database. I have a clunky solution with predicates, e.g:
func fetchWordsToArray() {
if let managedObjectContext = (UIApplication.shared.delegate as? AppDelegate)?.managedObjectContext {
let wordsFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "EnglishWord")
let searchWord = ["dastardly"]
let searchPredicate = NSPredicate(format: "word = %#", argumentArray: searchWord)
do {
englishWords = try managedObjectContext.fetch(wordsFetch) as! [EnglishWord]
let resultFilteredByPredicate = (englishWords as NSArray).filtered(using: predicate)
print(resultFilteredByPredicate)
} catch {
//error handling
}
}
}
But in order to use the filtered function I have to convert to an NSArray which means I can't work with the results directly (e.g. get resultFilteredByPredicate.word).
Also it feels like I'm probably going about this all the wrong way. Since everything has to go into an array first, I must be losing a lot of the value of using an sqlite database in the first place.
Any suggested approaches for better working with the database?
Many thanks in advance for any help!
To make the database do the filtering (which could then be optimized automatically with an index), put a predicate on the original fetch request:
let formatRequest : NSFetchRequest<Word> = Word.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "word == %#", searchWord)
let fetchedResults = try context.fetch(fetchRequest) as! [Word]
But Core Data is a framework to manage your objects.
If you decide that your words are not objects but just values, you could replace Core Data with some other library that does SQL directly, and execute an SQL query instead:
SELECT * FROM EnglishWord WHERE word = ?

Fetch of Core-Data is resulting in duplicate items

In my iOS Xcode8 project using Swift, I'm performing a fetch of my Core-Data:
func searchFoods() {
let context: NSManagedObjectContext = appDel.managedObjectContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Foods")
print("Searching Database for \(searchVariable)...")
var subPredicates : [NSPredicate] = []
let codeSearch = NSPredicate(format: "codeText contains[c] %#", "\(searchVariable)")
subPredicates.append(codeSearch)
request.predicate = NSCompoundPredicate(orPredicateWithSubpredicates: subPredicates)
request.returnsObjectsAsFaults = false
do {
let results = try context.fetch(request)
if results.count > 0 {
for result in results as! [NSManagedObject] {
if let item = result.value(forKey: "title") as? String {
// Maybe put a loop of some kind to only append the found item count??
searchArray.append(item)
myTableView.reloadData()
}
}
}
} catch {
print("Fetch failed...")
}
}
However my searchArray that is a [String] of the search results created many duplicates I know are not there; it's listing them 2 or 3 times. Can't figure out how to limit the appending to just the result count amount. If I search fruit, it might return an array like bananas, strawberries, peaches, oranges, bananas, strawberries, peaches, oranges etc, repeating. Can someone please help?
There's nothing in the code that would cause duplicates in the fetch results. If they're not actually present in the persistent store, the likely cause is that you're doing this:
searchArray.append(item)
But there's no sign of ever clearing out past results. Your sample results are consistent with this-- if there are four results, you add them to the array once, then later add them again.
It's also possible that there's a problem with your table view data source methods, but you're probably driving that directly from the contents of searchArray.

Do - Catch error handling Swift 2.0

I have read through numerous posts regarding conversion between older versions of swift and swift 2.0 on the issue of Do-catch error handling. However each and every one of them seem different to my personal issue.
Besides solving my personal issue I'm fairly curious as to what the general idea is behind this concept, because I simply can not figure out how this works on a low level scale just by reading all these topics.
I'll post my personal issue below, but I'd also very much appreciate some sort of general explanation about how this do-catch method works.
if(urlResponse.statusCode == 200) {
self.tweets = NSJSONSerialization.JSONObjectWithData(responseData,
options: NSJSONReadingOptions.MutableContainers,
error: &jsonParseError) as? NSMutableArray
}
the error shows at the line:
error: &jsonParseError) as? NSMutableArray
Change your code to
if(urlResponse.statusCode == 200) {
do {
self.tweets = try NSJSONSerialization.JSONObjectWithData(responseData, options: NSJSONReadingOptions.MutableContainers) as? NSMutableArray
} catch let jsonParseError {
print("Parse error = \(jsonParseError)")
}
}
You can find more about error handling here.