Typhoon Storyboard Integration - typhoon

I am using a StoryBoard in my application. When I first started integrating Typhoon, I listed the Assemblies in the plist like so:
<key>TyphoonInitialAssemblies</key>
<array>
<string>ApplicationAssembly</string>
<string>CoreComponents</string>
</array>
This worked fine as I was injecting into the AppDelegate.
Now, if I need to inject into the various view controllers, it appears I have to remove the UILaunchStoryboardName and UIMainStoryboardFile from the application plist file, and use a TyphoonStoryboard like so:
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSString *storyboardName = ...
TyphoonComponentFactory *factory = ...
TyphoonStoryboard *storyboard = [TyphoonStoryboard
storyboardWithName:storyboardName factory:factory bundle:nil];
self.window = ...
self.window.rootViewController = [storyboard instantiateInitialViewController];
[self.window makeKeyAndVisible];
return YES;
}
However, I'm confused where I obtain the TyphoonComponentFactory. Since I already list the assemblies in the plist, can I somehow use that?

it appears I have to remove the UILaunchStoryboardName and UIMainStoryboardFile from the application plist file, and use a TyphoonStoryboard like so
Incorrect. You can combine UIMainStoryboardFile (UILaunchStoryboardName) and TyphoonInitialAssemblies keys in your info.plist file.
In that case, created storyboard will be TyphoonStoryboard and has typhoon factory (created with specified in plist assemblies).
You can use storyboards exactly as you'd normally use them, with the added benefit that dependencies will also be injected, according to the definitions in your one ore more TyphoonAssembly classes.

