Skip to content

Commit

Permalink
New feature 'TyphoonStoryboard' introduced. (#182)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexgarbarev committed Feb 24, 2014
1 parent 5ee4021 commit a3d1473
Show file tree
Hide file tree
Showing 16 changed files with 17,755 additions and 4,061 deletions.
2 changes: 1 addition & 1 deletion .scripts/pod-update-checksum.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
a858f8fbb771e8db241f5a3f882166951d7b5c2d
ffd157ba3bee7c7ab38d8d90e16c04f20c58a049
16 changes: 16 additions & 0 deletions A-Typhoon.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,8 @@
FA1E2C3418B0BD8500744771 /* TyphoonFactoryPropertyInjectionPostProcessor.h in Headers */ = {isa = PBXBuildFile; fileRef = FA1E2C3118B0BD8500744771 /* TyphoonFactoryPropertyInjectionPostProcessor.h */; };
FA1E2C3518B0BD8500744771 /* TyphoonFactoryPropertyInjectionPostProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = FA1E2C3218B0BD8500744771 /* TyphoonFactoryPropertyInjectionPostProcessor.m */; };
FA1E2C3618B0BD8500744771 /* TyphoonFactoryPropertyInjectionPostProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = FA1E2C3218B0BD8500744771 /* TyphoonFactoryPropertyInjectionPostProcessor.m */; };
FA381FB418BBB95C0094BBFA /* TyphoonStoryboard.h in Headers */ = {isa = PBXBuildFile; fileRef = FA381FB218BBB95C0094BBFA /* TyphoonStoryboard.h */; };
FA381FB518BBB95C0094BBFA /* TyphoonStoryboard.m in Sources */ = {isa = PBXBuildFile; fileRef = FA381FB318BBB95C0094BBFA /* TyphoonStoryboard.m */; };
FA7A961A1895370900053EAE /* NSObject+PropertyInjection.h in Headers */ = {isa = PBXBuildFile; fileRef = FA7A96181895370900053EAE /* NSObject+PropertyInjection.h */; };
FA7A961B1895370900053EAE /* NSObject+PropertyInjection.h in Headers */ = {isa = PBXBuildFile; fileRef = FA7A96181895370900053EAE /* NSObject+PropertyInjection.h */; };
FA7A961C1895370900053EAE /* NSObject+PropertyInjection.m in Sources */ = {isa = PBXBuildFile; fileRef = FA7A96191895370900053EAE /* NSObject+PropertyInjection.m */; };
Expand Down Expand Up @@ -575,6 +577,8 @@
FA1E2C2C18B0B0DF00744771 /* TyphoonAssemblyPropertyInjectionPostProcessor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TyphoonAssemblyPropertyInjectionPostProcessor.m; sourceTree = "<group>"; };
FA1E2C3118B0BD8500744771 /* TyphoonFactoryPropertyInjectionPostProcessor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TyphoonFactoryPropertyInjectionPostProcessor.h; sourceTree = "<group>"; };
FA1E2C3218B0BD8500744771 /* TyphoonFactoryPropertyInjectionPostProcessor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TyphoonFactoryPropertyInjectionPostProcessor.m; sourceTree = "<group>"; };
FA381FB218BBB95C0094BBFA /* TyphoonStoryboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TyphoonStoryboard.h; sourceTree = "<group>"; };
FA381FB318BBB95C0094BBFA /* TyphoonStoryboard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TyphoonStoryboard.m; sourceTree = "<group>"; };
FA7A96181895370900053EAE /* NSObject+PropertyInjection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+PropertyInjection.h"; sourceTree = "<group>"; };
FA7A96191895370900053EAE /* NSObject+PropertyInjection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+PropertyInjection.m"; sourceTree = "<group>"; };
FA8AA3F118AD4055001B88CE /* TyphoonPropertyInjectedByComponentFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TyphoonPropertyInjectedByComponentFactory.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -728,6 +732,7 @@
BA79809BD126B307EFF68239 /* ios */ = {
isa = PBXGroup;
children = (
FA381FB118BBB9450094BBFA /* Storyboard */,
466CC659185D589600742220 /* Configuration */,
466CC65D185D589600742220 /* TypeConversion */,
);
Expand Down Expand Up @@ -1106,6 +1111,15 @@
name = PostProcessors;
sourceTree = "<group>";
};
FA381FB118BBB9450094BBFA /* Storyboard */ = {
isa = PBXGroup;
children = (
FA381FB218BBB95C0094BBFA /* TyphoonStoryboard.h */,
FA381FB318BBB95C0094BBFA /* TyphoonStoryboard.m */,
);
path = Storyboard;
sourceTree = "<group>";
};
FA99165F189838F9000AC4B5 /* Pool */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1138,6 +1152,7 @@
FA9916621898390E000AC4B5 /* TyphoonComponentsPool.h in Headers */,
466CC667185D589600742220 /* TyphoonUIColorTypeConverter.h in Headers */,
4BC6492917A20BD7007F0463 /* TyphoonParameterInjectedByReference.h in Headers */,
FA381FB418BBB95C0094BBFA /* TyphoonStoryboard.h in Headers */,
4BC6492A17A20BD7007F0463 /* TyphoonParameterInjectedWithStringRepresentation.h in Headers */,
FAD826991891513C009D9A27 /* TyphoonPropertyInjectedByFactoryReference.h in Headers */,
4BC6492B17A20BD7007F0463 /* TyphoonDefinition.h in Headers */,
Expand Down Expand Up @@ -1405,6 +1420,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
FA381FB518BBB95C0094BBFA /* TyphoonStoryboard.m in Sources */,
4BC648F917A20BD7007F0463 /* TyphoonRXMLElement+XmlComponentFactory.m in Sources */,
4BC648FA17A20BD7007F0463 /* TyphoonRXMLElement.m in Sources */,
4BC648FB17A20BD7007F0463 /* TyphoonXmlComponentFactory.m in Sources */,
Expand Down
5 changes: 5 additions & 0 deletions Source/Factory/TyphoonComponentFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,9 @@
*/
- (void)injectProperties:(id)instance;

