Skip to content

Instantly share code, notes, and snippets.

@smileyborg
Last active May 26, 2020 12:08
Show Gist options
  • Select an option

  • Save smileyborg/d513754bc1cf41678054 to your computer and use it in GitHub Desktop.

Select an option

Save smileyborg/d513754bc1cf41678054 to your computer and use it in GitHub Desktop.
Backwards compatible macros for Objective-C nullability annotations and generics
/**
* The following preprocessor macros can be used to adopt the new nullability annotations and generics
* features available in Xcode 7, while maintaining backwards compatibility with earlier versions of
* Xcode that do not support these features.
*/
#if __has_feature(nullability)
# define __ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
# define __ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
# define __NULLABLE nullable
#else
# define __ASSUME_NONNULL_BEGIN
# define __ASSUME_NONNULL_END
# define __NULLABLE
#endif
#if __has_feature(objc_generics)
# define __GENERICS(class, ...) class<__VA_ARGS__>
# define __GENERICS_TYPE(type) type
#else
# define __GENERICS(class, ...) class
# define __GENERICS_TYPE(type) id
#endif
/**
* Here are some examples of the above macros being used.
* The comments below each line represent the output of the preprocessor (e.g. what the compiler will see)
* when using both Xcode 7 and Xcode 6.
*/
#import "Xcode7Macros.h"
__ASSUME_NONNULL_BEGIN
// Xcode 7: NS_ASSUME_NONNULL_BEGIN
// Xcode 6:
@interface __GENERICS(MyClass, GenericType1, GenericType2) : NSObject
// Xcode 7: @interface MyClass<GenericType1, GenericType2> : NSObject
// Xcode 6: @interface MyClass : NSObject
@property (nonatomic, strong) __GENERICS(NSArray, __GENERICS(NSDictionary, GenericType1, GenericType2) *) *arrayOfDictionaries;
// Xcode 7: @property (nonatomic, strong) NSArray<NSDictionary<GenericType1, GenericType2> *> *arrayOfDictionaries;
// Xcode 6: @property (nonatomic, strong) NSArray *arrayOfDictionaries;
- (__NULLABLE __GENERICS_TYPE(GenericType2))someMethodWithAParameter:(__NULLABLE NSString *)param;
// Xcode 7: - (nullable GenericType2)someMethodWithAParameter:(nullable NSString *)param;
// Xcode 6: - (id)someMethodWithAParameter:(NSString *)param;
@end
__ASSUME_NONNULL_END
// Xcode 7: NS_ASSUME_NONNULL_END
// Xcode 6:
@ricardopereira

Copy link
Copy Markdown

What about value type definitions for NSArray or key value types for NSDictionary that are only available for Xcode 7? Do you have any idea?

e.g.:

// Xcode 6
NSArray *authParams; //<-- will produce AnyObject

// Xcode 7
NSArray<NSURLQueryItem *> *authParams;

@ricardopereira

Copy link
Copy Markdown

Using __apple_build_version__ for the check? What do you think?

// http://en.wikipedia.org/wiki/Xcode#Toolchain_Versions
if defined(__apple_build_version__)
     if __apple_build_version__ >= 700059

@smileyborg

Copy link
Copy Markdown
Author

@ricardopereira I'm not sure what you're asking here.

@ricardopereira

Copy link
Copy Markdown

I want to support NSArray *authParams and NSArray<NSURLQueryItem *> *authParams. Any thoughts? /cc @bdash @mickeyreiss

@smileyborg

Copy link
Copy Markdown
Author

@ricardopereira and why doesn't this work for you?

__GENERICS(NSArray, NSURLQueryItem *) *authParams;

@ricardopereira

Copy link
Copy Markdown

Oh, sorry. Didn't know.
Thanks.

@colasbd

colasbd commented Oct 20, 2015

Copy link
Copy Markdown

Xcode inserts automatically code such as

saveOperation.modifyRecordsCompletionBlock = 
^(NSArray <CKRecord *> * _Nullable savedRecords,
  NSArray <CKRecordID *> * _Nullable deletedRecordIDs,
  NSError * _Nullable operationErro) {...};

which fails at being compiled with Xcode 6, because of NSArray <CKRecord *> *. Is it possible to help Xcode 6 compiling?

@smileyborg

Copy link
Copy Markdown
Author

@colasjojo You can probably just omit (delete) the _Nullable and <CKRecord *>/<CKRecordID *> entirely and it should continue to compile fine on Xcode 7 with no change in behavior. If you want to preserve the type information then you should try converting the syntax to use the macros in this gist, something like:

saveOperation.modifyRecordsCompletionBlock = 
^(__GENERICS(NSArray, CKRecord *) * _Nullable savedRecords,
  __GENERICS(NSArray, CKRecordID *) * _Nullable deletedRecordIDs,
  NSError * _Nullable operationErro) {...};

(You would need to also define a new macro for _Nullable and substitute that if you're trying to support Xcode 6.2 and earlier.)

@samjarman

Copy link
Copy Markdown

@smileyborg I believe the macro breaks bridging into swift?

@smileyborg

Copy link
Copy Markdown
Author

@samjarman They shouldn't, these are being used by projects that bridge to Swift. Are you seeing a specific issue?

@hrieke

hrieke commented May 26, 2020

Copy link
Copy Markdown

Very cool, do you have a license for your code that we can use?
Thank you.

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