Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save appleios/93f41103c8268b1c439f to your computer and use it in GitHub Desktop.
Save appleios/93f41103c8268b1c439f to your computer and use it in GitHub Desktop.
Objective-C property name to a string with autocompletion and compile-time check

Getting a string value for a property of a class with autocompletion feature and checking it at compile-time is very useful.

How to use:

Get the property name for a class:

@interface AnyClass : NSObject
@property (strong) NSData *data;
@end

// Bad approach (no autocompletion; no compile-time check):
NSString *propertyName = @"data";

// == My approach ==
// C string for a class
keypathForClass(AnyClass, data);        // ==> "data"
// NSString for a class
keypathStringForClass(AnyClass, data);  // ==> @"data"

Get the property name for a protocol:

@protocol AnyProtocol
@property (strong) NSDate *date;
@end

// C string for a protocol
keypathForProtocol(AnyProtocol, date);        // ==> "date"
// NSString for a protocol
keypathStringForProtocol(AnyProtocol, date);  // ==> @"date"

How to implement:

Just simply add the following two files in your project:

NSObject+PropertyName.h:

#import <Foundation/Foundation.h>

/* Get property name for the class (C string or NSSting). */
#define keypathForClass(Klass, PropertyName) \
        (((void)(NO && ((void)[Klass _nullObjectForCheckingPropertyName].PropertyName, NO)), # PropertyName))
#define keypathStringForClass(Klass, PropertyName) \
        @keypathForClass(Klass, PropertyName)

/* Get property name for the protocol (C string or NSSting). */
#define keypathForProtocol(Protocol, PropertyName) \
        (((void)(NO && ((void)((NSObject<Protocol> *)[NSObject _nullObjectForCheckingPropertyName]).PropertyName, NO)), # PropertyName))
#define keypathStringForProtocol(Protocol, PropertyName) \
        @keypathForProtocol(Protocol, PropertyName)


@interface NSObject (PropertyName)
+ (instancetype)_nullObjectForCheckingPropertyName;
@end

NSObject+PropertyName.m:

#import "NSObject+PropertyName.h"

@implementation NSObject (PropertyName)
+ (instancetype)_nullObjectForCheckingPropertyName;{
  return nil;
}
@end

Now you can get the property name by the macro and check the property at compile-time without any side effect.


Discussion

This gist demonstrates a simple idea using null-object pattern and the trick of keypath(...) derived from libextobjc and ReactiveCocoa to get the property name from a class.

Although the compiler should ignore ((void)(NO && ((void)<#_ObjC_Message_#>, NO)) statement at compile-time, the method +_nullObjectForCheckingPropertyName still guarantees that the getter method in <#_ObjC_Message_#> will not be invoked to prevent if from any possible side effect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment