• Stars
    star
    1,847
  • Rank 25,121 (Top 0.5 %)
  • Language
    Objective-C
  • License
    MIT License
  • Created almost 14 years ago
  • Updated about 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A lightweight dependency injection framework for Objective-C

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

More Repositories

1

heatshrink

data compression library for embedded/real-time systems
C
1,317
star
2

ts-react-graphql-starter-kit

A starter kit repository for building single page webapps in TypeScript with react, redux, graphql/express, jest
TypeScript
114
star
3

odo

an atomic odometer for the command line
C
71
star
4

elegant-form-validation-react

Elegant Form Validation in React
JavaScript
62
star
5

lenses

TypeScript
57
star
6

piece_pipe

PiecePipe helps you break your code into small interesting pieces and provides the glue for pipelining them together to provide elegant, readable code.
Ruby
53
star
7

VMWareFB_OpenStep

VMWareFB OpenStep display driver -- run OpenStep and NeXTStep in VMWare in high color and high res
Objective-C
42
star
8

hamsterdam

Immutable Struct-like record structures based on Hamster.
Ruby
36
star
9

hex_string

String extensions to convert binary data to / from human readable hex tuples.
Ruby
35
star
10

ecs-deployment

Shell
32
star
11

monadt

Monads & ADTs in Ruby
Ruby
27
star
12

motion-objection

Wrapping Objection in RubyMotion.
Ruby
25
star
13

publisher

Simple event firing/subscribing mechanism in Ruby.
Ruby
23
star
14

space-battle-2

RTS game engine for Atomic Games
Ruby
23
star
15

ddc

Data Driven Controllers for your Rails app
Ruby
22
star
16

ansible-laptop-playbook-example

example Ansible playbook for a laptop
Shell
21
star
17

ts-workshop

Guided TypeScript workshop
TypeScript
18
star
18

typescript-permissions-demo

TypeScript
18
star
19

jquery.expand

Structural template engine for jQuery.
CoffeeScript
17
star
20

to_api

Instead of to_xml and as_json, simply implement to_api. Return an unformatted hash. Let the model understand the data. Let the controller understand the format. In Rails, the controller can simply call render :xml => model.to_api or render :json => model.to_api.
Ruby
16
star
21

docker-cli-distribution

Example code to accompany the blog post
Ruby
14
star
22

constructor

Declarative named-argument object initialization
Ruby
13
star
23

atomic_vim

Shared Atomic Object vim configuration
Vim Script
12
star
24

autopsy

Emit helpful artifacts on Capybara Webkit test failures
Ruby
11
star
25

fire_poll

Simple method for knowing when something is ready, running correctly, and dealing with asynchronous effects in tests and specs.
Ruby
11
star
26

multiple_return_in_c_benchmarks

benchmarks for post "Comparing the Cost of Different Multiple-return Techniques in C"
C
7
star
27

utf8_to_gsm

`Utf8ToGsm` provides functionality to convert UTF-8 characters to their GSM equivalents.
Ruby
6
star
28

CSharpMemoizer

C# Memoization Example
C#
6
star
29

ts-stack

TypeScript
6
star
30

Ensemble-Test-Framework

Ensemble Test Framework
6
star
31

birdbath

A rails gem for verifying migrations with rspec or Test::Unit
Ruby
6
star
32

strange-loop-ts-workshop

Strange Loop 2019 TypeScript Workshop!
TypeScript
5
star
33

rubymotion-nimbus-table-example

Example of using a Nimbus table view in RubyMotion.
Ruby
5
star
34

ssl_certificate_expiration_checker

Simple application to check the expiration dates on SSL certificates, and send notifications to a service.
Ruby
4
star
35

mvccontrib

Atomic MVC Contrib 'Fork'
JavaScript
3
star
36

injection

Injection is a simple dependency injection plugin for Rails. It allows you to inject objects into your controllers and observers which have been described in a yaml file (config/objects.yml).
Ruby
3
star
37

csharp_mvp_generator

Ruby code that generates model / view / presenter C# code stubs
Ruby
3
star
38

diy

Constructor-based dependency injection container using YAML input.
Ruby
2
star
39

environment_configurable

A library that makes environment dependent configuration easy in Rails.
Ruby
2
star
40

roommate-lambda

AO Conference Room IoT Project - Backend
F#
2
star
41

survival-pack

Surviving a Zombie Apocolypse
2
star
42

psinfo

psinfo example application used for embedded TDD workshops
Ruby
2
star
43

roommate

AO Conference Room IoT Project - embedded device
C
2
star
44

mongoid_class_for_collection

Flexible migrations for mongoid_rails_migrations
Ruby
2
star
45

jquery.persistedtoggle

Toggle the visibility of elements on a page using keyboard shortcuts, persisting the visibility state across requests.
JavaScript
2
star
46

terraform-aws-tailscale-router

deploys a Tailscale subnet router container into an AWS VPC
HCL
2
star
47

hardmock

A strict, ordered, expectation-oriented mock object library for testing Ruby code.
Ruby
2
star
48

kinetic-c

Seagate Kinetic Cloud Storage C Client Library and Examples
C
1
star
49

june_2012_embedded_tdd_workshop

Examples and documentation from the June 2012 Embedded TDD workshop
Ruby
1
star
50

ubuntu-1604-ruby-233-nodejs-6-passenger

Docker build configuration for Ubuntu 16.04 with Ruby 2.3.3 and NodeJS 6.10.2 LTS and Passenger (specific version tags)
Dockerfile
1
star
51

require_options

Enforces key requirements in a hash
Ruby
1
star
52

angular-pdf-library-test

TypeScript
1
star
53

alexa-atomic-spin

Alexa Skill for reading Atomic Spin posts
Ruby
1
star
54

kinetic-ruby

Seagate Kinetic Protocol Client Library
Ruby
1
star
55

LTIB-Ruby-Source

Ruby package for cross-compiling with LTIB
Ruby
1
star
56

systir

Write and run automated system tests in a domain specific language (DSL) you design, as you need it.
Ruby
1
star
57

cdcicd

Test CI/CD github actions
HTML
1
star