/BloodMagic

Provides kind of custom property attributes.

Primary LanguageObjective-CMIT LicenseMIT

BloodMagic

Build status

More details here: Dependency Injection in Objective-C with Blood and Magic

Objective-C is a powerful language, but sometimes it lacks of custom property attributes, like these:

@property (nonatomic, strong, lazy) NSMutableArray *resources;

@property (nonatomic, strong, storable) NSString *authToken;

@property (nonatomic, strong, copyable) NSString *username;
@property (nonatomic, strong, copyable) NSString *email;

@property (nonatomic, strong, anything_you_want) AwesomeView *someAwesomeView;

We can't implement these attributes without hacking on clang, but fortunately, we're able to achieve these effects by means of BloodMagic' spells

Note: At this time only lazy attribute is implemented, the rest attributes will come later

Embark on the Dark

Simple installation via CocoaPods:

  pod 'BloodMagic', :git => 'https://github.com/railsware/bloodmagic'

Available Spells

BloodMagic has been designed to be extensible, so few more spells will be available soon.

Lazy initialization

Initializes object on demand.

If you use Objective-C, then you should be familiar with this code:

@interface ViewController : UIViewController

@property (nonatomic, strong) ProgressViewService *progressViewService;

@end
- (ProgressViewService *)progressViewService
{
    if (_progressViewService == nil) {
      _progressViewService = [ProgressViewService new];
    }
  
    return _progressViewService;
}

But we are able to automate this routine!

Just add BMLazy protocol to your class:

@interface ViewController : NSObject
  <BMLazy>

@property (nonatomic, strong) ProgressViewService *progressViewService;

@end

and mark any property as @dynamic:

@implementation ViewController

@dynamic progressViewService;

@end

Object progressViewService will be initialized on the first call

self.progressViewService
// or
yourViewController.progressViewService

or when you try to get value for key

[self valueForKey:@"progressViewService"]
// or
[yourViewController valueForKey:@"progressViewService"]

By default it creates an instance with the +new class' method.

In this case progressViewService will be deallocated as a usual property.

Dependency Injection (kind of)

During the creation of Lazy Initialization spell an interesting side effect was found - kind of dependency injection.

For example, if you need to initialize progressViewService in a special way, a special initializer might be created:

BMInitializer *initializer = [BMInitializer lazyInitializer];
initializer.propertyClass = [ProgressViewService class]; // optional, uses NSObject by default
initializer.containerClass = [ViewController class]; // optional, uses NSObject by default
initializer.initializer = ^id (id sender){
    return [[ProgressViewService alloc] initWithViewController:sender];
};
[initializer registerInitializer];

This spell is very useful when dealing with the singleton

BMInitializer *initializer = [BMInitializer lazyInitializer];
initializer.propertyClass = [RequestManager class];
initializer.initializer = ^id (id sender){
    static id singleInstance = nil;
    static dispatch_once_t once;
    dispatch_once(&once, ^{
      singleInstance = [RequestManager new];
    });
    return singleInstance;
};
[initializer registerInitializer];

Thus, neither the RequestManager nor the class that uses it, will not be aware about his singleton nature.

Adepts of SRP school must approve ;)

Also, you're able to use @protocols as well

BMInitializer *initializer = [BMInitializer lazyInitializer];
initializer.protocols = @[ @protocol(ProgressViewServiceProtocol) ];
initializer.initializer = ^id (id sender){
    return [[ProgressViewService alloc] initWithViewController:sender];
};
[initializer registerInitializer];

Creation of new spells

TODO

Side effects (aka bugs)

BloodMagic may have side effects, if you find one, please, open issue or send us a pull request.

Those actions will help us to protect you from mutilation.