{"id":19010287,"url":"https://github.com/railsware/bloodmagic","last_synced_at":"2025-04-05T23:08:47.519Z","repository":{"id":11168488,"uuid":"13542760","full_name":"railsware/BloodMagic","owner":"railsware","description":"BloodMagic is a framework, which gives you a way to create custom property attributes.","archived":false,"fork":false,"pushed_at":"2020-02-16T21:47:14.000Z","size":3643,"stargazers_count":316,"open_issues_count":11,"forks_count":36,"subscribers_count":30,"default_branch":"master","last_synced_at":"2025-03-29T22:06:42.850Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/railsware.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-10-13T16:42:22.000Z","updated_at":"2024-02-29T15:44:53.000Z","dependencies_parsed_at":"2022-08-30T20:50:48.878Z","dependency_job_id":null,"html_url":"https://github.com/railsware/BloodMagic","commit_stats":null,"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/railsware%2FBloodMagic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/railsware%2FBloodMagic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/railsware%2FBloodMagic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/railsware%2FBloodMagic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/railsware","download_url":"https://codeload.github.com/railsware/BloodMagic/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247411234,"owners_count":20934653,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-08T19:10:41.673Z","updated_at":"2025-04-05T23:08:47.497Z","avatar_url":"https://github.com/railsware.png","language":"Objective-C","funding_links":[],"categories":[],"sub_categories":[],"readme":"## BloodMagic \n![License MIT](https://go-shields.herokuapp.com/license-MIT-blue.png) \u0026nbsp;\n![Build status](https://api.travis-ci.org/railsware/BloodMagic.png) \u0026nbsp; ![Version](https://cocoapod-badges.herokuapp.com/v/BloodMagic/badge.png) \u0026nbsp; ![Platform](https://cocoapod-badges.herokuapp.com/p/BloodMagic/badge.png)\n\nObjective-C is a powerful language, but sometimes it lacks of custom property attributes, like these:\n\n```objectivec\n@property (nonatomic, strong, lazy) ProgressViewService *progressView;\n@property (nonatomic, strong, partial) HeaderView *headerView;\n@property (nonatomic, strong, final) NSString *almostImmutable;\n@property (nonatomic, strong, preference) NSString *authToken;\n@property (nonatomic, strong, injectable) id\u003cNetworkClient\u003e client;\n\n@property (nonatomic, strong, anything_you_want) AwesomeView *someAwesomeView;\n```\n\nWe can't implement these attributes without hacking on `clang`, but fortunately, we're able to achieve these effects by means of BloodMagic' spells\n\n[FAQ](https://github.com/railsware/BloodMagic/blob/master/FAQ.md)\n\n[Blog-post](http://l.rw.rw/dibm)\n\n[Presentation](https://speakerdeck.com/alexdenisov/bloodmagic) by [AlexDenisov](https://github.com/AlexDenisov)\n\n[Presentation](https://speakerdeck.com/0xc010d/dependency-injection-ftw) by [Ievgen Solodovnykov](https://github.com/0xc010d)\n\n### Embark on the Dark\n\n#### [CocoaPods](http://cocoapods.org/):\n\n```ruby\n  pod 'BloodMagic', :git =\u003e 'https://github.com/railsware/BloodMagic.git'\n```\n\n#### [Components](https://github.com/AlexDenisov/Components)\n\n```bash\n$ mkdir -p ./Components.make\n# iOS\nwget https://raw.githubusercontent.com/AlexDenisov/Components/master/Components.make/BloodMagic/1.0.0/BloodMagic-iOS.make -O ./Components.make/BloodMagic-iOS.make\n# OSX\nwget https://raw.githubusercontent.com/AlexDenisov/Components/master/Components.make/BloodMagic/1.0.0/BloodMagic-OSX.make -O ./Components.make/BloodMagic-OSX.make\n```\n\n#### Manually\n\nAlternatively you can use built frameworks for [iOS](https://github.com/railsware/BloodMagic/releases/download/1.0.0/BloodMagic-iOS-1.0.0.zip) and [OSX](https://github.com/railsware/BloodMagic/releases/download/1.0.0/BloodMagic-OSX-1.0.0.zip).\n\nJust drag\u0026drop framework into your project and don't forget to add `-all_load`, `-ObjC` and `-lc++` or `-lstdc++` to `OTHER_LINKER_FLAGS`\n\n### Available Spells\n\n[Lazy Initialization](https://github.com/railsware/BloodMagic#lazy-initialization)\n\n[Dependency Injection](https://github.com/railsware/BloodMagic#dependency-injection)\n\n[Partial Views](https://github.com/railsware/BloodMagic#partial-views)\n\n[Assign-once properties](https://github.com/railsware/BloodMagic#assign-once-properties)\n\n[Preferences (NSUserDefaults wrapper)](https://github.com/railsware/BloodMagic#preferences)\n\nBloodMagic has been designed to be extensible, so few more spells will be available soon.\n\n====\n\n#### Lazy initialization\n\n```ruby\n  pod 'BloodMagic/Lazy', :git =\u003e 'https://github.com/railsware/BloodMagic.git'\n```\n\nInitializes object on demand.\n\nIf you use Objective-C, then you should be familiar with this code:\n\n```objectivec\n@interface ViewController : UIViewController\n\n@property (nonatomic, strong) ProgressViewService *progressViewService;\n\n@end\n```\n\n```objectivec\n- (ProgressViewService *)progressViewService\n{\n    if (_progressViewService == nil) {\n      _progressViewService = [ProgressViewService new];\n    }\n  \n    return _progressViewService;\n}\n```\n\nBut we are able to automate this routine!\n\nJust add `BMLazy` protocol to your class:\n\n```objectivec\n@interface ViewController : NSObject\n  \u003cBMLazy\u003e\n\n@property (nonatomic, strong, bm_lazy) ProgressViewService *progressViewService;\n\n@end\n```\n\nand mark any property as `@dynamic`:\n\n```objetivec\n@implementation ViewController\n\n@dynamic progressViewService;\n\n@end\n```\n\nObject `progressViewService` will be initialized on the first call \n```objectivec\nself.progressViewService\n// or\nyourViewController.progressViewService\n```\n\nor when you try to get value for key\n\n```objectivec\n[self valueForKey:@\"progressViewService\"]\n// or\n[yourViewController valueForKey:@\"progressViewService\"]\n```\n\nBy default it creates an instance with the `+new` class' method.\n\nIn this case `progressViewService` will be deallocated as a usual property.\n\n#### Dependency Injection\n\n```ruby\n  pod 'BloodMagic/Injectable', :git =\u003e 'https://github.com/railsware/BloodMagic.git'\n```\n\nDuring the creation of `Lazy Initialization` spell an interesting side effect was found - Dependency Injection.\n\nIt behaves the same way as `BMLazy`, but uses another approach to instantiate object.\n\nFor example, if you need to initialize `progressViewService` in a special way, you should provide initializer:\n\n```objectivec\nBMInitializer *initializer = [BMInitializer injectableInitializer];\ninitializer.propertyClass = [ProgressViewService class]; // optional, uses NSObject by default\ninitializer.containerClass = [ViewController class]; // optional, uses NSObject by default\ninitializer.initializer = ^id (id sender){\n    return [[ProgressViewService alloc] initWithViewController:sender];\n};\n[initializer registerInitializer];\n```\n\n_Note:_ `containerClass` doesn't apply on derived classes, to achieve such behavior you should specify `containerClass` explicitly.\n\nThis spell is very useful when dealing with the singleton\n\n```objectivec\nBMInitializer *initializer = [BMInitializer injectableInitializer];\ninitializer.propertyClass = [RequestManager class];\ninitializer.initializer = ^id (id sender){\n    static id singleInstance = nil;\n    static dispatch_once_t once;\n    dispatch_once(\u0026once, ^{\n      singleInstance = [RequestManager new];\n    });\n    return singleInstance;\n};\n[initializer registerInitializer];\n```\n\nThus, neither the `RequestManager` nor the class that uses it, will not be aware about his singleton nature.\n\nAdepts of [SRP](http://en.wikipedia.org/wiki/Single_responsibility_principle) school must approve ;)\n\nAlso, you're able to use `@protocol`s as well\n\n```objectivec\nBMInitializer *initializer = [BMInitializer injectableInitializer];\ninitializer.protocols = @[ @protocol(ProgressViewServiceProtocol) ];\ninitializer.initializer = ^id (id sender){\n    return [[ProgressViewService alloc] initWithViewController:sender];\n};\n[initializer registerInitializer];\n```\n\n##### Injection hooks\n\n`BMInjectable` module provides a hook system to catch the object creation.\nTo enable these hooks just create instance method named `propertyNameInjected:`.\n\nFor example:\n```objectivec\n@implementation ViewController\n\n@injectable(progressViewService)\n\n- (void)progressViewServiceInjected:(ProgressViewService *service)\n{\n    service.title = self.title;\n}\n\n@end\n```\n\n#### Partial Views\n\n```ruby\n  pod 'BloodMagic/Partial', :git =\u003e 'https://github.com/railsware/BloodMagic.git'\n```\n\nInstantiates view from `xib` on demand, similar to `Lazy` module.\nThis spell might be helpful if you have reusable views.\n\nFor example:\n\nYou need to show the same user info in table cells (`UsersListViewController`) and in some header view (`UserProfileViewController`).\nIt makes sense to create one `UserView.xib` associated with `UserView` class and use it through the whole app.\n\nSo it may looks like this:\n\n```objectivec\n\n// Cell used from UsersListViewController\n// Created manually\n@implementation UserViewCell\n{\n    UserView *_userView;\n}\n\n- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier\n{\n    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];\n    if (self) {\n        NSString *nibName = NSStringFromClass([UserView class]);\n        UINib *nib = [UINib nibWithNibName:nibName bundle:nil];\n        _userView = [[nib instantiateWithOwner:nil options:nil] lastObject];\n        [self addSubview:_userView];\n    }\n    return self;\n}\n\n@end\n\n// View used from UserProfileViewController\n// Created from xib\n@implementation UserHeaderView\n{\n    UserView *_userView;\n}\n\n- (void)awakeFromNib\n{\n    [super awakeFromNib];\n    NSString *nibName = NSStringFromClass([UserView class]);\n    UINib *nib = [UINib nibWithNibName:nibName bundle:nil];\n    _userView = [[nib instantiateWithOwner:nil options:nil] lastObject];\n    [self addSubview:_userView];\n}\n\n@end\n```\n\nBoth cases use the same, similar code.\nSo, BloodMagic does nothing special, just hides this boilerplate:\n\n```objectivec\n\n#import \u003cBloodMagic/Partial.h\u003e\n\n@interface UserViewCell ()\n    \u003cBMPartial\u003e\n\n@property (nonatomic, strong, bm_partial) UserView *userView;\n\n@end\n\n@implementation UserViewCell\n\n@dynamic userView;\n\n- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier\n{\n    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];\n    if (self) {\n        [self addSubview:self.userView];\n    }\n    return self;\n}\n\n@end\n\n// ...\n\n@interface UserHeaderView ()\n    \u003cBMPartial\u003e\n\n@property (nonatomic, strong, bm_partial) UserView *userView;\n\n@end\n\n@implementation UserHeaderView\n\n@dynamic userView;\n\n- (void)awakeFromNib\n{\n    [super awakeFromNib];\n    [self addSubview:self.userView];\n}\n\n@end\n```\n\n#### Assign-once properties\n\n```ruby\n  pod 'BloodMagic/Final', :git =\u003e 'https://github.com/railsware/BloodMagic.git'\n```\n\nJava provides [final](http://en.wikipedia.org/wiki/Final_(Java)) keyword, which determines (at least) that value can't be changed after initialization.\n\nFrom now this feature available in Objective-C, via BloodMagic.\n\n```objectivec\n\n#import \u003cBloodMagic/Final.h\u003e\n\n@interface FinalizedObject : NSObject\n    \u003cBMFinal\u003e\n\n@property (nonatomic, strong, bm_final) NSString *almostImmutableProperty;\n\n@end\n\n@implementation FinalizedObject\n\n@dynamic almostImmutableProperty;\n\n@end\n\n// ...\n\nFinalizedObject *object = [FinalizedObject new];\nobject.almostImmutableProperty = @\"Initial value\"; // everything is fine\nobject.almostImmutableProperty = @\"Another value\"; // exception will be thrown\n\n```\n\n#### Preferences\n\n```ruby\npod 'BloodMagic/Preference', :git =\u003e 'https://github.com/railsware/BloodMagic.git'\n```\n\nEnjoy the simplest way to deal with `NSUserDefaults`\n\n```objectivec\n#import \u003cBloodMagic/Preference.h\u003e\n\n@interface Settings : NSObject\n    \u003cBMPreference\u003e\n\n@property (nonatomic, strong, bm_preference) NSString *nickname;\n\n@end\n\n@implementation Settings\n\n@dynamic nickname;\n\n@end\n\n// ...\n\nSettings *settings = [Settings new];\nsettings.nickname = @\"AlexDenisov\"; // @\"AlexDenisov\" goes to [NSUserDefaults standardUserDefaults] with key \"nickname\"\nNSLog(@\"My name is: %@\", settings.nickname); // reads object for key \"nickname\" from [NSUserDefaults standardUserDefaults]\n```\n\n### Side effects (aka bugs)\n\nBloodMagic may have side effects, if you find one, please, open issue or send us a pull request.\n\nThose actions will help us to protect you from mutilation.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frailsware%2Fbloodmagic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frailsware%2Fbloodmagic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frailsware%2Fbloodmagic/lists"}