As Aleksey says in his answer, as long as plist integration is used, along with the usual UILaunchStoryboardName and UIMainStoryboardFile, then Typhoon will ensure that any Storyboard is a TyphoonStoryboard. Use exactly as with a normal storyboard, with the added benefit that dependencies will be injected according to the definitions in your TyphoonAssembly classes.
The TyphoonComponentFactory will be retained by the storyboard and so will persist throughout the lifecycle of your app.
Outside of Storyboards: (ie MacOS apps, utilities, etc)
The TyphoonComopnentFactory is indeed designed to be retained throughout the full life-cycle of your app. (Although you could do something else if you wished).
Key concept:
You can use the TyphoonComponentFactory as is.
Also, any of your assembly interfaces can pose in front of the TyphoonComponentFactory. At build-time an assembly returns definitions. At run-time it returns built components.
There are two ways to retain the TyphoonComponentFactory when proceeding from one object-graph to another. We call this making of your components 'Typhoon aware'.
Approach 1: Inject the assembly:
- (MyAppDelegate *)appDelegate
{
return [TyphoonDefinition withClass:[MyAppDelegate class]
configuration:^(TyphoonDefinition *definition)
{
//Other injections . . .
[definition injectProperty:#selector(factory) with:self];
}];
}
The above example injects the TyphoonComponentFactory into a property called factory.
When you inject the assembly it can be used as a TyphoonComponentFactory.
It can also be used as any of your assembly types. For example, you could declare a property components of type CoreCompopnents and inject the assembly as that.
More info on this feature can be found in the User Guide here.
Approach 2: Use callback hook:
Another way of making a component 'Typhoon aware' is to use Typhoon's callback hooks. by overriding NSObject category methods:
typhoonSetFactory:(id)thefactory
As with the other approach above, the factory can be used as a TyphoonComponentFactory or any of your assembly interfaces my pose in front, both of the following are fine:
typohoonSetFactory:(TyphoonComponentFactory*)factory
{
//Do something with factory
}
typhoonSetFactory:(ApplicationAssembly*)assembly
{
//Do something with assembly
}
Of the two approaches, use the one that suits you best. We recommend the former, as it 'non-invasive' meaning your own classes don't directly call any Typhoon APIs. If you ever wished to migrate away from Typhoon, you would simply provide an alternative implementation of the assembly.
Proceeding from one object graph to another:
Once a component is 'Typhoon aware' using either of the above methods, we can use this to proceed from one object graph to another.
The default scope for Typhoon is TyphoonScopeObjectGraph, meaning you can load a view controller, including any delegates and circular references. Upon completion, it will be discarded from memory.
Meanwhile any components of TyphoonScopeSingleton (or TyphoonScopeWeakSingleton) will be retained.
More information on this feature is in the User Guide here.
Summary:
The assembly follows normal Objective-C/Swift memory rules. So as long as its being used by at least one of your classes, it will continue to persist. Using the process of 'proceeding from one object graph to another' described above means that it will persist throughout the life-cycle of your app.
stackoverflow.com/questions/26492175/typhoon-storyboard-integration

Related

WatchKit thread issue between classes

I have two classes named InterfaceController and LoadInterfaceController.
I'm calling InterfaceController's uiChange function from my LoadInterfaceController:
InterfaceController *interfaceController = [InterfaceController alloc];
//[interfaceController performSelectorOnMainThread:#selector(uiChange) withObject:nil waitUntilDone:true];
[interfaceController uiChange];
The function is called, but the UI in InterfaceController isn't modified.
- (void)uiChange {
NSLog(#"uiChange was called");
//... make changes to the UI ...
}
If the function is called from a function originating from InterfaceController class the UI is changed, respectively.
I have tried calling uiChange on the main thread (as explained here), but the UI isn't responding. How may I specify the thread used for InterfaceController's UI?
The same issue as here. Do not initialise controllers on your own, let Watch do it as a user flow happens.
I'd suggest adding into your architecture a Pub/Sub pattern. The NSNotificationCenter class is a good example that implements one. It allows parts of application to communicate between each other and it is used really often for controllers communication as well.
Here is a good example of communication between an AppDelegate and controller that I provided answering to another question recently. But if you really need I could adopt it for your example.

Xcode Cocos2d Can't Create AppDelegate Object

I am working with cocos2d and CoreData. I have imported "AppDelegate.h" but I can't create an object from it. I would think that all I would do is:
AppDelegate *delegate;
When I do this it get an error saying AppDelegate is not defined. The AppDelegate .h and .m files are next to the main file.
Also, when I try to write to a file I don't get an error but it does not write.
The boiler plate code created by XCode (I'm assuming you are using XCode) will create an NSManagedObjectContext for you called *managedObjectContext. If you need to create other objects that are going to interact with your core data model (such as a view controller), you simply pass in that managedObjectContext object as an argument (or link it to a property in your custom class) and interact with it in your custom class. It's worth noting that it's not good practice to be passing around an AppDelegate object in your app. Your app delegate should be at the foundation of your code base and not treated as a typical class. There are definitely times when you will want (or need) to pass the app delegate as an object or reference it in IB, but typically your app will launch in your
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
method and you will launch your primary view controller from there.
Some things in core data also require the persistent store coordinator (such as retrieving managed object ID's), so you may need to pass that in as well. If you need some help working with Core Data, there are a bunch of great articles and books on the subject. I recommend reading as many as you can, since Core Data can be difficult to comprehend at first. It helped me to read a bunch of tutorials at first since everyone explains it a little differently.
Here's a great recent tutorial written on Bindings, Core Data, and working with the app delegate to get you started: http://www.raywenderlich.com/21752/how-to-use-cocoa-bindings-and-core-data-in-a-mac-app
In Cocos2d 2.0, AppDelegate is renamed to AppController
AppController *app = (AppController*)[UIApplication sharedApplication].delegate;

Is there design pattern for showing same view controller in several places with some customization?

Very frequently we reuse same view controllers when developing universal apps both for iPhone and iPad. But frequently some customization is needed, like:
IF iPad THEN
...
ELSE
...
So, in order to achieve such customization the controller might have some property that is set during initialization of the controller, or there might be custom constructors. Just curious is there design pattern that suites for such situations.
Don't.... :) Use a common class called for instance MyClass and then sub-class it MyClass-iPad & MyClass-iPhone and use two different XIB for each. Avoid using this kind of stuff (there is no need for it).
Explanation:
The iPad version should only be aware of classes of the type Something-iPad this makes the code clean and creates a well defined architecture. If I jump into your code and someone tell's me: "Ok Jacky Boy, you have to make changes on the iPad version". I won't care to look ath the Something-iPhone classes. Most of the logic (business) should be on super class Something where the small tweaks should be on the sub-classes.
On side note, on most of my projects, normally I don't have anything on the Something-iPhone classes, because the design is done on the XIB. On the Something-iPad I would normally keep a reference to a UIPopOverController (just an example) and some rotations tweaks.
Edit 1:
I would receive an NSDictionary on the init of the UIViewController, like this:
initWithNibName:bundle:configurationDictionary:
After receiving this configurationDictionary, I would then use it on the viewDidLoad (for example). You could then do some cool stuff like this:
- (void)viewDidLoad
{
[[self view] setBackgroundColor:[[self configurationDictionary] objectForKey:BACKGROUND_COLOR_KEY]];
}
If you have different initializers or larger chunks of different functionality then it makes sense to define a base class with the core functionality and then an iPad-specific subclass and an iPhone-specific subclass.
But in cases where you only have a trivial difference (for example, displaying an action sheet), then I would simply use something like:
- (void)someMethod {
// a bunch of stuff that is the same
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// one or two lines for iPad
} else {
// one or two lines for iPhone
}
}
I have plenty of situations where I do both - subclass for the bigger differences and use UI_USER_INTERFACE_IDIOM for trivial differences in the base class.
If you need the same VC in N places but each time initialized a little differently I would move the logic in a specific factory class / category on the VC
like its done with for example SLRequest objects / in ShareKit

NSUserDefaults IOS Accessible Everywhere?

I am wondering if the NSUserDefaults object is shared and can be accessed from within the app delegate as well as within several of my view controllers. Basically I need to pass data from the app delegate back and forth to the view controllers. I don't want to use a singleton. I was wondering if the NSUserDefauflts object was a way to do this.
If this is possible, how would I initialize the object so that is possible?
Thanks!
If you just use [NSUserDefaults standardUserDefaults], the same instance will be returned every time. Different classes can then use it to store data that is persistent across sessions.
If you're just trying to pass data between parts of the app, but not store it, user defaults are not the appropriate way to do so. You should expose methods or properties on your classes that take as input the data you need to pass.
Well, it is but that's not really what it's designed for. The normal design pattern is to pass the objects back and forth between your view controllers "manually." You want your view controllers to be as independent -- reusable -- from the rest of your application as possible. Tying them to NSUserDefaults isn't a good way to do that!
You should not be doing any processing in your app delegate. Ideally, you should initialise your window, root view controller (if not doing it by storyboard) and model and that's it. All processing should be done elsewhere (mostly in view controllers talking to the model classes).
Make your root model class a singleton so that all your view controllers can talk to it via an interface of your choosing.
Making a singleton is not hard:
#interface MyModel: NSObject
+ (MyModel *)sharedModel;
#end
and the implementation:
#implementation MyModel
+ (MyModel *)sharedModel
{
static MyModel* modelSingleton = nil;
static dispatch_once_t once;
dispatch_once(&once, ^{
modelSingleton = [[MyModel alloc] init];
});
return modelSingleton;
}
#end
And then you just use:
[MyModel sharedModel]
to access it.

Override Interface Builder instantiation of objects?

I'm developing for the iPhone, and I have a class DataManager, that is used to maintain my application data. When the application launches/exits, the data is read from/written to disk to create an instance of this class, using the NSKeyedArchiver (and Unarchiver) classes, since the DataManager adheres to the NSCoding protocol.
One problem I'm having is that I need the DataManager to be accessible by many of my other IB classes, so it is defined as an object in IB, and those classes have an outlet to it. The DataManager is being created using the standard init: method (or maybe initWithCoder:?), but since IB doesn't have the proper file (or NSData from the file) to instantiate the object, it has no initial contents.
So, is there any way to tell IB not to instantiate the class automatically? This will instead be performed using my application delegate, something like:
AppDelegate.h
IBOutlet DataContext *context;
AppDelegate.m
context = [NSKeyedUnarchiver unarchiveObjectWithData:dataLoadedFromFile];
As you can see, this presents a problem. Wouldn't the context be instantiated twice, once by InterfaceBuilder, then a second time by my application delegate?
I would like to prevent maintaining the context as an ivar in the delegate, since that seems to stray from the MVC paradigm, and leans toward the singleton pattern instead. (The controller should not be responsible for the data in my mind. It can maintain a reference to it obviously, but should not be responsible for offering it to other classes.)
When the application launches/exits, the data is read from/written to disk to create an instance of this class, using the NSKeyedArchiver (and Unarchiver) classes, since the DataManager adheres to the NSCoding protocol.
One problem I'm having is that I need the DataManager to be accessible by many of my other IB classes, so it is defined as an object in IB … As you can see, this presents a problem. Wouldn't the context be instantiated twice, once by InterfaceBuilder, then a second time by my application delegate?
Yup.
First, you should think about whether this is a controller or a model object. It sounds to me like it's a controller.
If it is, then you should move the model to a separate object or objects, and make those NSCoding-compliant, and make the data manager load and save those objects. A bonus of this solution is that you could tell the data manager to save the objects and purge them when you get a low-memory warning, not just at quit time.