23

When I create a new project with Xcode 4.4 and add these lines:

NSDictionary *test = @{ @"key" : @"test value" };
NSString *value = test[@"key"];
NSLog(@"value is: %@", value);

it compiles with no warnings and executes as expected.

Adding the same lines to an existing project produces the compiler error:

NSString *value = test[@"key"]; <-- Expected method to read dictionary element not found on object of type 'NSDictionary *'

I compared both projects' target build settings but nothing leapt out at me.

Update: The new project that successfully compiled was for OSX. I tried another new one for iOS with the above lines and it fails to compile, same as my pre-existing (iOS) project.

darrinm
  • 9,117
  • 5
  • 34
  • 34

1 Answers1

48

This has nothing to do with old vs. new project, but rather is a factor of the SDK you use. The problem you're running into is that while this is a compiler feature, it requires SDK support. The iOS 5 SDK does not provide that support, though the iOS 6 SDK does.

For that reason, now you should just use the iOS 6 SDK. Read on if you want to use object subscripting with the iOS 5 SDK.

All you need to do is add a header file so that the compiler will try the call. There's no need to add an implementation; it's handled automatically by arclite. (If you are not using ARC, you will have to force the linker to include arclite. But you still don't have to actually switch to it.)

Create a new interface file, NSObject+subscripts.h.

#if __IPHONE_OS_VERSION_MAX_ALLOWED < 60000
@interface NSDictionary(subscripts)
- (id)objectForKeyedSubscript:(id)key;
@end

@interface NSMutableDictionary(subscripts)
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;
@end

@interface NSArray(subscripts)
- (id)objectAtIndexedSubscript:(NSUInteger)idx;
@end

@interface NSMutableArray(subscripts)
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx;
@end
#endif

I've put this chunk on github.

Note: I used to suggest adding the required methods to NSObject before explaining how to add them only to the relevant objects. In retrospect, I believe this was an error on my part; it led to errors being caught at runtime rather than compile time, unlike the approach now presented here. That approach is still on my blog, but I now believe it to be more of a cool hack than a useful approach.

Source:

Steven Fisher
  • 44,462
  • 20
  • 138
  • 192
  • That did the job for me, thanks! It'll be fine until Xcode 4.5 ships with the official iOS support. – darrinm Jul 26 '12 at 07:05
  • I only added it on NSDictionary / NSArray and implemented it to call the regular methods. It works just fine (that's what ios 6 does anyway). – borrrden Jul 26 '12 at 14:25
  • You don't need an @implementation. But yeah, today I had no problem adding just the categories I needed to just the classes I wanted. I'll update the answer. – Steven Fisher Jul 26 '12 at 15:35
  • Anyone have any idea how Apple would feel about this in an app submission? As I understand it, ARClite actually adds the necessary implementations itself, so from Apple's point of view the methods `objectAtIndexedSubscript:` etc are not undocumented private methods but user-defined methods, correct? – Anshu Chimala Jul 27 '12 at 08:49
  • Correct. You're making calls to arclite. I haven't gone through App Review yet, but there's no other point to Apple putting those swizzles in arclite than for us to use them. – Steven Fisher Jul 27 '12 at 15:38
  • how to force the linker to include arclite? – Kreiri Jan 23 '13 at 11:39
  • Turn on ARC in the target's build settings. – Steven Fisher Jan 23 '13 at 17:25
  • To clarify, you saying that in order to use literals in a non-arc project, I must turn on arc and put the linker flags on all my files to not use arc? This does not sound efficient. – AddisDev Jul 19 '13 at 16:12
  • No, just turn on ARC for the linker only. I used to know how to do this, but don't anymore. Sorry. :) I think it might have been OTHER_LDFLAGS. – Steven Fisher Jul 19 '13 at 16:21
  • 1
    Also even using a newer SDKs will give you problems on iOS 5 if you try to use subscripting from a class' `+(void)load` method! It seems that arclite is not yet loaded at that time. – Rivera Oct 17 '13 at 10:22