364

On my Lion app, I have this data model:

enter image description here

The relationship subitems inside Item is ordered.

Xcode 4.1 (build 4B110) has created for me the file Item.h, Item.m, SubItem.h and SubItem.h.

Here is the content (autogenerated) of Item.h:

#import <Foundation/Foundation.h>

#import <CoreData/CoreData.h>

@class SubItem;

@interface Item : NSManagedObject {
@private
}

@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSOrderedSet *subitems;
@end

@interface Item (CoreDataGeneratedAccessors)

- (void)insertObject:(SubItem *)value inSubitemsAtIndex:(NSUInteger)idx;
- (void)removeObjectFromSubitemsAtIndex:(NSUInteger)idx;
- (void)insertSubitems:(NSArray *)value atIndexes:(NSIndexSet *)indexes;
- (void)removeSubitemsAtIndexes:(NSIndexSet *)indexes;
- (void)replaceObjectInSubitemsAtIndex:(NSUInteger)idx withObject:(SubItem *)value;
- (void)replaceSubitemsAtIndexes:(NSIndexSet *)indexes withSubitems:(NSArray *)values;
- (void)addSubitemsObject:(SubItem *)value;
- (void)removeSubitemsObject:(SubItem *)value;
- (void)addSubitems:(NSOrderedSet *)values;
- (void)removeSubitems:(NSOrderedSet *)values;

@end

And here is the content (autogenerated) of Item.m:

#import "Item.h"
#import "SubItem.h"

@implementation Item

@dynamic name;
@dynamic subitems;

@end

As you can see, the class Item offers a method called addSubitemsObject:. Unfortunately, when trying to use it in this way:

Item *item = [NSEntityDescription insertNewObjectForEntityForName:@"Item" inManagedObjectContext:self.managedObjectContext];
item.name = @"FirstItem";

SubItem *subItem = [NSEntityDescription insertNewObjectForEntityForName:@"SubItem" inManagedObjectContext:self.managedObjectContext];

[item addSubitemsObject:subItem];

this error appear:

2011-09-12 10:28:45.236 Test[2002:707] *** -[NSSet intersectsSet:]: set argument is not an NSSet

Can you help me?

Update:

After just 1,787 days from my bug report, today (August 1, 2016) Apple wrote me this: "Please verify this issue with the latest iOS 10 beta build and update your bug report at bugreport.apple.com with your results.". Let's hope this is the right time :)

Dev
  • 7,041
  • 6
  • 33
  • 63
  • The `addSubitemsObject:`method accept a `SubItem`, not a `NSSet`, as you can see in the declaration (`Item.h`). – Dev Sep 12 '11 at 20:27
  • 5
    I'm seeing same issue. Hopefully it gets fixed soon. Although using the mutable ordered set directly is an easy workaround for the time being. Note: I am using mogenerator, but I assume it is using the same Apple generator internally for this portion of the generated code. – Chad Podoski Oct 10 '11 at 21:48
  • I'm hitting this same bug. Can anyone confirm I can access the Ordered Set directly without problems? Is there nothing important going on in the auto-generated methods that I will bypass through direct access. – Undistraction Oct 18 '11 at 10:46
  • I'm running into the same problem. 3rd major issue I've run into with Core Data. Making me wonder if its worth it. If I have to write my own scaler accessors and relationship accessors, not much left that its providing. – David May 04 '12 at 03:19
  • Running into the same issue. It looks like it is caused by the back-reference to item from subitem. If I remove it it works just fine. – tapi Oct 16 '12 at 18:16
  • 12
    It is almost 2 years! Will you fix it in iOS 7, Apple? —— I just want to share with those wondering whether this bug is still there:"Yes, it is." – an0 Jun 02 '13 at 15:17
  • 1
    Very close to two years now, this is still an issue in all xcode 5 developer previews. – Korvin Szanto Aug 21 '13 at 18:42
  • Why is this still not addressed? Does anybody know? Is it really that hard to fix this bug?... – neural5torm Mar 07 '14 at 15:58
  • 2
    Do you still see the problem if you use the appropriate KVC accessor? (i.e. ``mutableOrderedSetValueForKey:``) – quellish Apr 05 '14 at 08:07
  • 3
    Appears to still be an issue on Mavericks. – Tim May 04 '14 at 16:15
  • 1
    alomost 3 years. still there. – Lensflare Aug 14 '14 at 17:08
  • 1
    and with Yosemite!! still there, when they are gonna fix it!! – AMTourky Oct 27 '14 at 12:26
  • 1
    well actually one should use mutableOrderedSetValueForKey as mentioned here http://stackoverflow.com/a/26676124/926899 – Nicolas Manzini Oct 31 '14 at 13:45
  • and still there! iOS 8.1.3, xcode 6.1.1! – gaussblurinc Feb 06 '15 at 09:48
  • Still crashing on Yosemite 10.10.5 and Xcode 7.1. – Dalmazio Nov 13 '15 at 20:23
  • Swift bug filed: https://bugs.swift.org/browse/SR-468 – Ben Leggiero Jan 05 '16 at 03:32
  • Observing the same issue with Xcode 7.2.1, iOS 9.2 – DevGansta Feb 27 '16 at 20:42
  • Crashed in ios 10.0 Xcode8.0 – Anees Nov 16 '16 at 10:23

25 Answers25

262

I reproduced your setup both with your data model and one of my own with different names. I got the same error in both cases.

Looks like a bug in Apple's autogenerated code.

