Swift 5 Coredata Predicate Using Uuid

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 between UUID and NSUUID.

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:

  1. Create a new project (Single View App)
  2. Add the code below in the top of the AppDelegate.m class
  3. 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



Leave a reply



Submit