iOS: Using the Factory Design Pattern to Dynamically Change Accessible Properties? - ios

I have the following example where CarTypeTesla is an enum value.
Car *car = [Car carOfType:CarTypeTesla];
+ (instanceType)carOfType: does an enum check and returns an instance of a given Car subclass, like this:
+ (instanceType)carOfType:(CarType)carType {
switch (carType) {
case: CarTypeTesla: {
return [[Tesla alloc] init];
}
case: CarTypeMustang: {
return [[Mustang alloc] init];
}
}
}
So that back in the main file something like this can be done (And I don't have to expose my Tesla, Mustang, and 20 other subclasses):
Car *car = [Car carOfType:CarTypeTesla];
NSLog(#"%#", car.batteryChargeRemaining);
or
Car *car = [Car carOfType:CarTypeMustang];
NSLog(#"%#", car.gasFuelRemaining);
How can I use this Factory Design Pattern, to only display properties / methods related to the returned subclass based on the enum value provided (Wouldn't want to show -(float)gasFuelRemaining when using CarTypeTesla?

What you are implementing is known as a class cluster in iOS. Some framework classes, like NSArray, NSString and NSDictionary work like this (they optimise by providing different solutions based on the amount of data they hold). This allows you to have a generic, common class that's exposed to the API, while hiding all the intricate details for solutions that are not necessarily relevant for developers, including solutions that are different based on context but should behave the same. What this means is you have a generic base class with common methods that are implemented across all other hidden classes.
The way I see it you have to options:
1 - You implement all methods in all your car classes, and have them return empty values when they are not relevant, in which case your Tesla instances would return 0 for gasFuelRemaining OR
2 - You implement protocols for different types of cars, like ElectricCarProtocol and FuelCarProtocol and have a common method in your Car class called fuelRemaining that does something along the lines of this:
if ([self conformsToProtocol:#protocol(ElectricCarProtocol)]) {
return self.batteryChargeRemaining; // you might need to cast the object here
}
return self.gasFuelRemaining; // idem
Hope this helps!

Related

Mocking with Swift

Consider the following class named SomeClass written in Swift:
#objc class SomeClass: NSObject
{
var shouldCallBar = false
func foo()
{
if (shouldCallBar == true)
{
bar()
}
}
func bar()
{
}
}
For testing the above class foo() method (and similar scenarios mostly written in Objective-C) I was using OCMock like:
- (void) testFooBarShouldBeCalledWhenShouldCallBarIsTrue
{
SomeClass * someClass = [SomeClass new];
// Create mocks.
id mockSomeClass = OCMPartialMock(someClass);
// Expect.
[[mockSomeClass expect] bar];
// Stub.
someClass.shouldCallBar = YES;
// Run code under test.
[someClass foo];
// Verify.
[mockSomeClass verify];
// Stop mocking.
[mockSomeClass stopMocking];
}
But above test fails with Swift code as OCMock won't works well with Swift.
So I am considering something like entirely in Swift:
class SomeClassTests: XCTestCase
{
class MockSomeClass: SomeClass
{
var isBarCalled = false
override func bar()
{
isBarCalled = true
}
}
func testBarShouldBeCalledWhenTrue()
{
let someClass = MockSomeClass()
someClass.shouldCallBar = true
someClass.foo()
XCTAssertTrue(someClass.isBarCalled == true)
}
}
Note here I am subclassing the original class under test and overriding the bar(). I am not at all touching the foo() implementation.
But the downside is I am using MockSomeClass instance to test foo() of SomeClass. This is something I don't like and not recommended.
Is there any better solution to the problem above?
Notes:
I am not talking about Dependency Injection here. Dependency Injection is entirely different approach.
I face these kind of issues when testing UI code in UIViewController.
I have thought of Protocol based programming but was not able to come up with solution to problem above.
So, you want to test that one method (foo) does or does not call another method (bar). The foo method is the one under test, and the bar method is, in the wider sense, a dependent component.
If the invocation of bar has lasting side effects, you could get away with testing that the side effect is/isn't present, maybe by querying a property or similar. In that case you don't need mocks or similar.
If there are no side effects then you must substitute the dependency. To do so you need a seam at which you place code that can tell the test whether the method has been invoked or not. For that, I can only see the two options that Jon already discussed in the question you refer to.
You either put the two methods into separate classes, in which case the class boundary is the seam. Using a protocol, or just an informal convention, you can then completely replace the class that implements bar. Dependency injection comes in handy here.
If the two methods must stay in the same class then you have to use a subclass boundary as a seam, i.e. you use the fact that you can override methods and implement a test-specific sublass. It's easiest when you can use a mock framework. If that's not an option you have to write the code yourself, much like what you describe in your question.

How Can I Update a Single Instance of a Class Across Many View Controllers?

Good morning,
I'm designing an application that be used to conduct surveys. A user will open the application, create a "New" survey, answer the associated questions, and then receive some output.
The survey is rather convoluted. Basically, the person is going to be answering the same series of questions about each of their family members. Once the questions are complete, the user will allow the application to take all of that information and perform a certain number of calculations and return a result.
I have a class, called "Survey," which contains properties for the user's basic information (Name, Email Address, etc). I also have a class called "indQuestions," which contains properties that need to have values set for each member of the family (Each one will have a height, weight, DOB, income, etc). Based on the size of the family, the Survey instance will have any number of instances of "indQuestions."
Now, I have to create a single instance of Survey when a new survey is created, but that instance of Survey has to contain X number of instances of indQuestions, and multiple Surveys need to be created based on whether the user wants to do a second survey, change answers to compare returned values, etc for whatever reason.
My application contains many UIViewControllers, since the survey is long and not everything can fit on one page, but the survey is cumulative, and it requires me to be able to access information on each UIViewController that was entered in previous UIViewControllers.
I considered creating a Singleton instance of my Survey class, but ultimately, there has to be multiple instances of Survey, since the customer may opt to do the survey multiple times. Also, the customer has to be able to add an unknown number of instances of "indQuestions" to accommodate the size of the family. Ive been wracking my brain trying to figure out how to find a way to both make a single instance of Survey (and its associated values) available across multiple views while also allowing for the dynamic creation of multiple family members.
I would appreciate any guidance that you can provide. I don't know how to progress with this.
Follow up:
Okay, so I assume I'd use the following instance to create a new survey?
+ (instancetype)startNewSurvey {
static Survey *_instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[Survey alloc] init];
});
return _instance;
}
#end
Would I then use the activateSurvey method to pass the startNewSurvey instance into activeSurvey? I'm trying to follow the logic.
It sounds like you could really benefit from using an sqlite database for your Surveys, but if it's too much work or falls too far out of the design of your application, you could use this kind of strategy:
#interface Survey : NSObject
+ (instancetype)startNewSurvey;
+ (instancetype)activeSurvey;
+ (NSArray *)allSurveys;
+ (void)removeSurvey:(Survey *)survey;
+ (void)activateSurvey:(Survey *)survey;
#property (strong) NSMutableArray *indQuestionsObjects;
#end
It allows you to effectively have a singleton, but track multiple instances at the same time. Rough implementation follows:
#implementation Survey
static Survey *__activeSurvey = nil;
static NSMutableArray *__allSurveys = nil;
+ (instancetype)startNewSurvey {
if (!__allSurveys) {
__allSurveys = [[NSMutableArray alloc] init];
}
Survey *survey = [[Survey alloc] init];
[__allSurveys addObject:survey];
return survey;
}
+ (instancetype)activeSurvey {
if (!__activeSurvey) {
__activeSurvey = [self startNewSurvey];
}
return __activeSurvey;
}
+ (NSArray *)allSurveys {
return __allSurveys;
}
+ (void)removeSurvey:(Survey *)survey {
if (survey) {
[__allSurveys removeObject:survey];
}
}
+ (void)activateSurvey:(Survey *)survey {
__activeSurvey = survey;
}
- (instancetype)init {
if (self = [super init]) {
_indQuestionsObjects = [[NSMutableArray alloc] init];
}
return self;
}
#end

Minimizing header import

I just thought up a simple scenario for myself to play around with NSClassFromString and the Objective-C runtime.
Background:
Imagine I had a class method in which I create an instance from a class based on some condition, let's say:
Class class;
id object;
if(classNumber == 1)
{
//create ClassA
class = NSClassFromString(#"classA");
object = [[class alloc] init];
}
else if(classNumber == 2)
{
//create ClassB
class = NSClassFromString(#"classB");
object = [[class alloc] init];
}
else if(classNumber == 3)
{
//create ClassC
class = NSClassFromString(#"classC");
object = [[class alloc] init];
}
else
{
object = nil;
}
return object;
For the runtime version, I would replace the above and use within the above if-elseif-else respectively:
Class class = objc_allocateClassPair([classA class], "mySubClass", 0);
...
Class class = objc_allocateClassPair([classB class], "mySubClass", 0);
...
Class class = objc_allocateClassPair([classC class], "mySubClass", 0);
...
Both versions go well. But I have to #import nonetheless all these classX.
And given this is a class method and will be called only once, I wonder if there exist some clever way of creating a class without having to import all these headers.
I understand that the answer will most likely be "No", because the compiler needs to know the class and its method signature at compile time.
But my experience is that whenever I say "Ok, I think I am pushing a bit here. Let's settle what seems to work now", someone would come along and would refute my satisfaction by saying "Hey, look here, there is a better/more clever way to do this. It's possible.". So I thought I would ask.
To sum up the question: is it possible to minimize #importing as much as possible, if we only needed to create a class from one of the files that need to be imported? Or is there some clever way where we could dynamically import a header file?
First off, I really don't see the point of this. For one thing, you still need to link the object files for all those classes into the final binary. So you can't minimize the binary size by using reflection and manipulating your includes.
So you can only try to optimize the number of include statements. Is it really worth it, just to remove a couple of lines of code? Of course, you could include the required headers into a new header file, and just include that one. That would cut down the number of includes to 1. Is it worth the trouble? I don't know.

equality of two objects in Objective C [duplicate]

This question already has answers here:
Should you use 'isEqual' or '=='?
(2 answers)
Comparing objects in Obj-C
(4 answers)
Closed 8 years ago.
I am reading the Programming with Objective-C . In the section of Determining Equality of Objects , it says the following words:
- When dealing with objects, the == operator is used to test whether two separate pointers are pointing to the same object:
if (firstPerson == secondPerson) {
// firstPerson is the same object as secondPerson
}
- If you need to test whether two objects represent the same data, you need to call a method like isEqual:, available from NSObject:
if ([firstPerson isEqual:secondPerson]) {
// firstPerson is identical to secondPerson
}
I get confused about the differences between == and isEqual with the above explanation, does it mean firstPerson == secondPerson is an alternative of [firstPerson isEqual:secondPerson] ?
The definition of == is correct, it checks to see that they're pointing to the actual same pointer/memory address (ie. 0xffffff)
The key to understanding what you're asking is to think about what you mean by the word "equal". "equal" typically means, from the developer's point of view, that "these two objects contain the same data in the fields that I require for all practical purposes". You can have two user objects each with the same userID property but different times in lastUpdated - would you consider them equal? Depends on your use case. Most likely you would say yes because they're the same user. They were updated from the server at different times, so some fields differ, but for your implementation, they're equal.
In the case above, are they the same object? Definitely not. They point to different memory addresses. So == would be NO, whereas if you wrote your isEqual: method to check just the userID property, it would return YES
The definition of isEqual: is entirely up to the author of the class. isEqual: can be written to use == if you wanted. All you have to do, in your class, is to override the isEqual: method which is defined by the NSObject protocol.
If you have a custom object, use isEqual: to define what your definition of equal is. In the example of a user object, you might define:
- (BOOL)isEqual:(id)otherObject {
if ([otherObject isKindOfClass:[self class]]) {
MyClass *otherObjectAfterCast = (MyClass*)otherObject;
if ([otherObjectAfterCast.userID isEqualToString:self.userID])
return YES;
}
return NO;
}
Technically you'd probably want to use caseInsensitiveCompare: or something like that but you get the drift...
isEqual: can also be used to trigger other methods - in the case of NSString - calling isEqual: when both operands are strings results in a call to isEqualToString: - which is why the documentation recommends calling isEqualToString: if you know they're both strings, since it's a bit faster.
So, isEqual: is whatever you make of it, or whatever the class author has defined it to be.
This is also a pretty clear definition in the docs (for once lol): NSObject Protocol Reference
Hope this helps! Let me know if you need any further clarification.
NSString *string1 = [[NSString alloc] initWithString:#"some string"];
NSString *string2 = [[NSString alloc] initWithString:#"some string"];
NSString *string3 = string2;
BOOL equal1 = (string1 == string2); // NO
BOOL equal2 = [string1 isEqual:string2]; // YES
BOOL equal3 = (string2 == string3); // YES
BOOL equal4 = [string2 isEqualToString:string3]; // YES
The simple version is this.
== tells you if the pointers are the same object or not.
The isEqual: family of methods do something different.
They tell you if the objects at the other end of the pointers are effectively the same based on some criteria such as the properties or ivars holding equal values or whatever logic is implemented in the method used. They may or may not be the exact same object.

How can I define a variable which could initialise from two different classes?

Sorry about the slightly vague question title but I found it very difficult to get it straight in my head myself.
The issue here is that I have two different dataSources I might be initialising and loading data from. Depending on the data changes which dataSource I need.
The problem I am having is how to define a variable of that dataSource when it could come from two different classes.
If I define them in my interface:
BColumnChartDataSource * chartDatasource = [[BColumnChartDataSource alloc] initWithExercise:_exercise];
BDoubleColumnChartDataSource * chartDatasource = [[BDoubleColumnChartDataSource alloc] initWithExercise:_exercise];
Then it obviously doesn't like them being called the same thing.
If I try to put them in an if statement then they aren't available outside the logic statement
if (_exercise.unitTypeLinks.count < 2) {
BColumnChartDataSource * chartDatasource = [[BColumnChartDataSource alloc] initWithExercise:_exercise];
}
else {
BDoubleColumnChartDataSource * chartDatasource = [[BDoubleColumnChartDataSource alloc] initWithExercise:_exercise];
}
Eventually I want to put them into a statement like this so I could put an if statement into every one of these but it is an awfully verbose way which might take more time if I add more dataSources.
// Get the exercise event list for our clicked exercise
_exerciseEventList = [chartDatasource getExerciseEventList];
I think I must be missing something obvious here so thank you for any help you can give
You should create create a base class, and inherit both of those classes with the base class.
#interface BDataSource : NSObject
#end
#interface BColumnChartDataSource : BDataSource
//your custom implementation here
#end
#interface BDoubleColumnChartDataSource : BDataSource
//your custom implementation here
#end
After that you can initialise your datasource like this
BDataSource *dataSource = nil;
if (_exercise.unitTypeLinks.count < 2) {
dataSource = [[BColumnChartDataSource alloc] initWithExercise:_exercise];
}
else {
dataSource = [[BDoubleColumnChartDataSource alloc] initWithExercise:_exercise];
}
The easiest way is to make sure that BColumnChartDataSource and BDoubleColumnChartDataSource have a common super class. For example, write a super class called BDataSource and make sure that both of the other class are subclass of this.
If that is too difficult to do, the easiest thing to do (which I don't recommend) is to make sure the property is id or NSObject, then do the type checking every time you access the property. This is definitely not ideal and you shouldn't do this. The right thing to do is the previous paragraph.
NSObject *chartDataSource;
if (_exercise.unitTypeLinks.count < 2) {
chartDataSource = [[BColumnChartDataSource alloc] initWithExercise:_exercise];
}
else {
chartDataSource = [[BDoubleColumnChartDataSource alloc] initWithExercise:_exercise];
}
//Now do something with chartDataSource
that handles the exact example you describe, although it leaves quite a bit to be desired as downstream consumers of chartDataSource will probably have to do their own conditioning upon the result of ([chartDataSource isKindOfClass:[BColumnChartDataSource class]])
Better patterns are likely to be found somewhere in the idea of "inheritance", depending on what the actual differences of your two dataSource classes actually are.

Resources