How to extend initWithWindowNibName for an NSWindowController? - objective-c

I'm not sure I am using the correct terminology, but I want to extend and existing method for a class. I want to be able to call
[[CustomWindowViewController alloc] initWithWindowNibName:#"Something" withObject:object];
I want to implement all of the default functionality, of initWithWindowNibName, but then also pass the object as well.
Thanks
Chet

Figured it out
- (id)initWithWindowNibName:(NSString *)windowNibName withObject:(NSObject*)object {
self = [super initWithWindowNibName:windowNibName];
// custom stuff
return self;
}

Related

Calling init in init, before self = [super init]

It is a common init pattern to do self = [super init]; which should assign the self pointer
But can i forward the init like this?
- (id)initWithObject:(id)object {
return [self initWithObject:object scrollTo:nil];
}
The code works, but not sure if it is Kosher... and also how can it work without self = [super init]
moving further, is this ok?
- (id)initWithObject:(id)object {
self = [self initWithObject:object scrollTo:nil]; // NOT super
if (self) {
//...
}
return self;
}
Yes, this is fine. I have done this myself a couple of times without problems, and I found a code example in the Apple documentation (scroll down to "Multiple Initializers and the Designated Initializer").
It is absolutely legitimate only if in return operator you call designated initializer or initializer which calls the one. Make sure that one way or other the designated initializer is called.
Yes, sure you can! Note that initWithObject:scrollTo has to return a valid self object.

Struggling with SKScene inheritance

I have a subclass of SKScene called SPGamePlayScene. It does a bunch of stuff. I need to make another scene that does the same stuff as SPGamePlayScene, but a little more/little differently, so I thought I would subclass my SPGamePlayScene for this new scene, SPPracticeScene. however im having issues with my class level instantiation methods.
below is the method of SPGamePlayScene:
//SPGamePlayScene.m
+ (instancetype)sceneWithSize:(CGSize)size colored:(BOOL)colored {
SPGamePlayScene *gamePlayScene = [SPGamePlayScene sceneWithSize:size];
gamePlayScene.colored = colored;
gamePlayScene.backgroundColor = [SKColor whiteColor];
return gamePlayScene;
}
this works fine. However in my subclass of SKGamePlayScene, SPPracticeScene, I need to create an SPPracticeScene object using its super class's sceneWithSize colored method.
//SPPracticeScene.m which inherits from SPGamePlayScene
+ (instancetype)sceneWithSize:(CGSize)size {
//this keeps giving me a SPGamePlayScene but I need it to be a SPPracticeScene
SPPracticeScene *practiceScene = (SPPracticeScene *)[self sceneWithSize:size colored:NO];
//this line throws an exception because practiceHud is not a property of SPGamePlayScene
practiceScene.practiceHud = [SPPracticeHud practiceHudAtPosition:CGPointMake(0, practiceScene.frame.size.height - 20) inClef:[SPGameState sharedInstance].clef inFrame:practiceScene.frame];
[practiceScene addChild:practiceScene.practiceHud];
return practiceScene;
}
I know that this returns an SPGamePlayScene so I tried casting it with no luck. The line that sets the practiceHud property causes a crash since SPGamePlayScene does not have that property (even though its supposed to be an SPPracticeScene. This is my first foray into custom subclasses/inheritance so im probably misunderstanding something about the way things need to be done/what types need to be returned etc. How can I make this work?
I ended up just overriding the initWithSize method and using an instance level initializer instead of a class level one. using an init method returning an id seemed to do the trick.
//SPGamePlayScene.m
(id)initWithSize:(CGSize)size colored:(BOOL)colored {
if (self = [super initWithSize:size]) {
//some custom code here
}
return self;
}
//SPPracticeScene.m which inherits from SPGamePlayScene
- (id)initWithSize:(CGSize)size {
//init it using the SPGamePlayScene custom initializer
if (self = [super initWithSize:size colored:NO]) {
//some custom code here
}
return self;
}
using the above code let me use the custom initializer for SPPracticeScenes super class, but add additional functionality to it that I needed, while making sure the object returned was of the correct type. I would love to know how I could have made it work with my class level initializers though.

Typhoon - How do I inject a UIView defined in a xib file?

I want to inject a view into my view controller so that I can inject a mock view in my unit tests (WPDependencyInjectorImplementation is my TyphoonAssembly subclass).
My ideal loadView method would look like the following:
- (void)loadView {
WPDependencyInjectorImplementation *injectorAssembly = (WPDependencyInjectorImplementation *) [TyphoonAssembly defaultAssembly];
self.view = [injectorAssembly someView];
}
I'm not sure what the definition for this would look like or whether it's possible, given that the code for creating a view from a xib is the following:
NSArray *views = [[NSBundle mainBundle] loadNibNamed:#"WPSomeView" owner:nil options:nil];
return [views firstObject];
That's right you just override loadView, rather than looking up the view from the container, as you've shown, you should provide the view via an initializer or property setter. And then in loadView, set it to the injected view as follows:
- (void)loadView
{
[self.view = _myInjectedView]; //Do this rather than looking it up via the TyphoonFactory - easier to test.
}
If you do it this way:
You can refer to the view via it's actual type, rather than down-casting from UIView
It'll be really simple to mock-out in a pure unit test. (No need for TyphoonPatcher, swizzling, etc).
Here's an example:
- (id)userDetailsController
{
return [TyphoonDefinition withClass:[UserDetailsController class] initialization:^(TyphoonInitializer* initializer)
{
initializer.selector = #selector(initWithSession:userDetailsView:);
[initializer injectWithDefinition:[self session]];
[initializer injectWithDefinition:[self userDetailsView]];
}];
}
- (id)userDetailsView
{
return [TyphoonDefinition withClass:[UserDetailsView class]
properties:^(TyphoonDefinition* definition)
{
//circular dependency. Can also be set within VC.
[definition injectProperty:#selector(delegate)
withDefinition:[self userDetailsController]];
[definition injectProperty:#selector(sideMargin)
withValueAsText:#"${view.field.default.side.margin}"];
}];
}
Injecting From a Xib
We don't actually have a Xib factory we can provide you yet. It should be a quick job to define one using a similar pattern to the object that emits a theme here, so you'll have a component in the DI container for each of your Xib-based views, and just inject that directly.
Alternatively you could use our new TyphoonFactoryProvider.
If you get stuck, please let us know and one of us will find some time to create and push that Xib-view-factory for you.

How to add another parameter to an existing method

I have a category NSObject+Utilities which contains all kinds of tool methods I use in my projects. Unfortunately I have to add one more parameter to a method that returns NSColor.
Here an Example:
- (NSColor*) ccBlueLight {
return [self libMakeAColor :0.000f :0.535f :1.0f :1.000f];
}
The last parameter (1.000f) is responsible is for transparency. When I created this method I didn´t think of transparency and I fixed it to 1.000f. Since I´m using these cc Colors in different projects I cannot simply add another parameter without getting errors in the other projects when using them.
Is there a way to add the transparency parameter without problems?
Write a new method with transparency parameter:
- (NSColor*) ccBlueLight:(CGFloat)transparency {
return [self libMakeAColor :0.000f :0.535f :1.0f :transparency];
}
And change the implementation of existing method to use default value.
- (NSColor*) ccBlueLight {
return [self ccBlueLight:1.000f];
}
Note that these two are different methods. The old one's signature is not changed and there is no need to change the callers of old one.
Is there anything speaking against simply adding a second method? So that one project could use the original and the other one the new method.
- (NSColor *) ccBlueLightWithAlpha:(NSNumber *)alpha
{
return [self libMakeAColor :0.000f :0.535f :1.0f :[alpha floatValue];
}
Like this?
- (NSColor*) ccBlueLight {
return [self libMakeAColorRed:0.000f green:0.535f blue:1.0f];
}
- (NSColor*) ccBlueLightWithAlpha:(CGFloat)alpha {
return [self libMakeAColorRed:0.000f green:0.535f blue:1.0f alpha:alpha];
}

Designated initializer and calling it

I have general question about designated initializer. I have a some class and from there i want to call a initializer, but before i started to fill my #properties with passing data i want to make data default. For example:
-(id)initWithDefault:(NSDictionary*)defaultTemplate
{
self = [super init];
_fontColor = [defaultTemplate objectForKey:#"color"];
_fontSize = [[defaultTemplate objectForKey:#"size"] intValue];
return self;
}
-(id)initWithTemplate:(NSDictionary*)template
{
self = [self initWithDefault:myDefaultTemplate];
//now i doing something with my template
return self;
}
Is this is a way to prevent null #properties? It this a correct use of designated initializer? Of course you can assume that myDefaultTemplates is not null, and has not null object in keys.
This seems fine with me. I would use the safe way (presented below), but otherwise your code is fine.
-(id)initWithTemplate:(NSDictionary*)template
{
if(self = [self initWithDefault:myDefaultTemplate]) {
//now i doing something with my template
}
return self;
}
Your implementation is perfectly fine, given the fact that _fontColor and _fontSize variables are your local variables of properties.
adig's suggestion is just an enhancement on what you already have implemented. This check takes care of the situation, when your object does not get allocated due to any reason.