Git Product home page Git Product logo

objection's Introduction

Build Status

Description

Objection is a lightweight dependency injection framework for Objective-C for MacOS X and iOS. For those of you that have used Guice, Objection will feel familiar. Objection was built to stay out of your way and alleviate the need to maintain a large XML container or manually construct objects.

Features

  • "Annotation" Based Dependency Injection
  • Seamless support for integrating custom and external dependencies
    • Custom Object Providers
    • Meta Class Bindings
    • Protocol Bindings
    • Instance Bindings
    • Named Bindings
  • Lazily instantiates dependencies
  • Eager Singletons
  • Initializer Support
    • Default and custom arguments

Using Objection

For questions, visit the mailing list

Basic Usage

A class can be registered with objection using the macros objection_register (optional) or objection_register_singleton. The objection_requires macro can be used to declare what dependencies objection should provide to all instances it creates of that class. objection_requires can be used safely with inheritance.

Example

@class Engine, Brakes;

@interface Car : NSObject
{
  Engine *engine;
  Brakes *brakes;
  BOOL awake;  
}

// Will be filled in by objection
@property(nonatomic, strong) Engine *engine;
// Will be filled in by objection
@property(nonatomic, strong) Brakes *brakes;
@property(nonatomic) BOOL awake;

@implementation Car
objection_requires(@"engine", @"brakes")
@synthesize engine, brakes, awake;
@end

Defining dependencies with selectors

You can alternatively use selectors to define dependencies. The compiler will generate a warning if a given selector is not visible or cannot be found.

Example

@implementation Car
objection_requires_sel(@selector(engine), @selector(brakes))
@synthesize engine, brakes, awake;
@end

Fetching Objects from Objection

An object can be fetched from objection by creating an injector and then asking for an instance of a particular class or protocol. An injector manages its own object context. Which means that a singleton is per injector and is not necessarily a true singleton.

- (void)someMethod {
  JSObjectionInjector *injector = [JSObjection createInjector];
  id car = [injector getObject:[Car class]];
}

A default injector can be registered with Objection which can be used throughout your application or library.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
  JSObjectionInjector *injector = [JSObjection createInjector];
  [JSObjection setDefaultInjector:injector];
}

- (void)viewDidLoad {
  id myModel = [[JSObjection defaultInjector] getObject:[MyModel class]];
}

Injecting dependencies

There may be instances where an object is allocated outside of the injector's life cycle. If the object's class declared its dependencies using objection_requires an injector can satisfy its dependencies via the injectDependencies: method.

@implementation JSTableModel
objection_requires(@"RESTClient")
- (void)awakeFromNib {
  [[JSObjection defaultInjector] injectDependencies:self];
}
@end

Subscripting

Objection has support for the subscripting operator to retrieve objects from the injection context.

- (void)someMethod {
  JSObjectionInjector *injector = [JSObjection createInjector];
  id car = injector[[Car class]];
}

Awaking from Objection

If an object is interested in knowing when it has been fully instantiated by objection it can implement the method awakeFromObjection.

Example

@implementation Car
//...
objection_register_singleton(Car)
  - (void)awakeFromObjection {
    awake = YES;
  }
@end  

Object Factory

A class can get objects from the injector context through an object factory.

Example

@interface RequestDispatcher
@property(nonatomic, strong) JSObjectFactory *objectFactory
@end

@implementation RequestDispatcher
- (void)dispatch:(NSDictionary *)params
{
  Request *request = [self.objectFactory getObject:[Request class]];
  request.params = params;
  [request send];
}
@end

Modules

A module is a set of bindings which contributes additional configuration information to the injector. It is especially useful for integrating external depencies and binding protocols to classes or instances.

Instance and Protocol Bindings

  • Bind a protocol or class to a specific instance of that type
  • Bind a class that is registered with Objection to a protocol

Example

@interface MyAppModule : JSObjectionModule {
  
}
@end

@implementation MyAppModule
- (void)configure {
  [self bind:[UIApplication sharedApplication] toClass:[UIApplication class]];
  [self bind:[UIApplication sharedApplication].delegate toProtocol:@protocol(UIApplicationDelegate)];
  [self bindClass:[MyAPIService class] toProtocol:@protocol(APIService)];
}

@end
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
  JSObjectionInjector *injector = [JSObjection createInjector:[[MyAppModule alloc] init]];
  [JSObjection setDefaultInjector:injector];
}

Meta Class Bindings

There are times when a dependency -- usually external -- is implemented using only class methods. Objection can explicitly support binding to the meta class instance through a protocol. This avoids having to unnecessarily create a wrapper class that passes through to the class methods. The catch, of course, is that it requires a protocol definition so that Objection knows how to bind the meta class to objects in the injector context.

Example

@protocol ExternalUtility
  - (void)doSomething;
@end

@interface ExternalUtility
  + (void)doSomething;
@end

@implementation ExternalUtility
  + (void)doSomething {...}
@end

// Module Configuration
- (void)configure {
  [self bindMetaClass:[ExternalUtility class] toProtocol:@protocol(ExternalUtility)];    
}

@interface SomeClass
{
  ...
}
// Use 'assign' because a meta class is not subject to the normal retain/release lifecycle. 
// It will exist until the application is terminated (Class Initialization -> Application Termination)
// regardless of the number of objects in the runtime that reference it.
@property (nonatomic, assign) id<ExternalUtility> externalUtility
@end

Providers

Occasionally you'll want to manually construct an object within Objection. Providers allow you to use a custom mechanism for building objects that are bound to a type. You can create a class that conforms to the ObjectionProvider protocol or you can use a block to build the object.

Example

@interface CarProvider : NSObject <JSObjectionProvider>
@end

@implementation CarProvider
- (id)provide:(JSObjectionInjector *)context arguments:(NSArray *)arguments {
  // Manually build object
  return car;
}
@end

@implementation MyAppModule
- (void)configure {
    [self bindProvider:[[CarProvider alloc] init] toClass:[Car class]];
    [self bindBlock:^(JSObjectionInjector *context) {
      // Manually build object
      return car;          
    } toClass:[Car class]];
}
@end

Scopes

A class can be scoped as a singleton in a module. Conversely, a registered singleton can be demoted to a normal lifecycle with in the injector's context.

Example

@implementation MyAppModule
- (void)configure {
    [self bindClass:[Singleton class] inScope:JSObjectionScopeNormal];
    [self bindClass:[Car class] inScope:JSObjectionScopeSingleton];
}
@end

Named Bindings

Dependencies of the same class or protocol can be identified using the objection_requires_names macro, which takes a dictionary of names to properties as a parameter.

Example

@interface ShinyCar : NSObject
@property (nonatomic, strong) Headlight *leftHeadlight;
@property (nonatomic, strong) Headlight *rightHeadlight;
@end

@implementation ShinyCar
objection_register(ShinyCar)
objection_requires_names((@{@"LeftHeadlight":@"leftHeadlight", @"RightHeadlight":@"rightHeadlight"}))
@synthesize leftHeadlight, rightHeadlight;
@end

@implementation NamedModule

- (void)configure
{
    [self bind:[[Headlight alloc]init] toClass:[Headlight class] named:@"RightHeadlight"];
    [self bindClass:[HIDHeadlight class] toClass:[Headlight class] named:@"LeftHeadlight"];

}
@end

Eager Singletons

You can mark registered singleton classes as eager singletons. Eager singletons will be instantiated during the creation of the injector rather than being lazily instantiated.

Example

@implementation MyAppModule
- (void)configure {
  [self registerEagerSingleton:[Car class]];
}

@end

Deriving a new injector from an existing injector

A new injector can be created from an existing injector using the withModule: method. A new injector will be created containing the same bindings as the injector it was derived from. The new injector will also contain additional bindings provided by the new module.

Conversley, if withoutModuleOfType: is used the new injector will not contain the bindings of the removed module.

Example

injector = [otherInjector withModule:[[Level18Module alloc] init]] 
                          withoutModuleOfType:[Level17Module class]];
                          

Initializers

By default, Objection allocates objects with the default initializer init. If you'd like to instantiate an object with an alternate ininitializer the objection_initializer macro can be used to do so. The macro supports passing in default arguments (scalar values are not currently supported) as well.

Default Arguments Example

@implementation ViewController
objection_initializer(initWithNibName:bundle:, @"ViewController")
@end

Custom Arguments Example

@implementation ConfigurableCar
objection_requires(@"engine", @"brakes")
objection_initializer(initWithMake:model:)

@synthesize make;
@synthesize model;

- (instancetype)initWithMake:(NSString *)make model:(NSString *)model {
  ...
}
@end

- (void)buildCar {
  ConfigurableCar *car = [self.objectFactory getObjectWithArgs:[ConfigurableCar class], @"VW", @"Passat", nil];
  NSLog(@"Make: %@ Model: %@", car.make, car.model);
}

Class Method Initializer

@implementation Truck
objection_requires(@"engine", @"brakes")
objection_initializer(truckWithMake:model:)
+ (instancetype)truckWithMake:(NSString *) make model: (NSString *)model {
  ...
}
@end

Ad-Hoc Initializer

@implementation ConfigurableCar
- (instancetype) initWithModel:(NSString *)model {
    //....
}
@end

- (void)buildCar {
  ConfigurableCar *car = [self.objectFactory getObject:[ConfigurableCar class], 
                                           initializer: @selector(initWithModel:) 
                                           withArgumentList:@[@"Passat"]];
}

Testing

If you're using Kiwi for testing, checkout MSSpec. It provides a convenient way inject mocks into your specs using Objection.

TODO

  • Add a motivation section that speaks to why Objection was created

Installation

Static Framework and Linkable Framework

It can be downloaded here

Building Static Framework

git clone git://github.com/atomicobject/objection.git
git checkout 1.6.1

iOS

  1. rake artifact:ios
  2. cp -R build/Release-iphoneuniversal/Objection-iOS.framework ${DEST_DIR}
  3. In XCode -> Project Icon -> Your Target -> Build Phases -> Link Binary With Libraries -> Add (+) -> Add Other
  4. Add -ObjC and -all_load to Other Link Flags in your project

Include framework

#import <Objection-iOS/Objection.h>

MacOS X

  1. rake artifact:osx
  2. cp -R build/Release/Objection.framework ${DEST_DIR}
  3. In XCode -> Project Icon -> Your Target -> Build Phases -> Link Binary With Libraries -> Add (+) -> Add Other

Include framework

#import <Objection/Objection.h>

CocoaPods

Edit your Pofile

edit Podfile
pod 'Objection', '1.6.1'

Now you can install Objection

pod install

Include framework

#import <Objection/Objection.h>

Learn more at CocoaPods.

Ruby Motion

A companion library for Objection was created called motion-objection

gem install motion-objection

Requirements

  • MacOS X 10.8 +
  • iOS 7.0 +

Authors

Other Dependency Injection Libraries

One only has to search GitHub

Applications that use Objection

objection's People

Contributors

0oneo avatar alloy avatar beholdr avatar fletcherm avatar jdewind avatar jonwallg avatar mpurland avatar nachosoto avatar nickynick avatar patspam avatar plu avatar sethew avatar shawn42 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

objection's Issues

Reseting Objection's global context storage caches

Hi folks, I ran across a case where Objection doesn't give me the control I need during testing. I'm curious if anyone else has seen the same issue and if I can propose an appropriate solution in line with the design of the rest of the framework.

I'm writing integration/functional tests where I deliberately exercise many classes, several of which have been instantiated via Objection. Some of these classes are registered with Objection as singletons (via objection_register_singleton()) and as such multiple other classes might have been given references to the same instance.
These singletons introduce our old enemy global mutable state. I want to make sure that state is reset between test cases so that the order in which tests are run cannot influence their outcome (for example a queue of pending network requests might need to be emptied between tests).
As is I might manually reset these states but that requires that my test be aware of, able to access, and coupled to all of these dependencies. I would rather not tightly couple my test of a high level interface to implementation details of classes deep in the object graph the test produces.
Alternately I see [JSObjection reset] which clears the global context. Unfortunately this destroys the JSObjectionInjectorEntrys in that context and the scope with which they were registered as well. Since objection_register_singleton implements +initialize I can't easily re-run those registration calls. As a result classes which were once singletons no longer are.
I considered creating a new JSObjectionInjector instance without the same global context but allowing an injector's global context to not match JSObjection's gObjectionContext seems like a violation of Objection's expected behavior. In addition there's no public mechanism to obtain a JSObjectionInjector's set of modules so I cannot currently init a new injector with the same module set but an empty context.
I can obviously reach into Objection's internals to make this work for now but is there an appropriate public interface we could expose to solve this problem?

What is the intended use of [JSObjection reset]? Is it important that it clear the scope with which classes are registered? Is so does it seem appropriate to introduce a new method which discards the _storageCache from each JSObjectionInjectorEntry in the JSObjection gObjectionContext?

As an example I'd like to be able to write something like:

@interface TestObserver : XCTestObserver
@end

@implementation TestObserver

- (void)testCaseDidStart:(XCTestRun *)testRun {
    [JSObjection resetSingletons];
    [super testCaseDidStart:testRun];
}

@end

how to deal with multi initializers?

suppose I have a ViewController which can init in multi ways like -initWithCategory:, -initWithTag:, etc. how to deal this scenario? cause objection_initializer seems can be used once.

use one init and multi -configureWithCategory:, -configureWithTag: ?

EXEC_BAD_ACCESS on cyclic reference requirement

So, I'm getting an EXEC_BAD_ACCESS on this situation: AuthenticationService -> SingletonHttpClient -> AuthenticationService (-> denotes requires)

@implementation AuthenticationService
objection_requires_sel(@selector(singletonHttpClient))
@end

@implementation SingletonHttpClient
objection_register_singleton(TTTophatService)
objection_requires_sel(@selector(authenticationService)) // EXEC_BAD_ACCESS here
@end

Do you think it is possible to Objection to support this kind of dependency?

FYI if I register AuthenticationService as singleton the crash stops but the AuthenticationService.singletonHttpClient instance does not match the singleton instance that everybody else gets. Besides the fact that it shouldn't be singleton so not a real solution.

Fetching Objects from Objection

Hi,

I have following use case:

@protocol IFoo <NSObject>
-(void)doFoo; 
@end

@interface Foo : NSObject <IFoo> {
    Bar* _bar;
}
@property (nonatomic, retain) Bar* bar;
@end

