Swift 5 CoreData predicate using UUID
- The
%K
format expects a key path string, this can be obtained using the#keyPath
directive. - The
%@
format expects an Objective-C object, here we can use the toll-free bridging betweenUUID
andNSUUID
.
That gives:
let predicate = NSPredicate(format: "%K == %@",
#keyPath(Bike.bleID), bleID as NSUUID)
Swift 5 NSFetchRequest predicate when trying to lookup a String UUID
You can replace your predicate with this:
guard let uuidQuery = UUID(uuidString: uuid) else { return [] } // no valid UUID with this code
request.predicate = NSPredicate(format: "%K == %@", #keyPath(ExistingUsers2.uuid), uuidQuery as CVarArg)
Everything else should work.
UPDATE
This is the code that finally worked, thanks for your help @André Henrique da Silva
func loadUser(uuid: String) -> [ExistingUsers2] {
let request : NSFetchRequest<ExistingUsers2> = ExistingUsers2.fetchRequest()
let uuidQuery = NSUUID(uuidString: uuid)
request.predicate = NSPredicate(format: "uuid == %@", uuidQuery! as CVarArg)
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
do {
existingUsersArray = try context.fetch(request)
} catch {
print("Error fetching data from context \(error)")
}
return existingUsersArray
}
Fetch Core Data using UUID
In my case it works with a following predicate:
func getItem(with id: UUID?) -> Item? {
guard let id = id else { return nil }
let request = Item.fetchRequest() as NSFetchRequest<Item>
request.predicate = NSPredicate(format: "%K == %@", "id", id as CVarArg)
guard let items = try? context.fetch(request) else { return nil }
return items.first
}
More importantly, I am using UUID
(the same case is in the answer suggested by Martin), not ObjectIdentifier
. It is definitely possible to fetch items based on UUID
.
Please try to change a parameter type in your function to UUID
Unreliable update with @FetchRequest predicate filtering on UUID typed attributes?
I believe the weird behaviour is due to your predicate, which is being evaluated in two different ways. When you first run the app, or after closing and restarting, the predicate is parsed and passed to SQLite (as a WHERE clause in a SELECT statement). Thereafter, when you add new items, the predicate is evaluated directly in memory (ie in the NSManagedObjectContext) - no need to involve SQLite.
In your predicate, you are comparing a UUID type attribute with a String value - which fails. Even if the string representation of the UUID attribute is the same as the string you compare it with, the context sees them as different and regards the new object as failing the predicate. Hence the view is not updated.
However, SQLite is much more tolerant of type mismatches. (I'm guessing CoreData's implementation of UUID attributes is to store the string representation - but that's just a guess). So when you quit and restart, the fetch is processed by SQLite which regards the new object as meeting the predicate and accordingly includes it in the results.
To get the correct behaviour, I think you need your predicate to compare the UUID attribute with the UUID which has the correct string representation:
NSPredicate(format: "number == %@ OR uuid == %@", NSNumber(5), UUID(uuidString: ItemList.filterUUID))
Swift Core Data Predicate
Checking for nil is done using "%K != nil" so the first part of the predicate would be
NSPredicate(format: "%K != nil", #keyPath(Sublist.origin_List))
(Replace Sublist
with the actual name of that entity)
The second part would use a similar dot notation as in swift
NSPredicate(format: "%K = %@",
argumentArray: [#keyPath(Sublist.origin_List.id), someId])
This can then be put together into a compound predicate
NSCompoundPredicate(andPredicateWithSubpredicates: [
NSPredicate(format: "%K != nil", #keyPath(Sublist.origin_List)),
NSPredicate(format: "%K = %@",
argumentArray: [#keyPath(Sublist.origin_List.id), someId])
])
That being said you should first try with only the second predicate since I suspect the nil check is redundant in SQL. So hopefully the answer is simply
NSPredicate(format: "%K = %@",
argumentArray: [#keyPath(Sublist.origin_List.id), someId])
URI typed attributes in CoreData: how to query with NSPredicate
I'm answering to not let that question without an answer, but in my opinion, we three @nylki (the author), @Joakim Danielson and myself answered it together. I'll mark it as "Community Wiki" then.
URI
in CoreData is a for URL
objects. It's written as such in the documentation of NSAttributeDescription.AttributeType
for the NSAttributeDescription.AttributeType.uri
.
LIKE
keyword in the predicate is for String comparison as stated by the Predicate Format String Syntax documentation, so we need to use =
instead.
So the answer is:
NSPredicate(format: "imageUrl = %@", imageUrl as CVarArg)
Or
NSPredicate(format: "imageUrl = %@", argumentArray: [imageUrl])
if we don't want to use the as CVarArg
.
A better way to avoid typo would be to use %K
placeholder which is for %K is a var arg substitution for a key path.
. Combined to #keyPath()
:
NSPredicate(format: "%K = %@", argumentArray: [#keyPath(YourEntityClass.imageUrl), imageUrl])
That way, if we wrote by error "imageUrI = %@"
with an uppercase i
instead of l
that with might not see, we ensure the path.
Storing UUIDs in Core Data
How many of these are you planning on storing? Storing them as binary data saves about 50% -- roughly 20 bytes as binary data vs. roughly 40 bytes as a string. So you're talking about saving a whole 20K per thousand UUID's, which isn't so much that I'd worry about it either way. However, if you really want to save them as binary data, you can do that by storing them as NSData objects.
How to get data by giving id(UUID) in the same CoreData model?
What you need is a relation between two entities.
Solution #1 - Core Data
You can create a relation between Todo
and Task
directly at the Core Data level. You didn't post enough details in your question to expand more on this solution, however, you can follow these answers:
- One-to-Many and Many-to-Many Core Data Relationships
- Core Data relationships (swift)
Then, you'd be able to fetch the Task
associated with a Todo
item and then you'd just retrieve necessary properties directly from the Task
object.
Solution #2 - Map
Another possible solution is to fetch all the Task
objects in the IntentHandler
and then map your Todo
objects to the fetched tasks.
You can try the following (just an example as the code is not testable):
class IntentHandler: INExtension, ConfigurationIntentHandling {
var moc = PersistenceController.shared.managedObjectContext
func provideNameOptionsCollection(for intent: ConfigurationIntent, searchTerm: String?, with completion: @escaping (INObjectCollection<NSString>?, Error?) -> Void) {
var nameIdentifiers: [NSString] = []
do {
// fetch todos
let todosRequest = NSFetchRequest<TodoEntity>(entityName: "TodoEntity")
let todos = try moc.fetch(todosRequest)
// fetch tasks
let tasksRequest = NSFetchRequest<TaskEntity>(entityName: "TaskEntity")
let tasks = try moc.fetch(tasksRequest)
let tasksDict = Dictionary(grouping: tasks, by: \.id)
// map `todos` to `tasks`
nameIdentifiers = todos
.compactMap { todo in
guard let id = todo.id,
let task = tasksDict[id]?.first?.task
else {
return nil
}
return NSString(string: task)
}
}
catch let error as NSError {
print("Could not fetch.\(error.userInfo)")
}
let allNameIdentifiers = INObjectCollection(items: nameIdentifiers)
completion(allNameIdentifiers, nil)
}
override func handler(for intent: INIntent) -> Any {
return self
}
}
Can I Match NSPredicate for non-NSString data type?
I think the following predicate should work:
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"uuid.UUIDString ==[c] %@ ",strUUID];
Here is a working version just for you. To prove it I performed the following steps:
- Create a new project (Single View App)
- Add the code below in the top of the AppDelegate.m class
- Call [TestObj testPredicateJustForYou]; in the didFinishLaunchingWithOptions: function.
The sample class:
@interface TestObj : NSObject
@property (strong, nonatomic, readonly) NSUUID *uuid;
@end
@implementation TestObj
- (NSUUID*) uuid{
return [[UIDevice currentDevice] identifierForVendor]; }
+ (void) testPredicateJustForYou{
int uuidMaxNumber = 10;
NSMutableArray* uuids = [NSMutableArray array];
for (int i = 0; i < uuidMaxNumber; i++) {
[uuids addObject: [TestObj new]];
}
NSPredicate* predicate = [NSPredicate predicateWithFormat:@"uuid.UUIDString ==[c] %@ ",[[UIDevice currentDevice] identifierForVendor].UUIDString];
NSArray *filterArray=[uuids filteredArrayUsingPredicate:predicate];
NSAssert(filterArray.count == uuidMaxNumber, @"The predicate should work...");
}
@end
Good luck!
Related Topics
"Message from Debugger: Unable to Attach" When Running Tests on Osx App
How to Parse Firestore Fieldvalue to Date in Swift
Generics and Functional Programming in Swift
Http Request Swift Providing Parameters
In Swift, Can One Use a String to Access a Struct Property
Swift Firebase Storage How to Retrieve Image with Unknow Name(Nsuuid)
Response Struct Does Not Like Codingkeys
Firebase Storage Overwriting Files
Apple Push Notifications Without Developer Account
How to Get Rid of Array Brackets While Printing
Swift - Kvo - Change.Newvalue and Change.Oldvalue Are Nil
How to Open to a Specific View Using Home Quick Actions
Perform Migration by Adding List() and Another Model Class
How to Fix Memory Leaks in iOS Applications
Swift Firestore Search for Users
How Marquee Text of Label on Swift
Swiftui [Bug] Navigationview and List Not Showing on iPad Simulator Only