TechZen
  • 63,819
  • 15
  • 116
  • 144
  • 5
    Thanks. I filed a bug report. – Dev Sep 13 '11 at 06:55
  • @Dev Thanks! Please provide bug number. – nicerobot Dec 10 '11 at 02:15
  • +1 For the bug number. @Dev please provide it here. I does NOT seem to be fixed neither in 4.2.1 nor in XCode 4.3 Preview – Sbhklr Jan 15 '12 at 10:08
  • 60
    The bug ID is 10114310. It was reported on 13-Sep-2011 but today (15-Jan-2012) it is still "open". It is incredible, considering the number of people who have the same problem. – Dev Jan 15 '12 at 14:05
  • Having the same issue, updated from Xcode 3 to 4.x.. It is very frustrating. – DerekH Jan 18 '12 at 16:07
  • 1
    have same issue ... can you put the bug on openradar – Stephan Jan 25 '12 at 06:59
  • The bug is actually related to the ordered attribute since the auto generated functions are actually for NSSet not NSOrderedSet. – Sebastian Hoffmann Jan 26 '12 at 19:48
  • 3
    Everyone should file a duplicate bug with Apple on this. Duplicate bugs are one of the main ways the Apple engineers gauge priority for bug fixes. – atticus Feb 03 '12 at 11:27
  • If I go to apple radar and write in problem id this: 10114310, it doesn't find the bug. IS it normal? – Ricardo May 09 '12 at 12:13
  • 14
    Update: today (May 11, 2012) the bug #10114310 is still open, 241 days after my report (September 13, 2011). Unbelievable. – Dev May 11 '12 at 09:16
  • @Dev: I've a similar experience with this one and just hit it again. You would wonder how it got past testing given that it simply does not work! – Damien May 12 '12 at 17:41
  • 23
    I just brought this up with an Apple engineer during one of the CoreData Lab sessions at WWDC. They acknowledge the issue and that it's a genuine bug, and from what I've seen it has the "critical" status, but of course there's no promise as to when they'll fix it. I don't think it'll be fixed in iOS6/Mountain Lion. I think it'd be good to duplicate this radar further. Currently it has about 25 dup's, the more the better! – DaGaMs Jun 13 '12 at 22:48
  • For reference, here is my OpenRadar for the duplicate I entered: http://openradar.appspot.com/radar?id=1760411 – DaGaMs Jun 13 '12 at 23:21
  • I've also raised this on Apple's bug tracker. OpenRadar duplicate: http://openradar.appspot.com/radar?id=1850415 – j b Aug 13 '12 at 14:43
  • Update: today (August 30, 2012) the bug #10114310 is still open. Almost a year has passed since my first report (September 13, 2011). Unbelievable. – Dev Aug 30 '12 at 07:35
  • Seems Apple doesn't want to solve the problem. I still encounter the same problem today. – Bagusflyer Sep 04 '12 at 16:45
  • Bug # 12476849 also filed on Radar for this issue – jmstone617 Oct 11 '12 at 06:12
  • 2
    **Update:** 442 days have passed since I filled the bug #10114310 and, guess what? Today (November 28, 2012) it is still open. I also believe that seems Apple doesn't want to solve the problem. – Dev Nov 28 '12 at 14:35
  • 1
    Still happening here, Jan '13. – Simon Jan 15 '13 at 17:13
  • Simon, that's incredible. I use ordered relationships very often. Don't understand why apple don't fix it. – Ricardo Jan 28 '13 at 09:37
  • Yup - still exists in iOS 6.1 too. Filed radar 13191471 against not having a "known issue" about this in the docs. – Scott Corscadden Feb 12 '13 at 10:50
  • I've just filled a duplicate 13430690 just in case. – iGranDav Mar 15 '13 at 16:46
  • Filed another duplicate, in the hopes that more will help get it fixed. – GreenKiwi Apr 25 '13 at 22:09
  • 4
    After almost two years, Apple confirms this critical bug is still there: "This issue is still being investigated. If we have an update or need more information we will contact you." Wish they fix it in iOS 7. – an0 Jun 04 '13 at 16:01
  • How this is not listed as a "Known issue" utterly confounds me. If you use the type-safe auto generated methods, _you're gonna have a bad time_ (/me ends rant) – Scott Corscadden Jul 25 '13 at 12:52
  • Still happening for me, filed duplicate – marchinram Aug 01 '13 at 23:45
  • 1
    I just had this same error and since I almost missed it, I wanted to point out @Sterling's github code below which beautifully fixes this problem. His repository is [KCOrderedAccessorFix](https://github.com/CFKevinRef/KCOrderedAccessorFix), and with one line of code, everything works as expected! – rvijay007 Aug 17 '13 at 01:18
  • 40
    Just checked today, it is still there in iOS 7 GM/OMG! I can't believe it… – an0 Sep 17 '13 at 19:59
  • 80
    **Update:** 797 days, 2 new major iOS versions, and countless Xcode versions have passed since I filled the bug #10114310. And it is still "open." Unbelievable. – Dev Nov 18 '13 at 09:56
  • 4
    I cannot believe this. Just hit this right now. Spend an entire day debugging because I thought I had a flaw in my add logic. Thanks Crapple. – Andy Tsen May 14 '14 at 02:05
  • still get it...so terrible – ruandao Jul 21 '14 at 01:36
  • Does apple know about this bug ?? I just hit the same problem on iOS8 beta 5, Xcode 6 b6. – Ezimet Aug 20 '14 at 17:05
  • 12
    **Update:** Happy 3rd birthday! The NSOrderedSet's bug turned three two days ago :) – Dev Sep 15 '14 at 14:15
  • 1
    Welcome to iOS 8 GM, little bug. – Carlos P Oct 06 '14 at 16:57
  • **Update:** It's still open! – Dănuț Mihai Florian Oct 14 '14 at 14:07
  • well actually one should use mutableOrderedSetValueForKey as mentioned here http://stackoverflow.com/a/26676124/926899 – Nicolas Manzini Oct 31 '14 at 13:48
  • 2
    I met this problem in late 2012 and I searched to this question, now it is 2015, in iOS 8, the bug still exist, I will leave a comment every time I bump into this bug in the rest of my life!!!!!!! – CarmeloS Jan 13 '15 at 03:19
  • 3
    February 9th, 2015. Still isn't fixed. AppLOL :) – Alexander Perechnev Feb 09 '15 at 13:30
  • Just got this bug in my code. It is March 27th. Apple in a nutshell indeed. – Anton Ogarkov Mar 27 '15 at 20:15
  • I used a KCOrderedAccessorFix to fix this which broke with 64bit runtime. FML. – Ben Affleck May 06 '15 at 17:23
  • It looks like still happening. May 31, 2015 – andykkt May 30 '15 at 15:03
  • 9
    Hey guys, I'm still here with you, in iOS 9! – an0 Jun 10 '15 at 22:22
  • 3
    This is sad apple, after almost 4 years, the bug still exists in a central piece of core data – adamF Jul 01 '15 at 15:33
  • Strikes again. Come on guys – CommaToast Apr 20 '16 at 19:55
  • That's incredible...... Dear Apple, can you tell me how to insert/add an element on my NSOrderedSet????? – Tommaso Resti May 16 '16 at 16:15
  • Just verified it is still in iOS 10 beta 2. That's 5 years! I'm totally baffled. – an0 Jul 05 '16 at 20:00
  • 4
    **Update:** after just 1,787 days from my bug report, today (August 1, 2016) Apple wrote me this: "Please verify this issue with the latest iOS 10 beta build and update your bug report at https://bugreport.apple.com/ with your results.". Let's hope this is the right time :) – Dev Aug 02 '16 at 07:57
  • 9
    Fixed in iOS 10! NSManagedObject dynamic accessor generation has fixed issues to properly generate accessors for ordered to-many relationships. https://developer.apple.com/library/prerelease/content/releasenotes/General/WhatNewCoreData2016/ReleaseNotes.html#//apple_ref/doc/uid/TP40017342-CH1-DontLinkElementID_3 – Morrowless Oct 03 '16 at 09:23
  • 2019: iOS 12 I got this bug again. – Krypt Jul 05 '19 at 11:45