/**
* Injects the properties of an object, descripted in definition
*/
- (void)injectProperties:(id)instance withDefinition:(SEL)selector;

@end
11 changes: 11 additions & 0 deletions Source/Factory/TyphoonComponentFactory.m
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,17 @@ - (void)injectProperties:(id)instance
}
}

- (void)injectProperties:(id)instance withDefinition:(SEL)selector
{
if (![self isLoaded]) {[self load];}
TyphoonDefinition *definition = [self definitionForKey:NSStringFromSelector(selector)];
if (definition) {
[self doPropertyInjectionEventsOn:instance withDefinition:definition];
} else {
[NSException raise:NSInvalidArgumentException format:@"Can't find definition for specified selector %@",NSStringFromSelector(selector)];
}
}

/* ====================================================================================================================================== */
#pragma mark - Utility Methods

Expand Down
39 changes: 39 additions & 0 deletions Source/ios/Storyboard/TyphoonStoryboard.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
////////////////////////////////////////////////////////////////////////////////
//
// TYPHOON FRAMEWORK
// Copyright 2013, Jasper Blues & Contributors
// All Rights Reserved.
//
// NOTICE: The authors permit you to use, modify, and distribute this file
// in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////


#import <UIKit/UIKit.h>
#import "TyphoonComponentFactory.h"


/**
* TyphoonStoryboard will inject properties for each viewController created by storyboard.
*
* Normally, TyphoonStoryboard injection performed by viewController's type. But if you want to specify definition
* for viewController injection - use viewController's 'typhoonKey'runtime property.
*
* To specify 'typhoonKey' in storyboard IB, select your viewController, navigate to 'identity inspector'(cmd+option+3) tab,
* section 'User Defined Runtime Attributes'. Add new row with columns:
* @code
* Key Path : typhoonKey
* Type : String
* Value : #set your definition selector string here#
* @endcode
*/
@interface TyphoonStoryboard : UIStoryboard

@property (nonatomic, strong) TyphoonComponentFactory *factory;

