diff --git a/Objection.xcodeproj/project.pbxproj b/Objection.xcodeproj/project.pbxproj index 0bb450a..1112399 100644 --- a/Objection.xcodeproj/project.pbxproj +++ b/Objection.xcodeproj/project.pbxproj @@ -119,6 +119,8 @@ 4B9D310713DF613600C81C45 /* JSObjection.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B9D310213DF613500C81C45 /* JSObjection.m */; }; 4B9D311413DF79FE00C81C45 /* JSObjectionUtils.h in Copy Header Files */ = {isa = PBXBuildFile; fileRef = 4B9D30FE13DF5FAC00C81C45 /* JSObjectionUtils.h */; }; 4B9D311513DF79FE00C81C45 /* JSObjection.h in Copy Header Files */ = {isa = PBXBuildFile; fileRef = 4B9D310113DF613500C81C45 /* JSObjection.h */; }; + 4BA620D01C06BEAE00DB87B6 /* InjectImplementsProtocolSpecs.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BA620CF1C06BEAE00DB87B6 /* InjectImplementsProtocolSpecs.m */; }; + 4BA620D11C06BEAE00DB87B6 /* InjectImplementsProtocolSpecs.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BA620CF1C06BEAE00DB87B6 /* InjectImplementsProtocolSpecs.m */; }; 4BA6DD1F12AAB3CD00CFFD70 /* JSObjectionInjectorEntry.h in Copy Header Files */ = {isa = PBXBuildFile; fileRef = 4BED512F12AA8BF300CA6B36 /* JSObjectionInjectorEntry.h */; }; 4BA6DD2012AAB3CD00CFFD70 /* JSObjectionInjector.h in Copy Header Files */ = {isa = PBXBuildFile; fileRef = 4BED513012AA8BF300CA6B36 /* JSObjectionInjector.h */; }; 4BA6DD2112AAB3CD00CFFD70 /* JSObjectionBindingEntry.h in Copy Header Files */ = {isa = PBXBuildFile; fileRef = 4BED513112AA8BF300CA6B36 /* JSObjectionBindingEntry.h */; }; @@ -325,6 +327,7 @@ 4B9D30FE13DF5FAC00C81C45 /* JSObjectionUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSObjectionUtils.h; sourceTree = ""; }; 4B9D310113DF613500C81C45 /* JSObjection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSObjection.h; sourceTree = ""; }; 4B9D310213DF613500C81C45 /* JSObjection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSObjection.m; sourceTree = ""; }; + 4BA620CF1C06BEAE00DB87B6 /* InjectImplementsProtocolSpecs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InjectImplementsProtocolSpecs.m; sourceTree = ""; }; 4BA9CFF412AA86E600674F0E /* OCHamcrest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OCHamcrest.framework; path = Vendor/OCHamcrest.framework; sourceTree = ""; }; 4BB07D741668679A00D9BA1E /* AddAndRemoveModulesSpecs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddAndRemoveModulesSpecs.m; sourceTree = ""; }; 4BB428A116D3205C00710F1E /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; @@ -551,6 +554,7 @@ 4B95819612AE593F00CBF1EB /* ModuleUsageSpecs.m */, 4B4289301567F3EC004F73D3 /* InitializerSpecs.m */, 4BB07D741668679A00D9BA1E /* AddAndRemoveModulesSpecs.m */, + 4BA620CF1C06BEAE00DB87B6 /* InjectImplementsProtocolSpecs.m */, ); path = Specs; sourceTree = ""; @@ -880,6 +884,7 @@ 4B42585F13F56D41006BC001 /* InheritanceSpecs.m in Sources */, 4B42586013F56D41006BC001 /* ModuleUsageSpecs.m in Sources */, 4B42586113F56D41006BC001 /* JSObjectionProviderEntry.m in Sources */, + 4BA620D01C06BEAE00DB87B6 /* InjectImplementsProtocolSpecs.m in Sources */, 4B42586213F56D41006BC001 /* JSObjectionEntry.m in Sources */, 4B42586313F56D41006BC001 /* NSObject+Objection.m in Sources */, 4B42586413F56D41006BC001 /* JSObjectionUtils.m in Sources */, @@ -912,6 +917,7 @@ 4B4258D313F56F45006BC001 /* InheritanceSpecs.m in Sources */, 4B4258D413F56F45006BC001 /* ModuleUsageSpecs.m in Sources */, 4B4258D513F56F45006BC001 /* JSObjectionProviderEntry.m in Sources */, + 4BA620D11C06BEAE00DB87B6 /* InjectImplementsProtocolSpecs.m in Sources */, 4B4258D613F56F45006BC001 /* JSObjectionEntry.m in Sources */, 4B4258D713F56F45006BC001 /* NSObject+Objection.m in Sources */, 4B4258D813F56F45006BC001 /* JSObjectionUtils.m in Sources */, diff --git a/Source/JSObjectionInjector.h b/Source/JSObjectionInjector.h index f960e0e..c96f471 100644 --- a/Source/JSObjectionInjector.h +++ b/Source/JSObjectionInjector.h @@ -1,6 +1,14 @@ #import #import "JSObjectionModule.h" +@protocol JSObjectionInjectorSelectors + +@optional ++ (NSSet *)objectionRequires; ++ (NSDictionary *)objectionRequiresNames; + +@end + @interface JSObjectionInjector : NSObject - (instancetype)initWithContext:(NSDictionary *)theGlobalContext; diff --git a/Source/JSObjectionInjectorEntry.h b/Source/JSObjectionInjectorEntry.h index 0d31ab0..1d079ec 100644 --- a/Source/JSObjectionInjectorEntry.h +++ b/Source/JSObjectionInjectorEntry.h @@ -1,6 +1,13 @@ #import #import "JSObjectionEntry.h" +@protocol JSObjectionInjectorEntrySelectors + +@optional ++ (id)objectionInitializer; + +@end + @interface JSObjectionInjectorEntry : JSObjectionEntry @property (nonatomic, readonly) Class classEntry; diff --git a/Source/JSObjectionInjectorEntry.m b/Source/JSObjectionInjectorEntry.m index 053e0a7..aea8ece 100644 --- a/Source/JSObjectionInjectorEntry.m +++ b/Source/JSObjectionInjectorEntry.m @@ -73,11 +73,11 @@ - (id)buildObject:(NSArray *)arguments initializer: (SEL) initializer { } - (SEL)initializerForObject { - return NSSelectorFromString([[self.classEntry performSelector:@selector(objectionInitializer)] objectForKey:JSObjectionInitializerKey]); + return NSSelectorFromString([[self.classEntry objectionInitializer] objectForKey:JSObjectionInitializerKey]); } - (NSArray *)argumentsForObject:(NSArray *)givenArguments { - return givenArguments.count > 0 ? givenArguments : [[self.classEntry performSelector:@selector(objectionInitializer)] objectForKey:JSObjectionDefaultArgumentsKey]; + return givenArguments.count > 0 ? givenArguments : [[self.classEntry objectionInitializer] objectForKey:JSObjectionDefaultArgumentsKey]; } diff --git a/Source/JSObjectionUtils.m b/Source/JSObjectionUtils.m index f436856..08d75a1 100644 --- a/Source/JSObjectionUtils.m +++ b/Source/JSObjectionUtils.m @@ -35,6 +35,10 @@ static JSObjectionPropertyInfo FindClassOrProtocolForProperty(objc_property_t pr classOrProtocol = objc_getProtocol([classOrProtocolName UTF8String]); propertyInfo.type = JSObjectionTypeProtocol; } else { + if ([classOrProtocolName hasSuffix:@">"]) { + classOrProtocolName = [classOrProtocolName substringToIndex:[classOrProtocolName rangeOfString:@"<"].location]; + } + classOrProtocol = NSClassFromString(classOrProtocolName); propertyInfo.type = JSObjectionTypeClass; } @@ -50,7 +54,7 @@ static JSObjectionPropertyInfo FindClassOrProtocolForProperty(objc_property_t pr static NSSet* BuildDependenciesForClass(Class klass, NSSet *requirements) { Class superClass = class_getSuperclass([klass class]); if([superClass respondsToSelector:@selector(objectionRequires)]) { - NSSet *parentsRequirements = [superClass performSelector:@selector(objectionRequires)]; + NSSet *parentsRequirements = [superClass objectionRequires]; NSMutableSet *dependencies = [NSMutableSet setWithSet:parentsRequirements]; [dependencies unionSet:requirements]; requirements = dependencies; @@ -61,7 +65,7 @@ static JSObjectionPropertyInfo FindClassOrProtocolForProperty(objc_property_t pr static NSDictionary* BuildNamedDependenciesForClass(Class klass, NSDictionary *namedRequirements) { Class superClass = class_getSuperclass([klass class]); if([superClass respondsToSelector:@selector(objectionRequiresNames)]) { - NSDictionary *parentsNamedRequirements = [superClass performSelector:@selector(objectionRequiresNames)]; + NSDictionary *parentsNamedRequirements = [superClass objectionRequiresNames]; NSMutableDictionary *namedDependencies = [NSMutableDictionary dictionaryWithDictionary:parentsNamedRequirements]; [namedDependencies addEntriesFromDictionary:namedRequirements]; namedRequirements = namedDependencies; @@ -146,7 +150,7 @@ static void _validateObjectReturnedFromInjector(id *theObject, JSObjectionProper static void InjectDependenciesIntoProperties(JSObjectionInjector *injector, Class klass, id object) { if ([klass respondsToSelector:@selector(objectionRequires)]) { - NSSet *properties = [klass performSelector:@selector(objectionRequires)]; + NSSet *properties = [klass objectionRequires]; NSMutableDictionary *propertiesDictionary = [NSMutableDictionary dictionaryWithCapacity:properties.count]; for (NSString *propertyName in properties) { JSObjectionPropertyInfo propertyInfo; @@ -161,7 +165,7 @@ static void InjectDependenciesIntoProperties(JSObjectionInjector *injector, Clas } if ([klass respondsToSelector:@selector(objectionRequiresNames)]) { - NSDictionary *namedProperties = [klass performSelector:@selector(objectionRequiresNames)]; + NSDictionary *namedProperties = [klass objectionRequiresNames]; NSMutableDictionary *propertiesDictionary = [NSMutableDictionary dictionaryWithCapacity:namedProperties.count]; for (NSString *namedPropertyKey in [namedProperties allKeys]) { NSString* propertyName = [namedProperties valueForKey:namedPropertyKey]; @@ -177,7 +181,7 @@ static void InjectDependenciesIntoProperties(JSObjectionInjector *injector, Clas } if ([object respondsToSelector:@selector(awakeFromObjection)]) { - [object performSelector:@selector(awakeFromObjection)]; + [object awakeFromObjection]; } } diff --git a/Specs/InjectImplementsProtocolSpecs.m b/Specs/InjectImplementsProtocolSpecs.m new file mode 100644 index 0000000..87f0882 --- /dev/null +++ b/Specs/InjectImplementsProtocolSpecs.m @@ -0,0 +1,37 @@ +#import "SpecHelper.h" +#import "Fixtures.h" +#import "ModuleFixtures.h" + +@interface NewCarModule : JSObjectionModule +@end + +@implementation NewCarModule +-(void)configure { + [self bindClass:[UnregisteredCar class] toClass:[Car class]]; +} +@end + +@interface CarOwner : NSObject +@property (nonatomic, strong) Car *car; +@end + +@implementation CarOwner +objection_requires(@"car") +@end + +QuickSpecBegin(InjectImplementsProtocolSpecs) + +__block JSObjectionInjector *injector = nil; +__block CarOwner *carDriver = nil; + +beforeEach(^{ + injector = [JSObjection createInjector:[NewCarModule new]]; + carDriver = [CarOwner new]; +}); + +it(@"injects property car that implements UnregisteredProtocol", ^{ + [injector injectDependencies:carDriver]; + assertThat(carDriver.car, is(instanceOf([UnregisteredCar class]))); +}); + +QuickSpecEnd \ No newline at end of file