243

I agree that there may be a bug here. I've modified the implementation of the add object setter to append correctly to a NSMutableOrderedSet.

- (void)addSubitemsObject:(SubItem *)value {
    NSMutableOrderedSet* tempSet = [NSMutableOrderedSet orderedSetWithOrderedSet:self.subitems];
    [tempSet addObject:value];
    self.subitems = tempSet;
}

Reassigning the set to self.subitems will ensure that the Will/DidChangeValue notifications are sent.

pkamb
  • 26,648
  • 20
  • 124
  • 157
InitJason
  • 2,653
  • 2
  • 11
  • 16
  • Your code snippet was just what I needed to get me around this issue. Hope Apple fixes the issue eventually, but so far I haven't seen any issues with using your approach. – Christopher Hujanen Dec 02 '11 at 02:52
  • I am getting this error when I try to implement this work-around.. [__NSArrayI isEqualToSet:]: unrecognized selector sent to instance... This usually is from a item that has been release, but can't find where, anyone run into this? – DerekH Jan 19 '12 at 15:49
  • @DerekH isEqualToSet is a method only NSSet has, so my guess is you've converted, created, or are treating a pointer as an NSArray before passing back to the NSManagedObject, which should if any reason be calling isEqualToOrderedSet to determine if the set needs to even change or be left as is. – InitJason Jan 19 '12 at 17:42
  • @Leelll thanks for the reply, actually in mine I am using NSSet instead of NSOrderableSet set.. My code looks the same except I replaced NSMutableOrderedSet with NSMutableSet.. I wouldn't have though there was a difference, but maybe there is? – DerekH Jan 19 '12 at 19:12
  • Also, it only happens if when I do [tempSet addObject:value].. If I comment this out and just do the creation of the Mutable with itself and then re-set it.. it works fine.. It doesn't error until after I re-set "property". – DerekH Jan 19 '12 at 19:31
  • @DerekH I'm not sure what the underlying properties of a NSManagedObject are for a un-ordered to-many relationship. But what is not buggy is using the auto-generated method "addSubitemObject" don't implement this method, only declare it. – InitJason Jan 20 '12 at 05:24
  • Just as a warning, I have implemented this code and it worked fine, however, later I found that its very slow! Be warned if you are repeatedly calling this in a loop. – Robert Apr 30 '12 at 08:25
  • As a follow up to my previous comment I have found a way to speed this up if you need to call `addSubItemObject:` repeatedly. See my answer below. – Robert Apr 30 '12 at 08:58
  • Similar to what this guy did: http://dev.totodotnet.net/post/16894288540/coredata-with-nsorderedset-does-not-work-well-on – acecapades Jun 09 '12 at 02:23
  • @nicerobot - dead link now :( – Tin Can Aug 17 '12 at 18:40
  • Wouldn't it be simpler to just use the mutable set accessor? – John Cromartie Feb 12 '13 at 15:37
  • Other reimplementations of Apple's broken methods, like the one here: http://stackoverflow.com/a/15994818/1709587 and the one here: http://dev.totodotnet.net/post/16894288540/coredata-with-nsorderedset-does-not-work-well-on call `willChangeValueForKey:` and `didChangeValueForKey:`, respectively before and after adding the object, but this solution does not. Should it? I've never used either of those methods so I don't know when they're usually triggered or what they're typically used for. – Mark Amery Jul 11 '13 at 15:47
  • @MarkAmery the dynamic setter self.subitems should be sending the notifications. Have you tested if it is not, or only wanted to know they the methods were not called from addSubitemsObject? – InitJason Jul 12 '13 at 00:00
  • @JLust I haven't tested anything, or looked too closely at the code or docs - I just noticed that other people's approaches to this explicitly called them, and that yours doesn't, and wondered what the reason for the difference was and whether it was a mistake on anyone's part. – Mark Amery Jul 12 '13 at 10:26
  • 3
    @MarkAmery Tested. Verified. The dynamic setter self.subitems does send the notifications. So JLust solution is correct. – bernstein Aug 14 '13 at 15:59
  • 3
    This is a good answer, but its inefficient. You copy the entire ordered set, modify and then copy it back. The effect is not just a hit to the ordered set, but notifications are sent out that that every time the ordered set is altered, its entire contents are changed! If this ordered set is used for a UITable for instance, this could have serious ramifications for updating. I've outlined in my solution exactly where the error comes from and I've shown a more efficient method for bypassing the error. – Owen Godfrey Sep 29 '13 at 00:45
  • Hmmm, I tried this solution but the changes don't appear to be saved (after calling saveContext). – iphone007 Nov 04 '14 at 02:19
  • Hmmm, my code is not exactly the same now but it works under iOS 7.1 in the simulator but doesn't work under iOS 8.1 in the simulator. :-( – iphone007 Nov 04 '14 at 03:16
111

I've decided to improve the solution by implementing all the required methods:

static NSString *const kItemsKey = @"<#property#>";

- (void)insertObject:(<#Type#> *)value in<#Property#>AtIndex:(NSUInteger)idx {
    NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
    [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    [tmpOrderedSet insertObject:value atIndex:idx];
    [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
    [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)removeObjectFrom<#Property#>AtIndex:(NSUInteger)idx {
    NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
    [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    [tmpOrderedSet removeObjectAtIndex:idx];
    [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
    [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)insert<#Property#>:(NSArray *)values atIndexes:(NSIndexSet *)indexes {
    [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    [tmpOrderedSet insertObjects:values atIndexes:indexes];
    [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
    [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)remove<#Property#>AtIndexes:(NSIndexSet *)indexes {
    [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    [tmpOrderedSet removeObjectsAtIndexes:indexes];
    [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
    [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)replaceObjectIn<#Property#>AtIndex:(NSUInteger)idx withObject:(<#Type#> *)value {
    NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
    [self willChange:NSKeyValueChangeReplacement valuesAtIndexes:indexes forKey:kItemsKey];
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    [tmpOrderedSet replaceObjectAtIndex:idx withObject:value];
    [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
    [self didChange:NSKeyValueChangeReplacement valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)replace<#Property#>AtIndexes:(NSIndexSet *)indexes with<#Property#>:(NSArray *)values {
    [self willChange:NSKeyValueChangeReplacement valuesAtIndexes:indexes forKey:kItemsKey];
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    [tmpOrderedSet replaceObjectsAtIndexes:indexes withObjects:values];
    [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
    [self didChange:NSKeyValueChangeReplacement valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)add<#Property#>Object:(<#Type#> *)value {
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    NSUInteger idx = [tmpOrderedSet count];
    NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
    [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
    [tmpOrderedSet addObject:value];
    [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
    [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
}

- (void)remove<#Property#>Object:(<#Type#> *)value {
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    NSUInteger idx = [tmpOrderedSet indexOfObject:value];
    if (idx != NSNotFound) {
        NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
        [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
        [tmpOrderedSet removeObject:value];
        [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
        [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
    }
}

- (void)add<#Property#>:(NSOrderedSet *)values {
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet];
    NSUInteger valuesCount = [values count];
    NSUInteger objectsCount = [tmpOrderedSet count];
    for (NSUInteger i = 0; i < valuesCount; ++i) {
        [indexes addIndex:(objectsCount + i)];
    }
    if (valuesCount > 0) {
        [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
        [tmpOrderedSet addObjectsFromArray:[values array]];
        [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
        [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
    }
}

- (void)remove<#Property#>:(NSOrderedSet *)values {
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
    NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet];
    for (id value in values) {
        NSUInteger idx = [tmpOrderedSet indexOfObject:value];
        if (idx != NSNotFound) {
            [indexes addIndex:idx];
        }
    }
    if ([indexes count] > 0) {
        [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
        [tmpOrderedSet removeObjectsAtIndexes:indexes];
        [self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
        [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
    }
}
Senseful
  • 73,679
  • 56
  • 267
  • 405
Dmitry Makarenko
  • 1,450
  • 1
  • 12
  • 13
  • I'm using removeObjectFromSubitemsAtIndex but when I save the context, my program crashes. It seems the deleted object still exists. However if after removeObjectFromSubitemsAtIndex I call context deleteObject, when I save the context everything goes fine. Any idea? Thanks. – Ricardo May 10 '12 at 15:22
  • 1
    What is the crash type? 'removeObjectFromSubitemsAtIndex' doesn't delete these subitems, they still exist in your storage, it's just the way to remove the relationship between the objects. – Dmitry Makarenko May 10 '12 at 15:37
  • what is kItemsKey? Is it the attribute name? – Bagusflyer Sep 05 '12 at 03:06
  • And I add these methods in my program and did some updates, for example I called insertObject. But nothing happened in my Core Data. – Bagusflyer Sep 05 '12 at 03:19
  • 2
    kItemsKey is a constant which was added just for convenience in KVO methods calls. It's a name of ordered relationship you are writing your methods for. – Dmitry Makarenko Sep 05 '12 at 13:36
  • 1
    That's what I think it is. Thanks. But my problem is that the data is not saved to database by using these methods. – Bagusflyer Sep 06 '12 at 03:07
  • Did you call `save:` method of MOC you are using? Problem should not be in method I provided, as they are doing quite simple action, which definitely changes a state of NSManagedObject, most likely you are not calling `save:` or it returns with error. – Dmitry Makarenko Sep 06 '12 at 07:14
  • Why are you making a copy of the mutableOrderedSet, and then reassigning the primitive to it? I thought the whole point of the mutableOrderedSet is that you can affect it directly, and it will handle key/value notifications and relationships for you. (see http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdAccessorMethods.html#//apple_ref/doc/uid/TP40002154-SW6) – pickwick Jan 11 '13 at 17:20
  • 4
    !!!!!!!!! Just copying the code and change the method names, it works perfectly!!! This is the fastest answer. – flypig Sep 02 '13 at 14:50
  • 1
    This is terrific, but creating the temporary copy of the ordered set is unnecessary. The culprit is `willChangeValueForKey:withSetMutation:usingObjects`, which you have successfully avoided. After that, just use `[[self primitiveValueForKey:ChildrenKey] unionOrderedSet:values]` or `[[self primitiveValueForKey:ChildrenKey] minusOrderedSet:values]` as appropriate. See my answer for details. – Owen Godfrey Sep 29 '13 at 00:40
  • Awesome! I copied the codes and changed the variables a little. Works like a charm. – BiGGA Oct 27 '13 at 09:57
  • Great Answer!!! This should be the accepted answer with notes about the Apple bug. Tried a bunch of different versions from others that didn't work. This worked perfectly! Thank you!!! – Etep Aug 01 '16 at 02:56
38

Yes, this is definitely a Core Data bug. I wrote up an ObjC-Runtime-based fix a while back, but at the time I figured it would be fixed soon. Anyway, no such luck, so I posted it up on GitHub as KCOrderedAccessorFix. Work around the problem on all your entities:

[managedObjectModel kc_generateOrderedSetAccessors];

One entity in particular:

[managedObjectModel kc_generateOrderedSetAccessorsForEntity:entity];

Or just for one relationship:

[managedObjectModel kc_generateOrderedSetAccessorsForRelationship:relationship];
Sterling Archer
  • 911
  • 8
  • 14
  • I wonder if this will conflict with the real fix from Apple or not? – tia Jun 11 '13 at 10:26
  • 3
    This should not conflict with Apple's fix, as its purpose is to override Apple's implementation no matter what. When/if this is actually fixed by Apple, maybe I'll add a `- (BOOL)kc_needsOrderedSetAccessorFix;` or something that checks Foundation/iOS version. – Sterling Archer Jan 07 '14 at 19:22
  • 2
    There is a KCOrderedAccessorFix.podspec already in the CocoaPods master repo. So in order to link this to your projects you can simply add "pod 'KCOrderedAccessorFix'" to your Podfile – Anton Matosov Feb 27 '14 at 21:50
  • This had some issues with iOS 8 (incorrect method signatures for `objc_msg_send`) – NSTJ Dec 31 '14 at 02:06
  • On iOS9 it works, nice job! This is the best solution ever, no need to change anything in your code! – Borzh Dec 04 '15 at 13:34
33

Instead to making a copy I suggest to use the accessor in NSObject to get access to the NSMutableOrderedSet of the relationships.

- (void)addSubitemsObject:(SubItem *)value {
      NSMutableOrderedSet* tempSet = [self mutableOrderedSetValueForKey:@"subitems"];
      [tempSet addObject:value];
 }

e.g. the Core Data Release Notes for iOS v5.0 refer to this.

In a short test it worked in my application.

Stephan
  • 4,163
  • 2
  • 22
  • 32
  • 1
    Can't refactor literal strings as easily. The compiler can type check self.subitems if you use code. – logancautrell May 07 '12 at 17:47
  • 1
    @logancautrell yes this is correct. It depends on the priority of the specific use case. In general I focus on save resources especially in this case because this was just a workaround. – Stephan Jun 13 '12 at 06:36
  • 2
    The literal string may be replaced by `NSStringFromSelector(@selector(subitems))` though :) – Ja͢ck Jul 04 '14 at 00:02
17

I've tracked the bug. It occurs in willChangeValueForKey:withSetMutation:usingObjects:.

This call sets off a chain of notifications which may be difficult to track, and of course changes to one responder may have implications for another, which I suspect is why Apple have done nothing.

However, it is okay in Set and its only the Set operations on an OrderedSet that malfunction. That means there are only four methods that need to be altered. Therefore, all I did was convert the Set operations to their equivalent Array operations. These work perfectly and minimal (but necessary) overheads.

On a critical level, this solution does suffer from one critical flaw; if you are adding objects and one of the objects already exists, then it is either not added or moved to the back of the ordered list (I don't know which). In either case, the expected ordered index of the object by the time we arrive at didChange is different from what was anticipated. This may break some people's apps, but it doesn't affect mine, since I am only ever adding new objects or I confirm their final locations before I add them.

- (void)addChildrenObject:(BAFinancialItem *)value {
    if ([self.children containsObject:value]) {
        return;
    }
    NSIndexSet * indexSet = [NSIndexSet indexSetWithIndex:self.children.count];
    [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];
    [[self primitiveValueForKey:ChildrenKey] addObject:value];
    [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];
}

- (void)removeChildrenObject:(BAFinancialItem *)value {
    if (![self.children containsObject:value]) {
        return;
    }
    NSIndexSet * indexSet = [NSIndexSet indexSetWithIndex:[self.children indexOfObject:value]];
    [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexSet forKey:ChildrenKey];
    [[self primitiveValueForKey:ChildrenKey] removeObject:value];
    [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexSet forKey:ChildrenKey];
}

- (void)addChildren:(NSOrderedSet *)values {
    if ([values isSubsetOfOrderedSet:self.children]) {
        return;
    }
    NSIndexSet * indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(self.children.count, values.count)];
    [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];
    [[self primitiveValueForKey:ChildrenKey] unionOrderedSet:values];
    [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];
}

- (void)removeChildren:(NSOrderedSet *)values {
    if (![self.children intersectsOrderedSet:values]) {
        return;
    }
    NSIndexSet * indexSet = [self.children indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
        return [values containsObject:obj];
    }];
    [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexSet forKey:ChildrenKey];
    [[self primitiveValueForKey:ChildrenKey] minusOrderedSet:values];
    [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexSet forKey:ChildrenKey];
}

Of course, there is an easier solution. it is as follows;

- (void)addChildrenObject:(BAFinancialItem *)value {
    if ([self.children containsObject:value]) {
        return;
    }
    [self insertObject:value inChildrenAtIndex:self.children.count];
}

- (void)removeChildrenObject:(BAFinancialItem *)value {
    if (![self.children containsObject:value]) {
        return;
    }
    [self removeObjectFromChildrenAtIndex:[self.children indexOfObject:value]];
}

- (void)addChildren:(NSOrderedSet *)values {
    if ([values isSubsetOfOrderedSet:self.children]) {
        return;
    }
    [self insertChildren:values atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(self.children.count, values.count)]];
}

- (void)removeChildren:(NSOrderedSet *)values {
    if (![self.children intersectsOrderedSet:values]) {
        return;
    }
    [self removeChildrenAtIndexes:[self.children indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
        return [values containsObject:obj];
    }]];
}
Owen Godfrey
  • 3,261
  • 2
  • 20
  • 18
  • Too bad everyone else seems to have overlooked this answer, definitely seems like the best solution. – George Dec 02 '13 at 01:05
  • This solution has much better performance than the ones using orderedSetWithOrderedSet to make a local set. That has a big overhead when you have large data sets. The easier solution appears to just be a refactored version of the initial one with the methods not shown. – David Pettigrew May 05 '14 at 22:21
  • 1
    I'm still seeing a crash in addChildren: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[TrackHistory insertTrackpoints:atIndexes:]: unrecognized selector sent to instance 0x1702b1b20' – Victor Bogdan Jun 08 '15 at 13:57
  • @OwenGodfrey For the easier solution, where are you implementing these methods? I'm getting an exception: [Parent insertObject:inChildrenAtIndex:] unrecognized selector sent to instance 0x6180000ac480. – Dalmazio Nov 13 '15 at 23:58
  • your variable is "Parent" with a capital "P"? Does that means you are call the the class "Parent", or do you have an instance variable named "Parent"? If my class is Parent, then i implemented this methods at the bottom of Parent, but you would need to call it on an instance, which would more likely be named "parent" with a lowercase "p", as these are not class methods. – Owen Godfrey Nov 15 '15 at 01:54
  • I get the same error as Dalmazio Brisinda. It seems the category functions are not accessible from the main class? I have MyObject and MyObject+CoreDataProperties. I put all this code into MyObject (with the changes necessary (Children->MyObject) but I get the same exception. I'm guessing this must have to go into the Category in order to work but then each time I make a change to my Core Data Object this will get overwritten. – Etep Aug 01 '16 at 02:33
11

The Apple docs To Many Relations says: you should access the proxy mutable set or ordered set using

NSMutableOrderedSet * set = [managedObject mutableOrderedSetValueForKey:@"toManyRelation"];

Modifying this set will add or remove relations to your managed object. Accessing the mutable ordered set using the accessor whether with [ ] or . notation is wrong and will fail.

Nicolas Manzini
  • 7,809
  • 6
  • 58
  • 77
  • 3
    To be fair, the docs also say: "or one of the automatically-generated relationship mutator methods (see Dynamically-Generated Accessor Methods):" – Matt Nov 09 '14 at 21:49
  • Okay okay... you're right. Well then, let's say that that's the simplest working way... – Nicolas Manzini Nov 10 '14 at 10:28
9

Received the same error, @LeeIII solution worked for me (thanks!). I suggest slightly modify it:

  • use objective-c category to store the new method (so we wont lose our method if Item is generated again)
  • check if we already have mutable set

Content of Item+category.m:

#import "Item+category.h"

@implementation Item (category)

- (void)addSubitemsObject:(SubItem *)value {
    if ([self.subitems isKindOfClass:[NSMutableOrderedSet class]]) {
        [(NSMutableOrderedSet *)self.subitems addObject:value];
    } else {
        NSMutableOrderedSet* tempSet = [NSMutableOrderedSet orderedSetWithOrderedSet:self.subitems];
        [tempSet addObject:value];
        self.subitems = tempSet;
    }
}

@end
Danik
  • 93
  • 1
  • 6
  • Good point to move this code into category. But still we need to embrace actual add/remove with will/setPrimitiveValue/didChange calls like in @Dmitry Makarenko answer. – Vladimir Shutyuk Nov 17 '13 at 15:34
8

If you are using mogenerator, then instead of

[parentObject add<Child>sObject:childObject];

simply use:

[[parent object <child>sSet] addObject:childObject];
Καrτhικ
  • 3,625
  • 2
  • 27
  • 41
  • Because mogenerator takes care of the extra code you would otherwise have to write and simply allows access to the underlying set object. – Καrτhικ Aug 20 '12 at 15:02
  • It looks like a fix has just been committed that means mogenerator will generate corrected bodies ... https://github.com/dmakarenko/mogenerator/commit/4ccb3dc84e899a81202367f7f86996c24cbb260b – combinatorial Jan 04 '13 at 23:53
  • 1
    I am using `mogenerator` but I still have the bug. – Colas Jun 11 '13 at 21:50
7

Personally I have just replaced the calls to the CoreData generated methods with direct calls to the method as outlined in another solution by @Stephan:

NSMutableOrderedSet* tempSet = [self mutableOrderedSetValueForKey:@"subitems"];
      [tempSet addObject:value];
[tempSet addObject:value];

This removes the need for categories that might later conflict with a solution from Apple to the generated code when the bug is fixed.

This has the added plus of being the official way to do it!

Grouchal
  • 9,683
  • 6
  • 30
  • 45
  • This gives the following error: '[ valueForUndefinedKey:]: this class is not key value coding-compliant for the key subitems.' – jmstone617 Oct 11 '12 at 13:39
  • While it still irks me no end that this is not listed in Apple's known issues (I have opened a radar for the apparently futile gesture that it is), this solution has worked flawlessly for me. – Scott Corscadden Jul 25 '13 at 12:47
  • Wish I had seen this answer earlier; I was using the highest voted answer until I recently did some digging and finally implemented exactly what you have here :) – Ja͢ck Dec 21 '14 at 03:31
  • Why is `addObject:` called twice? – Jason Moore Mar 08 '16 at 15:33
5

It seems that if you link the parent with the child by setting the parent to the child and not the other way around it works without crashing.

So if you do:

[child setParent:parent]

instead of

[parent setChildObects:child]

It should work, at least it works on iOS 7 and didn't had any problems with the relationship.

Cata
  • 10,663
  • 10
  • 61
  • 83
  • 1
    Doesn't do much good when both sides are to-many. Then there's no clear parent-child relationship. – fatuhoku May 06 '14 at 16:33
3

I have had the same problem, but only when I tried something different to what I had been doing. I can't see the code for subItem, but I will assume that it has a reverse link to item. Lets call this reveres link, "parentItem", then the easiest solution is this:

Item *item = [NSEntityDescription insertNewObjectForEntityForName:@"Item" inManagedObjectContext:self.managedObjectContext];
item.name = @"FirstItem";

SubItem *subItem = [NSEntityDescription insertNewObjectForEntityForName:@"SubItem" inManagedObjectContext:self.managedObjectContext];

//[item addSubitemsObject:subItem];
subItem.parentItem = item;

The effect is that it makes use of apple's own code and it is simple and clean. In addition, the set is automatically added to, and all observers are updated. No problem.

Owen Godfrey
  • 3,261
  • 2
  • 20
  • 18
  • This is very nice. It solves the whole problem and keeps it ordered aswell. Still insane that the bug is still present. Another benefit of this answer is that if you regenerate your core-data models you don't have to rewrite your bug fixes. Thanks! – Johan S Nov 02 '13 at 13:19
  • See my other answer. I tracked the bug in more detail. This is still the easiest way, but the other method is the best because it opens up more possibilities. – Owen Godfrey Nov 04 '13 at 02:22
  • Wow! Finally!!! Thanks! (Tried your other code, but got errors, something about that wrong type was sent at [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];) – Leonard Pauli Apr 05 '14 at 17:57
3

I just fell foul of this issue, and resolved it using a much simpler implementation than the others outlined here. I simply make use of the methods available on NSManagedObject for dealing with relationships when not using subclasses.

An example implementation for inserting an entity into an NSOrderedSet relationship would look like this:

- (void)addAddress:(Address *)address
{
    if ([self.addresses containsObject:address]) {
        return;
    }
    // Use NSManagedObject's methods for inserting an object
    [[self mutableOrderedSetValueForKey:@"addresses"] addObject:address];
}

This works perfectly, and is what I was using before I moved to NSManagedObject subclasses.

3

This issue occurred to me while migrating a project from Objective-C to Swift 2 with XCode 7. That project used to work, and for a good reason: I was using MOGenerator which had replacement methods to fix this bug. But not all methods require a replacement.

So here's the complete solution with an example class, relying on default accessors as much as possible.

Let's say we have a List with ordered Items

First a quick win if you have a one/to-many relationship, the easiest is to just do:

item.list = list

instead of

list.addItemsObject(item)

Now, if that's not an option, here's what you can do:

// Extension created from your DataModel by selecting it and
// clicking on "Editor > Create NSManagedObject subclass…"

extension List {
  @NSManaged var items: NSOrderedSet?
}

class List

  // Those two methods work out of the box for free, relying on
  // Core Data's KVC accessors, you just have to declare them
  // See release note 17583057 https://developer.apple.com/library/prerelease/tvos/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc7_release_notes.html
  @NSManaged func removeItemsObject(item: Item)
  @NSManaged func removeItems(items: NSOrderedSet)

  // The following two methods usually work too, but not for NSOrderedSet
  // @NSManaged func addItemsObject(item: Item)
  // @NSManaged func addItems(items: NSOrderedSet)

  // So we'll replace them with theses

  // A mutable computed property
  var itemsSet: NSMutableOrderedSet {
    willAccessValueForKey("items")
    let result = mutableOrderedSetValueForKey("items")
    didAccessValueForKey("items")
    return result
  }

  func addItemsObject(value: Item) {
    itemsSet.addObject(value)
  }

  func addItems(value: NSOrderedSet) {
    itemsSet.unionOrderedSet(value)
  }
end

Of course, if you're using Objective-C, you can do the exact same thing since this is where I got the idea in the first place :)

Arnaud
  • 15,630
  • 9
  • 57
  • 78
3

I agree that there maybe a bug here. I've modified the implementation of the add object >setter to append correctly to a NSMutableOrderedSet.

- (void)addSubitemsObject:(SubItem *)value {
     NSMutableOrderedSet* tempSet = [NSMutableOrderedSet orderedSetWithOrderedSet:self.subitems];
     [tempSet addObject:value];
     self.subitems = tempSet;
}

Reassigning the set to self.subitems will ensure that the Will/DidChangeValue notifications >are sent.

Leelll, are you sure that after such custom setup of NSMutableOrderedSet values stored in that set will be saved to the database correctly by CoreData? I didn't check that, but it looks like CoreData knows nothing about NSOrderedSet and expects NSSet as to-many relationship container.

Eugene Dudnyk
  • 4,173
  • 1
  • 21
  • 41
  • For CoreData to return or take an NSOrderedSet object, multiple conditions have to be met as this started question has shown. Most common mistakes I see when people sharing my code have been a developer not running Lion. NSOrderedSets framework is not available on snowleopard. But yes, I have not seen this fail, though I am not sure this is best on performance. I'd guess this takes the whole set and replaces it instead of just inserting the desired record. – InitJason Jan 19 '12 at 17:33
2

I think everybody is missing the real problem. It is not in the accessor methods but rather in the fact that NSOrderedSet is not a subclass of NSSet. So when -interSectsSet: is called with an ordered set as argument it fails.

NSOrderedSet* setA = [NSOrderedSet orderedSetWithObjects:@"A",@"B",@"C",nil];
NSSet* setB = [NSSet setWithObjects:@"C",@"D", nil];

 [setB intersectsSet:setA];

fails with *** -[NSSet intersectsSet:]: set argument is not an NSSet

Looks like the fix is to change the implementation of the set operators so they handle the types transparently. No reason why a -intersectsSet: should work with either an ordered or unordered set.

The exception happens in the change notification. Presumably in the code that handles the inverse relationship. Since it only happens if I set an inverse relationship.

The following did the trick for me

@implementation MF_NSOrderedSetFixes

+ (void) fixSetMethods
{
    NSArray* classes = [NSArray arrayWithObjects:@"NSSet", @"NSMutableSet", @"NSOrderedSet", @"NSMutableOrderedSet",nil];

    [classes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        NSString* name = obj;
        Class aClass = objc_lookUpClass([name UTF8String]);
        [MF_NSOrderedSetFixes fixMethodWithSetArgument:@selector(intersectsSet:) forClass:aClass];
        [MF_NSOrderedSetFixes fixMethodWithSetArgument:@selector(isSubsetOfSet:) forClass:aClass];
    }];
}

typedef BOOL (*BoolNSetIMP)(id _s,SEL sel, NSSet*);

/*
    Works for all methods of type - (BOOL) method:(NSSet*) aSet
*/
+ (void) fixMethodWithSetArgument:(SEL) aSel forClass:(Class) aClass 
{
    /* Check that class actually implements method first */
    /* can't use get_classInstanceMethod() since it checks superclass */
    unsigned int count,i;
    Method method = NULL;
    Method* methods = class_copyMethodList(aClass, &count);
    if(methods) {
        for(i=0;i<count;i++) {
            if(method_getName(methods[i])==aSel) {
                method = methods[i];
            }
        }
        free(methods);
    }
    if(!method) {
        return;
    }

   // Get old implementation
   BoolNSetIMP originalImp  = (BoolNSetIMP) method_getImplementation(method);
   IMP newImp = imp_implementationWithBlock(^BOOL(NSSet *_s, NSSet *otherSet) {
        if([otherSet isKindOfClass:[NSOrderedSet class]]) {
            otherSet = [(NSOrderedSet*)otherSet set];
        }
        // Call original implementation
        return originalImp(_s,aSel,otherSet);
    });
    method_setImplementation(method, newImp);
}
@end
Entropy
  • 31
  • 4
2

I just got the problem in Swift (Xcode 6.1.1).

The answer was DO NOT CODE ANY METHOD OR ADDITIONAL THINGS in your NSManagedObject subclasses. I think it is a compilator mistake. Very strange bug ..

Hope it helps ..

lobodart
  • 216
  • 1
  • 11
2

I solved this problem by set the inverse to No Inverse, I don't know why, Maybe there is Apple Bug.enter image description here

LevinYan
  • 56
  • 4
1

I found a fix for this bug that works for me. I just replace this:

[item addSubitemsObject:subItem];

with this:

item.subitemsObject = subItem;
Ben Leggiero
  • 25,904
  • 38
  • 161
  • 267
Bimawa
  • 3,305
  • 2
  • 20
  • 42
1

I found this question by googling for the error message, and just wanted to point out that I ran into this error in a slightly different way (not using ordered sets). This isn't quite an answer to the given question, but I'm posting it here just in case it is helpful to anyone else who stumbles across this question while searching.

I was adding a new model version, and added some relationships to existing models, and defined the add*Object methods in the header file myself. When I tried to call them, I got the error above.

After reviewing my models, I realized I had stupidly forgotten to check the "To-Many Relationship" checkbox.

So if you're running into this and you're not using ordered sets, double check your model.

BenV
  • 1,098
  • 1
  • 11
  • 18
1

Better version of the correct answer in SWIFT

var tempSet = NSMutableOrderedSet()
if parent!.subItems != nil {
    tempSet = NSMutableOrderedSet(orderedSet: parent!.subItems!)
}

tempSet.add(newItem)
parent!.subItems = tempSet
emreoktem
  • 2,069
  • 17
  • 30
1

I have the same situation with an item called "signals" instead of "subitems". The solution with tempset works in my testing. Further, I had a problem with the removeSignals: method. This override seems to work:

- (void)removeSignals:(NSOrderedSet *)values {
    NSMutableOrderedSet* tempset = [NSMutableOrderedSet orderedSetWithOrderedSet:self.signals];
    for (Signal* aSignal in values) {
        [tempset removeObject:aSignal];
    }
    self.signals = tempset;
}

If there is a better way to do this, please let me know. My values input is never more than 10 -20 items so performance isn't much of a concern - nonetheless please point out anything relevant.

Thanks,

Damien

Damien Del Russo
  • 1,030
  • 8
  • 19
0

I found using the method by LeeIII worked, but on profiling found it was drastically slow. It took 15 seconds to parse 1000 items. Commenting out the code to add the relationship turned 15 seconds into 2 seconds.

My workaround (which is faster but much more ugly) involves creating a temporary mutable array then copying into the ordered set when all the parsing is done. (this is only a performance win if you are going to add many relationships).

@property (nonatomic, retain) NSMutableArray* tempItems;
 ....
@synthesize tempItems = _tempItems;
 ....

- (void) addItemsObject:(KDItem *)value 
{
    if (!_tempItems) {
        self.tempItems = [NSMutableArray arrayWithCapacity:500];
    }
    [_tempItems addObject:value];
}

// Call this when you have added all the relationships
- (void) commitRelationships 
{
    if (_tempItems) {
        self.items = [NSOrderedSet orderedSetWithArray:self.tempItems];
        self.tempItems = nil;
    }
}

I hope this help someone else!

Robert
  • 35,442
  • 34
  • 158
  • 205
0

Robert,

I agree your answer will work for this, but keep in mind that there is an automatically created method for adding a whole set of values to a relationship already. Apple's Documentation (as seen here under the "To-many Relationships" section or here under the "Custom To-Many Relationship Accessor Methods" section) implements them this way:

- (void)addEmployees:(NSSet *)value
{
[self willChangeValueForKey:@"employees"
      withSetMutation:NSKeyValueUnionSetMutation
      usingObjects:value];
[[self primitiveEmployees] unionSet:value];
[self didChangeValueForKey:@"employees"
      withSetMutation:NSKeyValueUnionSetMutation
      usingObjects:value];
}

- (void)removeEmployees:(NSSet *)value
{
[self willChangeValueForKey:@"employees"
      withSetMutation:NSKeyValueMinusSetMutation
      usingObjects:value];
[[self primitiveEmployees] minusSet:value];
[self didChangeValueForKey:@"employees"
      withSetMutation:NSKeyValueMinusSetMutation
      usingObjects:value];
}

You could easily compile your set of relationships outside of core data and then add them all at once using this method. It might be less ugly than the method you suggested ;)

JiuJitsuCoder
  • 1,746
  • 13
  • 13
0

I'm quite sure it is finally fixed in iOS 10 beta 6!

an0
  • 16,371
  • 11
  • 78
  • 132