@implementation Foo
objection_register(Foo)
objection_requires(@"bar")

@synthesize bar = _bar;

-(void)doFoo {
}
@end

@interface Bar : NSObject {
}
-(void)doBar;
@end

@implementation Bar
-(void)doBar {
}
@end

[self bind:[[Foo alloc] init] toProtocol:@protocol(IFoo)];

If I'm asking the injector for an implementation of IFoo:

[[JSObjection globalInjector] getObject:@protocol(IFoo)];

then an instance of Bar will not be injected by the injector.
But if I'm asking for a class:

[[JSObjection globalInjector] getObject:[Foo class]];

then Bar will be injected.

Is that the expected behavior?

Thanks,
Thomas Ziem

Inject into Object

There are some situations where the creation of an object is inaccessible because of framework architecture, for example the creation of UIStoryboard occurs in a static/class method. Subclassing it won't help as ultimately we have no access to what happens in that method and can't replicate the creation in an overridden method without risking bypassing crucial setup code. There are plenty of other instances where we would still want to use injection, but can't rely on the injector creating the object.

For this reason it would be hugely useful to be able to pass an object to Objection and ask for injection to occur into the already created object. This functionality already exists in JSObjectionInjectorEntry::buildObject, though it is currently obscured from usage.

Essentially I am asking for something like:

JSObjectionInjector::injectIntoObject:(id)object

Detect circular dependencies and throw an exception

Currently the library does not detect circular dependencies which means that the library will go into infinite recursion until it encounters a stack overflow error. At the very least we should detect circular dependencies and throw an exception.

Add bindings to live Injector

There is a way to add / change bindings on live injector? Or create new injector with old and additional context?

I have to refactor some of my code, and now database classes are dependent on username.
All across the code i use id and id protocols, and now i need to change this binding at program runtime? There is a way to do this?

reset global JSObjectionInjector won't dealloc old injector

When try to recreate a new injector and set it as new default injector, the old injector won't be deallocated. Once I changed the reference type of property injector from strong to weak/assign in class JSObjectFactory, it fixed dealloc issue. Is it a bug? Or it is supposed to work like that?

Cheers

Support for optional Injection Points

It would be very useful to have the option of marking requirements as optional. At the moment if a dependency is not satisfied Objection throws an exception, however it should be able to check whether the dependency was optional or not and suppress the exception if it was.

DefaultInjector not shared between app and Kiwi

I've got the same problem as it was mentioned here: #50

Because no one have answered my question from comment to that issue, I'm creating new one.

How should I use Objection and cocoapods to the same defaultInjector in tests and main target?

defaultInjector not shared between app and OCUnit

I am trying to use Objection to mock out a few components in my OCUnit tests, but I'm running into a problem where [JSObjection defaultInjector] returns a different value in the tests than the app.

In my app, I have:

[JSObjection setDefaultInjector:[JSObjection createInjector]];
NSLog(@"Main binary injector: %p", [JSObjection defaultInjector]);

double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    NSLog(@"Main binary injector after change: %p", [JSObjection defaultInjector]);
});

And in my test I have:

NSLog(@"ocunit initial injector: %p", [JSObjection defaultInjector]);
[JSObjection setDefaultInjector:[JSObjection createInjector]];
NSLog(@"ocunit injector: %p", [JSObjection defaultInjector]);
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 5, NO);

The flow is basically, that the app sets the default injector and the tests then reads it. After that, the test changes the default injector and the then app reads it.

I would expect the output to be two matching pointers, followed by two different matching pointers. What I get is:

2013-08-14 23:05:22.939 ObjectionDefect[96881:c07] Main binary injector: 0xe793850
2013-08-14 23:05:23.045 ObjectionDefect[96881:c07] ocunit initial injector: 0x0
2013-08-14 23:05:23.045 ObjectionDefect[96881:c07] ocunit injector: 0x7491230
2013-08-14 23:05:24.940 ObjectionDefect[96881:c07] Main binary injector after change: 0xe793850

A little probing reveals that &gGlobalInjector is different for the two binaries.

I am using Objection 1.0.3 via CocoaPods. I had used a previous version as a subproject in the past without issue.

Documentation doesn't make clear that CarProvider needs to conform to JSObjectionProvider protocol

The example code snippets have it, like:

(1)
@interface CarProvider : NSObject <JSObjectionProvider>
@end

@implementation CarProvider
(2)
- (id)provide:(JSObjectionInjector *)context arguments:(NSArray *)arguments {
  // Manually build object
  return car;
}
@end

Two things need to be updated on the README and on the main page:

  1. the @interface declaration
  2. the signature of provide:arguments:

Please update the documentation!

Support for registering a Singleton class mapped to an Interface

Currently if I want the injector to supply a singleton to one of my classes I have the following options:

  1. [JSObjection registerClass:theClass lifeCycle: JSObjectionInstantiationRuleSingleton];

