Can I apply multiple predicates to an NSFetchRequest? Would it be better to manually parse my results? - iphone

Ok I have a basic iPad app that asks for 5 search/filter criteria from the user. Based on this data, I need to go to my core data db, and pull out any managed objects that fit that criteria. It seems like I need to apply more than one predicate to the same request, is that possible? Or could I just write a really long fancy predicate? With multiple requirements? How should I approach that?
Would it be a good idea to just grab all the entities through the fetch request, and then loop through each array and grab any objects that I find that fit my search criteria?
Please advise!

Yes it's possible. You're looking for compound predicates and here's an example with AND predicates:
NSPredicate *compoundPredicate
= [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray of Predicates]];
You can also use notPredicateWithSubpredicates and orPredicateWithSubpredicates depending on your needs.
Link to documentation https://developer.apple.com/documentation/foundation/nscompoundpredicate

Swift 4
let fetchRequest: NSFetchRequest<YourModelEntityName> = YourModelEntityName.fetchRequest()
let fooValue = "foo"
let barValue = "bar"
let firstAttributePredicate = NSPredicate(format: "firstAttribute = %#", fooValue as CVarArg)
let secondAttributePredicate = NSPredicate(format: "secondAttribute = %#", barValue as CVarArg)
fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [firstAttributePredicate, secondAttributePredicate])
more information about different types of NSCompoundPredicate constructors can be found here

yes you can apply like this below
do {
let request = UserDeatils.fetchRequest() as NSFetchRequest<UserDeatils>
let firstAttributePredicate = NSPredicate(format: "userName = %#", userNameTextField.text!)
let secondAttributePredicate = NSPredicate(format: "password = %#", passwordTF.text!)
request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [firstAttributePredicate, secondAttributePredicate])
self.userDataArray = try context.fetch(request)
print("Fetching Data", userDataArray)
if userDataArray.isEmpty {
print("no user found")
} else {
print("Login Succesfully")
}
} catch {
print("Wrong Username and password")
}

Related

Understanding error handling with CoreData

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))`

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.

SWIFT: do I have someone set a record with different text every time?

So I made a chatroom and when someone sends a message they also add a Subscription in my cloud kit database but the problem is there cant be more then one of the same name that is a subscription and I want them to be able to set more subscriptions then one. Here is some code:
func setupCloudKitSubscription () {
let userDefaults = NSUserDefaults.standardUserDefaults()
if userDefaults.boolForKey("subscribed") == false {
let predicate = NSPredicate(format: "TRUEPREDICATE", argumentArray: nil)
let subscription = CKSubscription(recordType: "Extra1", predicate: predicate, options: CKSubscriptionOptions.FiresOnRecordCreation)
let notificationInfo = CKNotificationInfo()
notificationInfo.alertLocalizationKey = "New Sweet"
notificationInfo.shouldBadge = true
subscription.notificationInfo = notificationInfo
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.saveSubscription(subscription) { (subscription:CKSubscription?, error:NSError?) -> Void in
if error != nil {
print(error?.localizedDescription)
}else{
userDefaults.setBool(true, forKey: "subscribed")
userDefaults.synchronize()
You see how it says recordType: "Extra1" how can I made the "Extra1" different text every time someone makes a subscription? Thanks!
Your question is not completely clear. I think what you wanted to ask is that you want the subscription to send you a different message with each notification.
You could set it to display one or more fields of the record. For doing that you should use something like this:
notificationInfo.alertLocalizationKey = "Response: %1$#"
notificationInfo.alertLocalizationArgs = ["responseField"]
Then you also need this in your Localization.Strings file.
"Response: %1$#" = "Response: %1$#";