+ (TyphoonStoryboard *)storyboardWithName:(NSString *)name bundle:(NSBundle *)storyboardBundleOrNil;

+ (TyphoonStoryboard *)storyboardWithName:(NSString *)name factory:(TyphoonComponentFactory *)factory bundle:(NSBundle *)bundleOrNil;

@end
92 changes: 92 additions & 0 deletions Source/ios/Storyboard/TyphoonStoryboard.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
////////////////////////////////////////////////////////////////////////////////
//
// TYPHOON FRAMEWORK
// Copyright 2013, Jasper Blues & Contributors
// All Rights Reserved.
//
// NOTICE: The authors permit you to use, modify, and distribute this file
// in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////


#import "TyphoonStoryboard.h"
#import "TyphoonComponentFactory+TyphoonDefinitionRegisterer.h"

#import <objc/runtime.h>

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - UIViewController + TyphoonDefinitionKey

@interface UIViewController (TyphoonDefinitionKey)

@property (nonatomic, strong) NSString *typhoonKey;

@end

@implementation UIViewController (TyphoonDefinitionKey)

static const char *kTyphoonKey;

- (void)setTyphoonKey:(NSString *)typhoonKey
{
objc_setAssociatedObject(self, &kTyphoonKey, typhoonKey, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *)typhoonKey
{
return objc_getAssociatedObject(self, &kTyphoonKey);
}

@end

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - TyphoonStoryboard

@implementation TyphoonStoryboard

+ (TyphoonStoryboard *)storyboardWithName:(NSString *)name bundle:(NSBundle *)storyboardBundleOrNil
{
return [super storyboardWithName:name bundle:storyboardBundleOrNil];
}

+ (TyphoonStoryboard *)storyboardWithName:(NSString *)name factory:(TyphoonComponentFactory *)factory bundle:(NSBundle *)bundleOrNil
{
TyphoonStoryboard *storyboard = (id)[super storyboardWithName:name bundle:bundleOrNil];
storyboard.factory = factory;
return storyboard;
}

- (id)instantiateViewControllerWithIdentifier:(NSString *)identifier
{
NSAssert(self.factory, @"TyphoonStoryboard's factory property can't be nil!");

id viewController = [super instantiateViewControllerWithIdentifier:identifier];

[self injectPropertiesForViewController:viewController];

return viewController;
}

- (void)injectPropertiesForViewController:(UIViewController *)viewController
{
if (viewController.typhoonKey.length > 0) {
[self.factory injectProperties:viewController withDefinition:NSSelectorFromString(viewController.typhoonKey)];
} else {
[self.factory injectProperties:viewController];
}

if ([viewController isKindOfClass:[UINavigationController class]]) {
for (UIViewController *controller in ((UINavigationController *)viewController).viewControllers) {
[self injectPropertiesForViewController:controller];
}
}

if ([viewController isKindOfClass:[UITabBarController class]]) {
for (UIViewController *controller in ((UITabBarController *)viewController).viewControllers) {
[self injectPropertiesForViewController:controller];
}
}
}

@end
106 changes: 106 additions & 0 deletions Tests/Definition/Storyboard/Storyboard.storyboard
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="4514" systemVersion="13B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="h33-p8-fk0">
<dependencies>
<deployment defaultVersion="1296" identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3747"/>
</dependencies>
<scenes>
<!--Navigation Controller-->
<scene sceneID="ZHQ-UY-3uY">
<objects>
<navigationController storyboardIdentifier="navigation" definesPresentationContext="YES" id="h33-p8-fk0" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" id="lK6-ll-arL">
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="9MY-L9-RLM" kind="relationship" relationship="rootViewController" id="1Zp-S3-qNl"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="xFW-bq-dYe" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-179" y="-131"/>
</scene>
<!--View Controller - initial_first-->
<scene sceneID="hKB-7p-bzT">
<objects>
<viewController storyboardIdentifier="first" title="initial_first" id="9MY-L9-RLM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="gas-Bq-OmT"/>
<viewControllerLayoutGuide type="bottom" id="dPO-Rz-7Xx"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="eD0-AH-zH8">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
<navigationItem key="navigationItem" id="YY7-R7-W56"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="typhoonKey" value="firstViewController"/>
</userDefinedRuntimeAttributes>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="D64-yN-ygt" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="291" y="-131"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="tRw-Oa-83O">
<objects>
<navigationController storyboardIdentifier="navigation2" definesPresentationContext="YES" id="Wbk-bO-luc" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" id="psP-Rs-A3h">
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="rqb-Fg-W14" kind="relationship" relationship="rootViewController" id="C3Q-UX-ybR"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="XId-ly-UU6" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-179" y="589"/>
</scene>
<!--View Controller - initial_second-->
<scene sceneID="TZ1-o6-Fa4">
<objects>
<viewController storyboardIdentifier="second" title="initial_second" id="rqb-Fg-W14" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y56-x2-XKk"/>
<viewControllerLayoutGuide type="bottom" id="b46-Qx-Exo"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="foa-xB-tuk">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
<navigationItem key="navigationItem" id="idE-S2-aCL"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="typhoonKey" value="secondViewController"/>
</userDefinedRuntimeAttributes>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Fqy-Go-FPM" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="291" y="589"/>
</scene>
<!--Unique View Controller - initial_unique-->
<scene sceneID="381-2Z-vYR">
<objects>
<viewController storyboardIdentifier="unique" title="initial_unique" id="zoM-pS-euc" customClass="UniqueViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="YHr-zg-aFK"/>
<viewControllerLayoutGuide type="bottom" id="ZHd-Zt-DQP"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Pk3-lw-XCb">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="8HT-3h-bxv" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-179" y="1272"/>
</scene>
</scenes>
<simulatedMetricsContainer key="defaultSimulatedMetrics">
<simulatedStatusBarMetrics key="statusBar"/>
<simulatedOrientationMetrics key="orientation"/>
<simulatedScreenMetrics key="destination" type="retina4"/>
</simulatedMetricsContainer>
</document>
61 changes: 61 additions & 0 deletions Tests/Definition/Storyboard/StoryboardTests.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// StoryboardTests.m
// Tests
//
// Created by Aleksey Garbarev on 25.02.14.
//
//

#import <SenTestingKit/SenTestingKit.h>
#import "Typhoon.h"
#import "TyphoonStoryboard.h"
#import "StoryboardViewControllerAssembly.h"
@interface StoryboardTests : SenTestCase

@end

@implementation StoryboardTests {
UIStoryboard *storyboard;
}

- (void)setUp
{
[super setUp];

NSBundle *bundle = [NSBundle bundleForClass:[TyphoonBundleResource class]];

TyphoonComponentFactory *factory = [TyphoonBlockComponentFactory factoryWithAssembly:[StoryboardViewControllerAssembly assembly]];

storyboard = [TyphoonStoryboard storyboardWithName:@"Storyboard" factory:factory bundle:bundle];
}

- (void)test_first
{
UIViewController *controller = [storyboard instantiateViewControllerWithIdentifier:@"first"];
STAssertEqualObjects(controller.title, @"First", nil);
}

- (void)test_first_from_navigation
{
UINavigationController *controller = [storyboard instantiateViewControllerWithIdentifier:@"navigation"];
STAssertEqualObjects([[controller.viewControllers firstObject] title], @"First", nil);
}

- (void)test_second
{
UIViewController *controller = [storyboard instantiateViewControllerWithIdentifier:@"second"];
STAssertEqualObjects(controller.title, @"Second", nil);
}

- (void)test_second_from_navigation
{
UINavigationController *controller = [storyboard instantiateViewControllerWithIdentifier:@"navigation2"];
STAssertEqualObjects([[controller.viewControllers firstObject] title], @"Second", nil);
}

- (void)test_unique
{
UINavigationController *controller = [storyboard instantiateViewControllerWithIdentifier:@"unique"];
STAssertEqualObjects(controller.title, @"Unique", nil);
}
@end
Loading

0 comments on commit a3d1473

Please sign in to comment.