Which registers a class to be created when first asked for and shared as a singleton.

  1. [jSObjectionModuleinstance registerEagerSingleton:clazz];

Which registers a class to be created immediately and shared as a singleton.

But I have no way of having a singleton mapped to a protocol. No way to say; 'Whenever asked for a dependency of SomeProtocol satisfy it with a (singleton) instance of SomeClass.

Armv6 unsupported

I'm unable to archive my app if I add Objection it includes my app includes an armv6 architecture. How could I get this to work against armv6?

Error is as follows:
ld: in /Users/ncurran/workspace/main/Libraries/Objection-iOS.framework/Objection-iOS, file is universal but does not contain a(n) armv6 slice for architecture armv6

Injection at Initialization

I seem to be having a problem with dependency injection configuration. Actually there are two things that I am struggling with.

The first issue is that from the Objection website it seems that the only thing I need to do is add a property for my dependencies ex:

@property (nonatomic, weak) id<someModelService> someModelService

and add the clause:

objection_requies_sel(@selector(someModelService))

But It didn't seem to work until I added in the init function:
[[JSObjection defaultInjector] injectDependencies:self];

This is of course given the fact that I have added
[self bind:[ModelServiceImpl new] toProtocol:@protocol(someModelService)];
in the JSObjectionModule subclass that I have set as the default injector.

The second issue could be related to something I'm missing that is causing the first issue but here it goes:
In my DependencyModule subclass I have the following two lines:
[self bind:[ModelDaoImpl new] toProtocol:@protocol(someModelDao)];
[self bind:[ModelServiceImpl new] toProtocol:@protocol(someModelService)]; //Is dependent on someModelDao protocol and adds it as a dependency
But since the second object is dependent on the first one, if I call:
[[JSObjection defaultInjector] injectDependencies:self];
nothing will happen and someModelDao won't be injected because defaultInjector is not setup yet.

I have been looking for tutorials or examples but I can't come across any?
There's mention in http://spin.atomicobject.com/2012/02/13/objection-is-over-1-year-old/
about an example proj in the repo, but the project that I found is just the source code and tests.
Thanks for such an awesome project.

Document how to use Objection with ViewController

For many iOS developers DI is most useful if all of the ViewControllers' dependencies can be injected. However, when an iOS project uses Storyboards, the system instantiates the ViewController for you.

This instance of a ViewController is unknown to Objection, which is problematic because it may need to listen out for delegate methods of various other components of the system.

The ideal way is to hijack the ViewController's instantiation code and have all dependencies beneath ViewController be resolved by Objection, maximising this project's utility and convenience for the programmer.

Somekind of Changelog

Hi guys,

Maybe this is not the correct place but I couldn't find anywhere else so here we go.

I just executed pod outdated and saw that version 1.3.1 of objection was available and I'm on 1.2.1 so I was looking for a What's New/Changelog and couldn't find it. It would be really nice to have one.

Exception: Unable to find property declaration: "propertyName"

Our crashlogger is reporting these exceptions and we are getting a lot. Problem is our crash log is not properly symbolicated so we don't really know where to look aside from looking for code with the property name:

