Objective-C literals for NSSet and NSOrderedSet? - literals

What, if any, NSSet and NSOrderedSet operations can one perform with the new Objective-C collection literals?
For NSArray, NSDictionary, and NSNumber, see here.
FWIW, this Big Nerd Ranch Post says the indexing syntax is supposed to work for NSOrderedSet. But in my tests it does not. And I haven't been able to find anything about creating ordered sets.

There is no Objective-C literal syntax for NSSet. It doesn't really make sense to use index subscripting to access NSSet elements, although key-based subscripting might. Either by wrapping or subclassing, you could add your own.
(Correction):
Originally I had thought NSOrderedSet wasn't supported either, but it turns out that using index subscripting is supported, but there doesn't seem to be a way of initializing an NSOrderedSet with the literal syntax. However, you could use initWithArray: and pass a literal array:
NSMutableOrderedSet* oset = [[NSMutableOrderedSet alloc] initWithArray:
#[#"a", #"b", #"c", #"d", #42]];
oset[2] = #3;
NSLog(#"OrderedSet: %#", oset);
Outputs:
OrderedSet: {(
a,
b,
3,
d,
42
)}
According to this excellent post by Mike Ash, you can add the methods that are used to support indexed subscripting to your own objects.
- (id)objectAtIndexedSubscript:(NSUInteger)index;
- (void)setObject: (id)obj atIndexedSubscript: (NSUInteger)index;
And similar methods for object-based keying:
- (id)objectForKeyedSubscript: (id)key;
- (void)setObject: (id)obj forKeyedSubscript: (id)key;
Thus you could implement a wrapper (or with a bit more work, subclass it) around an NSSet and provide key-based retrieval. Cool!

What about trying this, in case of NSSet:
#define $(...) [NSSet setWithObjects:__VA_ARGS__, nil]
And using it like this:
NSSet *addresses = $(addr1,addr2,addr3,addr4);
;-)

Related

Does Ensembles support the new UUID entity attribute type?

In iOS 11, a new attribute type was introduced for Core Data Entities: UUID. I'd like to use that type for storing my uniqueIdentifiers for Ensembles. Is it safe to do so? Can Ensembles handle syncing the UUID attributes?
Here's how I plan to provide unique identifiers to the delegate method using my identifier properties:
- (NSArray *)persistentStoreEnsemble:(CDEPersistentStoreEnsemble *)ensemble
globalIdentifiersForManagedObjects:(NSArray *)objects
{
NSArray <UUID *> *uuidArray = [objects valueForKeyPath:#"uniqueIdentifier"];
return [uuidArray valueForKeyPath:#"UUIDString"];
}
I somehow missed this feature. I have nothing in Ensembles to handle this specially, so it may not work. I would expect the fix to be fairly straightforward if it doesn't work, so if you find it is not working, let me know, and I'll look into it.
The problem will probably be in the conversion to JSON. See JSONValueFromCoreDataValue and CoreDataValueFromJSONValue. You probably just need to add an extra branch in the if-else to handle UUID explicitly.

No Core Data entity subclasses during custom migrations

I have a core data database. I'm performing a custom migration. I have a subclass of NSEntityMigrationPolicy. My policy's migration method buildValueFromSectionFieldManagedObject: is being called by a mapping rule : FUNCTION($entityPolicy, "buildValueFromSectionFieldManagedObject:" , $source).
This part is actually working.
However, the implementation of buildValueFromSectionFieldManagedObject: uses methods in the custom entity NSManagedObject subclass of the $source, which is Choice.
The methods of Choice do not seem to be available to the migration function, and instead it gets just a vanilla NSManagedObject.
When I try to use Choice methods, I get an exception. If I po the choice in the debugger, I get something like this:
<NSManagedObject: 0x600000281860> (entity: Choice; id: 0xd00000000038001a ; data: )
Whereas, out of a migration I would usually see something like this:
<Choice: 0x60800028bdb0> (entity: Choice; id: 0x6080002225a0 ; data: {
Is this just how it is, or is there some way that I can use the entity objects during migration?
Possibly relevant – this particular entity, Choice, is removed during this migration. It does not exist in the target managed object model, but does exist in the source managed object model. However, I don't think this is the case as other entity classes that are in the target model are also unavailable as that class during migration – they have class NSManagedObject and their entity methods are not available.
That is correct, you only have access to basic NSManagedObjects during migration.
Three-Stage Migration
The migration process itself is in three stages. It uses a copy of the source and destination models in which the validation rules are disabled and the class of all entities is changed to NSManagedObject.
From: Core Data Model Versioning and Data Migration Guide
Dave's answer clarified that during a migration, core data object are only available as NSManagedObject instances. You don't get to use their Entity classes.
Worse, if you're using a tool like mogenerator, then any handy logic that you've extended the entity classes with is inaccessible.
Poor solutions
Working with an NSManagedObject directly feels dangerous to me. Using [managedObject valueForKey:#"someKey"] is verbose, but worse, there's no compiler checking that you've got your key name correct, so you might be asking for something managedObject doesn't have. There's also no compiler checking of the returned type either – it could be anything that you can put in to a managed object.
Slightly better is [managedObject valueForKey: NSStringFromSelector(#selector(someKey))] is safer, but horribly verbose and awkward, and still isn't that safe – lots of things might implement the method someKey.
You might also declare your keys as literals:
NSString *const someKey = #"someKey";
[managedObject valueForKey: someKey];
Again – this is slightly safer, less error prone and verbose, but you've got no guarantee still that this managed object has someKey.
None of these approaches will give us access to custom logic, either.
Better solution
What I did instead of this was to define a protocol for the properties that I know my managed object has. Here's are examples for entities Choice and ChoiceType.
#protocol ChoiceProtocol
- (NSSet<id <ChoiceTypeProtocol> > *)choiceTypes;
- (NSNumber *)selected;
- (NSNumber *)order;
#end
#protocol ChoiceTypeProtocol
- (NSNumber *)selected;
- (NSString *)name;
- (NSString *)textCustom;
- (NSNumber *)order;
#end
Now in my migration code, instead of having a custom migration function similar to:
- (NSString *)migratedRepresentationOfChoice:(NSManagedObject *)choice;
I have:
- (NSString *)migratedRepresentationOfChoice:(id <ChoiceProtocol>)choice;
In the body of this function I can use choice exactly as I would any regular object. I get code completion as I type, I get the right syntax highlighting, the compiler will complain if I call a non existent method.
I also get return type checking, so the compiler will complain if I use the NSNumber property selected as an NSString or NSSet. And it'll also be helpful and suggest NSNumber methods as completions while I type.
How about the logic?
This approach doesn't provide the logic you've added to entity classes.
In Swift you might be able to use a protocol extension to achieve that.
I was retiring these entities (hence the migration), so I moved the entity logic functions I needed in to a helper functions of my custom migration. As an example choice.orderedChoiceTypes becomes [self choiceOrderedChoiceTypes:choice].
In future I will probably avoid ever adding logic to NSManagedObject entities. I think it's probably a better plan to put any such logic in domain objects that you build from your managed objects. Further, I will probably avoid defining entity classes and instead only access NSManagedObject instances through a protocol as during the migration. It seems clean, simple, removes magic, and has benefits for testing – not just for migrations.

CFBitVector stored as an attribute to a coredata object

I am attempting to create a fingerprint for an object in coreData, and want to set it as attribute to an object. I figured CFBitArray is the way to go.
I am trying to figure out how to save this per object:
Here is an example
Object
attributes:
Name:
Fingerprint ("01010101010101010101010110") etc...
This is used to try to match with a master print
Any suggestions?
You'd have to convert that to/from something Core Data understands, and save the converted value. There are a couple of possibilities, both of which involve getting the actual bits via CFBitVectorGetBits. Once you have that, you can
Save them in an NSData using something like +dataWithBytes:length:, and put that in a binary-type attribute on a managed object. Or...
Depending on the number of bytes you're using, save them in an NSNumber using something like +numberWithLong: (or whatever is long enough for the number of bits). Then put that in one of Core Data's integer types-- again, choosing whatever size fits your bits.
You can make the conversion either by using custom accessor methods on your NSManagedObject subclass, or by using the transformable Core Data attribute type and a value transformer class. For the latter you'd subclass NSValueTransformer and implement your conversions there (Apple provides a couple of examples of this).
Depending on what you're actually doing, you might want to consider using NSIndexSet instead of CFBitVectorRef. If nothing else, it conforms to NSCoding-- which means you can use a transformable attribute but rely on Core Data's default value transformer instead of writing your own.
You might also find it a lot simpler to just use one of the integer types and rely on bitwise operators to determine if a bit is set. Then you don't need to do anything special with Core Data, you just choose the appropriately-sized integer type.
Why are you not just storing NSData? It is way, way easier to store binary data inside NSData than inside CFBitvectorRef.
If you're trying to store a hash / fingerprint of something, I assume you're creating a SHA-256 hash with CC_SHA256_Init, _Update and _Final. Those will give you a so-called digest which is a fingerprint of the data you pass into the CC_SHA256_Update.
// Create the context:
CC_SHA256_CTX shaContext;
CC_SHA256_Init(&shaContext);
// For each value:
CC_SHA256_Update(&shaContext, &v, sizeof(v));
// Get the fingerprint / digest:
unsigned char digest[CC_SHA256_DIGEST_LENGTH];
CC_SHA256_Final(digest, &shaContext);
NSData *fingerprint = [NSData dataWithBytes:digest length:sizeof(digest)];
Then you can store that fingerprint into a Core Data attribute that's Binary Data.
Depending on the type of v, you might have to change the call to CC_SHA256_Update(). If you do this on an NSObject, you need to call it for each instance variable that you're interested in (that should be part of the fingerprint), e.g. if you have
#property (nonatomic) int32_t count;
#property (nonatomic, copy) NSString *name;
you'd do
int32_t v = self.count
CC_SHA256_Update(&shaContext, &v, sizeof(v));
NSData *d = [self.name dataUsingEncoding:NSUTF8Stringencoding];
CC_SHA256_Update(&shaContext, [data bytes], [data length]);

What should be used instead of the deprecated EntityName.account.ToString()?

When I design a QueryExpression, I've always used the following, hard-coded syntax.
QueryExpression expression = new QueryExpression
{
EntityName = "account";
...
}
In this blog the following syntax based on this enumeration is used.
QueryExpression expression = new QueryExpression
{
EntityName = EntityName.account.ToString();
...
}
I liked it much better but I couldn't find it using intellisense. Then I've found this discussion where it's explained that the syntax is deprecated.
So, what should one use instead of EntityName? I've googled it a bit but can't find anything useful. It seems to me that some kind of enumeration is to be preferred before the hard-coded expression. Is that correct? And if so, how to do that in CRM 2011?
If you are using early bound classes you could use following syntax also for your custom entities. If not, this will work for all standard entities.
Account.EntityLogicalName
And if you are using late bound entities you can use Entity.LogicalName.
you can write for example Contact.EntityLogicalName.ToString() but you in this case you must
use early bound classes

How can I know the class type of an abstract entity in a NSPredicate?

Using core data I'd like to fetch some data. My model uses some abstract entities, see attached picture, where QuantifiedIngredient is an abstract class.
I'd like to fetch Ingredient entities that have at least one RecipeQuantifiedIngredients, but in the middle is QuantifiedIngredient, which is an abstract class.
How can I do that, how can I test the actual type of an abstract class inside a NSPredicate? Any idea or suggestion?
The only clue I found was:
How can you reference child entity name in a predicate for a fetch request of the parent entity?
Would work a custom property in my QuantifiedIngredient to know if it is a RecipeQuantifiedIngredient? For instance isRecipeQuantifiedIngredient?
Thanks a lot for your help.
If recipe is required in RecipeQuantifiedIngredient, you could try to make a fetch, that checks, if there is any ingredient.recipe. I think, that will work.
The custom property, in kind of flag, will work for you too. You'll just need to set and unset it whenever you add or delete all the recipeQuantifiedIngredient.
I don't want to take the time to translate this into CoreData-speak so here is my thought in SQL:
SELECT * FROM quantifiedIngredients WHERE recipe <> NULL
or something like that. This is essentially Nikita's suggestion of using a flag, except that the 'flag' is the existence of a property. I don't know how CoreData will react when faced with GroceryQuantifiedIngredients that don't have the recipe, I think KVO will throw an exception. You might be so bold as to add a category:
#interface GroceryQuantifiedIngredients (KVOHack)
-(id)recipe;
#end
#implementation GroceryQuantifiedIngredients (KVOHack)
-(id) recipe { return nil; }
#end
This would of course require CoreData to enumerate all quantifiedIngredients, but I presume it will have to do so anyway, and a the return nil should optimize into tiny code. The other consideration is whether this will have a bad effect on the rest of your code; you will have to make that call.
Another idea which pops to mind as I finish this up is to do something like this (I'm getting really loose with my pseudo-code now):
SELECT * FROM quantifiedIngredients WHERE [i respondsToSelector:#selector(recipe)];
See what I mean? I forget whether CoreData lets you play with some kind of cursor when working with predicates or fetchedThingamabobbers, if it does than I think this is your best bet. Anyway it's Sunday afternoon so that stuff is left as a exercise for the reader.
+1 for a good question.

Resources