reason = Unable to find property declaration: 'dhUiController';
stackTrace =
(
0   CoreFoundation                      0x3528f3ff + 186
1   libobjc.A.dylib                     0x36209963 objc_exception_throw + 30
2   DreamHouse                          0x002a0dfd DreamHouse + 2280957
3   DreamHouse                          0x0029f84d DreamHouse + 2275405
4   DreamHouse                          0x0029f60d DreamHouse + 2274829
5   DreamHouse                          0x0029ff63 DreamHouse + 2277219
6   DreamHouse                          0x00183203 DreamHouse + 1110531
7   DreamHouse                          0x00204061 DreamHouse + 1638497
8   DreamHouse                          0x00205d17 DreamHouse + 1645847
9   DreamHouse                          0x00227959 DreamHouse + 1784153
10  UIKit                               0x33b05441 + 5680
11  CoreFoundation                      0x35264941 + 20
12  CoreFoundation                      0x35262c39 + 276
13  CoreFoundation                      0x35262f93 + 746
14  CoreFoundation                      0x351d623d CFRunLoopRunSpecific + 356
15  CoreFoundation                      0x351d60c9 CFRunLoopRunInMode + 104
16  GraphicsServices                    0x36a8533b GSEventRunModal + 74
17  UIKit                               0x33b50291 UIApplicationMain + 1120
18  DreamHouse                          0x000bc06f DreamHouse + 295023
19  DreamHouse                          0x00076058 DreamHouse + 8280
);"""

Question is when exactly is this exception raised? We're sure that we have the correct name cause it'll crash right away if it doesn't. Another problem is we haven't experienced the crash ourselves (during testing), it only shows up on our crash logs.

Compatibility with Swift

The new programming language Swift is awesome but unfortunately it doesn't seem to support things like defines.
So we would need an other approach to define the dependencies of our classes without maintaining a lots of factories/providers.

Any ideas yet?

Add 'injectObject' to Injector

A module shouldn't be the only way an object can be injected into the injector context outside of the default registration mechanism.

Add qualifiers when registering/binding and getting objects

Sometimes you might want to wire up two instances of the same Protocol (or Class). They might be the same implementation with different configuration, or they might be different implementations.

For example, you might have a protocol that fetches movie titles. One instance might point to a database that has "Comedy" and another that has "Action". Or they might point to different servers.

Then you could bind them in a module like this:

[self bind:comedy toProtocol:@protocol(MovieSource) withQualifier:@"comedy"];
[self bind:action toProtocol:@protocol(MovieSource) withQualifier:@"action"];

There could be additional register macros that take NSString qualifiers too.

Then manually get them like this:

id comedy = [injector getObject:@protocol(MovieSource) withQualifier:@"comedy"];

Not sure what the additional requires macros would look like.

Exception when registering eager singleton

I'm getting an exception thrown when trying to register an eager singleton which I'm registering/binding within a module, like so:

@implementation MyAppModule
- (void)configure {
    [self bindClass:[Foo class] inScope:JSObjectionScopeSingleton];
    [self registerEagerSingleton:[Foo class]];
}
@end

This throws an exception in -[JSObjectionInjector initializeEagerSingletons] because there isn't a binding in the global context.

Would you be agreeable to a pull request that changes -initializeEagerSingletons to check for an entry in _context as well?

unRegisterClass Method

Hi,

Firstly thanks for this library. I am finding it really useful.

I just wanted to request a method for unregistering a class:

  • (void)unregisterClass:(Class)theClass;

Documentation confusion

Maybe it was just my reading, but from going over the documentation I thought each class had to be bound in a module before use.

In addition all of the examples of registering a module uses alloc/init on the bind so any injections of those instances won't be injected. These two led me to think there was a problem with nested injections until I realized what was going on.

Automatic injection with protocols

I have just started using Objection in a Mac OS X project. I'm finding a problem when trying to inject a dependency that implements a protocol.

I have a LoginService class that implements the protocol ILoginService. I keep an object that implements ILoginService inside another class named SessionManager. Now I register LoginService class with ILoginService using a Module in Objection. However, when trying to automatically inject the property that implements ILoginService in SessionManager I get the following error.
"Cannot find an instance that is bound to the protocol 'ILoginService' to assign to the property 'loginService'"

My code looks like follows.

========== start of code ==============================

@protocol ILoginService

@EnD

@interface LoginService : NSObject
{
}

@EnD

@implementation LoginService

// implementation code

@EnD

@protocol ISessionManager

@EnD

@interface SessionManager : NSObject
{
id loginService;
}

@Property(strong) id loginService;

@EnD

@implementation SessionManager

objection_register_singleton(SessionManager)
objection_requires(@"loginService")
@synthesize loginService;

// implementation code

@EnD

@interface MyInjectorModule : JSObjectionModule

@EnD

@implementation MyInjectorModule

  • (void)configure {
    [self bindClass:[LoginService class] toProtocol:@protocol(ILoginService)];
    [self bindClass:[SessionManager class] toProtocol:@protocol(ISessionManager)];
    }

@EnD

// inside Application main method
JSObjectionInjector *injector = [JSObjection createInjector:[[MyInjectorModule alloc] init]];
[JSObjection setDefaultInjector:injector];
id sessionManager = [[JSObjection defaultInjector] getObject:@protocol(ISessionManager)]; // I get the above mentioned error when executing this line

========= end of code ================================

If I rather use the concrete class name in the LoginManager property declaration in SessionManager (i.e. LoginService *loginService) it works. However I need to keep the loginService property with the type id so that the concrete class can be changed in the future.
Is there a way to achieve this in Objection?

Example

Is there a chance to get a fully working example, to see when and how to use objection in a more application stile example. E.g. is it useful to use objection for every kind of objects (view, controller, object)?

Regards

getting a nil defaultInjector when using with OCUnit

My unit test currently calls a class called ABTestConfig which creates an injector using a Module and assigns this to the defaultInjector. I have followed this happening in the debugger and can see the value assigned to gGlobalInjector.

When the method in the unit test is run e.g. testExample then this calls another method that tries to get the defaultInjector from JSObjection and I get a nil back. Using the debugger I have confirmed that the value of gGlobalInjector is indeed nil.

Any idea why this is happening?

Is Objection incompatible with unit testing as that would be a real shame!

I will provide code examples if needed.

Document integration with Kiwi

It took me a while to figure out how to integrate Objection well with Kiwi specs. I ended up building something to make this process simpler: https://github.com/mindsnacks/MSSpec

I figured I'd share this here, and maybe it will serve as inspiration to either document or reference this project in the README :)

Bind Injector to Itself

Currently when you need to create objects that have dependencies you need to set and then get the global instance. ie, somewhere in my class i need to create another object...

- (void)someMethod {
  // first get the global instance
  JSObjectionInjector *injector = [JSObjection globalInjector];
  // then use the injector to create something.
  // NB: this is NOT a dependency
}

This reduces the injector to little more than a service locater. I think it would be more useful, and more testable, to allow the injector to itself be injected as a dependency. So if your class needs to create other objects you simply ask for the injector as one of your dependencies.

@interface MyClass
@property (nonatomic, retain) JSObjectionInjector *injector;
@end

@implementation MyClass

objection_register( MyClass );
objection_requires( @"injector" );

I am currently doing this through my own "Injector Provider", but I think it would be a good feature to have built in.

Rakefile needs updating for Xcode 4.3 location

Xcode 4.3 installs under /Applications and CLI tools live in /usr/bin instead of /Developer/usr/bin. Building fails because xcodebuild is expected to still be under /Developer. Changing to new location works for me:

def xcodebuild_executable
  "/usr/bin/xcodebuild"  
end

But my Ruby-fu isn't strong enough to know the best way to make this conditional so it works for Xcode 4.2 and before, otherwise I'd send a pull request.

@objection_initializer not passing arguments

I have the following initialiser in a class:

objection_initializer(initWithComms:)
-(instancetype) initWithComms:(Comms *) commsX {
    self = [super init];
    if (self) {
        _comms = commsX;
    }
    return self;
}

However when I run the code the commsX argument is nil. I've tried to follow the Objection code and it appears that it is building an instance of the Comms object and putting it into it's context. However it's not passed through to the code that actually creates the instance of this code.

Am I doing anything wrong? I found the sample code on the Objection website rather difficult to follow :-(

equivalent to @Named injections

It would be interesting if a module could add an NSDictionary to an injector and have a new macro that maps keys in that dictionary to properties.

Something like:

@interface MyModule : JSObjectionModule
@end

@implementation MyModule
-(void) configure
{
    [self bindNames:@{@"NamedProperty" : @"This will be a named Property"}];
}
@end

@interface MyObject
@property (weak, nonatomic) NSString* myProperty;
@end

@implementation
objection_requires_names(@{@"NamedProperty" : @"myProperty"})
@end

While not needed it would be nice to have a convience method that would load a plist like that.

iOS 4.3 compatibility

I'm using the latest build but it says in the website it only supports 5.0 and higher. However, our project is still supporting 4.3 and it still works.

Are there any specific case where it'll cause a problem with 4.x?

JIT custom object creation via a provider

Hello dewind, thank you for fantastic work. This is my first foray into DI so I apologize if this is a silly question.

I'm trying to understand how to inject custom-created objects with Objection. My current understanding is that an JSObjectionInjector requires a JSObjectionModule to map classes to instance. So to use the defaultInjector I need to instantiate a subclass of JSObjectionModule. The recommended approach is to create defaultinjector in application:didFinishLaunching:. The problem with this is that my JSObjectionModule subclass doesn't know how to construct my custom object yet, it is in fact dependent on some further user actions.

How would you recommend I proceed in this case?

Bind subclass to superclass

Unless I am missing something there is currently no way to bind a class to another class.

This would allow a parameter typed to an Abstract superclass to be satisfied by a Concrete subclass.

The method signature would look like this:

  • (void)bindClass:(id)aClass toClass:(Class)bClass;

Sharing singleton scope when deriving a new injector

We have a scenario where we want to derive a new injector from an existing one with additional modules when displaying a new view controller. The existing injector has singleton instances that are already cached within it, and we need to use the same instance of them in the new injector.

Currently, using withModuleOfType: will create a new injector with the same global context, but with new injection entries that will create new instances of the singletons upon request.

It makes sense to me that derives injectors will produce the same singletons of the original injector, since we want to inherit the behavior of the original injector, including its context. What do you think?

Additionally, if anyone got any idea on how to overcome this in a different, elegant way, I'd be happy to know.

Inject property declared in a category.

it appears Objection is unable to inject a property declared in a category.

@interface WTAppDelegate (PrivateDependencies)
@property (nonatomic, strong) WTNotificationRoutingBehavior *notificationRoutingBehavior;
@end

@implementation WTAppDelegate
objection_requires_sel(@selector(notificationRoutingBehavior))
@end

Is this the intended behavior? Or have I simply stumbled across an edge-case?

Is this something Objection should be capable of? I'm happy to submit a PR if so.

2014-09-24 17:18:45.036 Wingtip[65236:60b] *** Terminating app due to uncaught exception 'JSObjectionException', reason: 'Unable to find property declaration: 'notificationRoutingBehavior' for class 'WTAppDelegate''
*** First throw call stack:
(
    0   CoreFoundation                      0x00000001047a2495 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x00000001044f999e objc_exception_throw + 43
    2   Wingtip                             0x0000000100dc432d GetProperty + 301
    3   Wingtip                             0x0000000100dc3c2b -[JSObjectionRuntimePropertyReflector propertyForClass:andProperty:] + 75
    4   Wingtip                             0x0000000100dbedce +[JSObjection propertyForClass:andProperty:] + 78
    5   Wingtip                             0x0000000100dc4c9c InjectDependenciesIntoProperties + 588
    6   Wingtip                             0x0000000100dc11bb -[JSObjectionInjector injectDependencies:] + 123
    7   Wingtip                             0x0000000100cd92c4 -[WTAppDelegate configureObjection] + 804
    8   Wingtip                             0x0000000100cd8a89 -[WTAppDelegate application:didFinishLaunchingWithOptions:] + 89
    9   UIKit                               0x00000001035043d9 -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 264
    10  UIKit                               0x0000000103504be1 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1605
    11  UIKit                               0x0000000103508a0c -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 660
    12  UIKit                               0x0000000103519d4c -[UIApplication handleEvent:withNewEvent:] + 3189
    13  UIKit                               0x000000010351a216 -[UIApplication sendEvent:] + 79
    14  UIKit                               0x000000010350a086 _UIApplicationHandleEvent + 578
    15  GraphicsServices                    0x0000000105e6271a _PurpleEventCallback + 762
    16  GraphicsServices                    0x0000000105e621e1 PurpleEventCallback + 35
    17  CoreFoundation                      0x0000000104724679 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 41
    18  CoreFoundation                      0x000000010472444e __CFRunLoopDoSource1 + 478
    19  CoreFoundation                      0x000000010474d903 __CFRunLoopRun + 1939
    20  CoreFoundation                      0x000000010474cd83 CFRunLoopRunSpecific + 467
    21  UIKit                               0x00000001035082e1 -[UIApplication _run] + 609
    22  UIKit                               0x0000000103509e33 UIApplicationMain + 1010
    23  Wingtip                             0x0000000100cbcdff main + 351
    24  libdyld.dylib                       0x0000000104ddf5fd start + 1

Ability to specify override objects

This is to support testing. It would be nice to be able to specify that a particular class registered with Objection is the preferred one to be instantiated. For example, when testing, I could specify a test class (or mock of some sort) will be returned by Objection instead of the one from the app.

The Spring framework has this concept (for Java) and I've found it very useful for providing stubbed out classes for predictable testing scenarios.

Thanks

TODO ARCify

Sounds like a couple hour task. If there a complication supporting OSX (I'm only familiar with iOS)? If not I'd gladly submit a PR.

Providing a singleton from an external dependency results in a nil property

I have an object called "MCEntry" that needs a field on it called "NSFNanoStore". I'd like to treat the NSFNanoStore as a singleton, but I'm a bit stymied at how to do this. I've set up my project like the following

@implementation AppModule {}

- (void)configure {
    [self bindBlock:^(JSObjectionInjector *context){
        NSFNanoStore *store = [NSFNanoStore createAndOpenStoreWithType:NSFPersistentStoreType path:[self pathForNanoStore] error:nil];
        return (id)store;
    } toClass:[NSFNanoStore class]];
}

@end



@implementation MCEntry {
}

objection_register(MCEntry)
objection_requires(@"nanoStore")

@synthesize bodyFat;
@synthesize muscle;
@synthesize weight;
@synthesize nanoStore;
@synthesize dateString;

...
@end

But when I try to create an instance of MCEntry using the injector, the nanoStore field of the MCEntry instance is nil:

savedEntry = [[JSObjection defaultInjector] getObject:[MCEntry class]];

Am I doing this incorrectly?

JS prefix on classnames

The JS prefix on classnames causes cognitive dissonance, and it feels to me like Objection is already enough of a qualifier.

Eg, ObjectionInjector is more harmonious when held in mental-space than JSObjectionInjector.

error: default initialization of an object of const type 'NSString *const __strong'

when using Objection within Objective-C++ code we are getting these errors:

In file included from .../SomeFile.mm:10:
In file included from .../Objection/Objection.h:11:
.../Objection/JSObjectionUtils.h:4:17: error: default initialization of an object of const type 'NSString *const __strong'
NSString *const JSObjectionInitializerKey;
^
.../Objection/JSObjectionUtils.h:5:17: error: default initialization of an object of const type 'NSString *const __strong'
NSString *const JSObjectionDefaultArgumentsKey;
^
2 errors generated.

getObjectWithArgs arguments ignored in some cases

  1. Create class with objection_initializer specifying selector and default argument.
  2. Bind class to protocol in module spec.
  3. Get instance of protocol binded on step 2 from injector with getObjectWithArgs overriding default argument from step 1.

Expected result: object binded to protocol initialized with argument passed on step 3.

Actual result: object initialized with argument specified on step 1 with no respect to values passed on step 3.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.