Accessing readonly property in testing iOS Swift - objective-c

I am pretty new to iOS and Swift and currently I'm facing a problem with writing a Unit Test. I have a class (let's suppose it is called A) which has (readonly property from Objective-C) and in my test I want to have object of this class to pass to the method which later does something with it. Oh, I don't have any initializers as well... My question is, how to test such think? Maybe I have to mock such object somehow?
------EDIT-----
Okay. My post wasn't quite precise. Okay, so I know only basics of Swift (unfortunately I don't have time now to learn Objective - C as I was asked to write sth in Swift). I have a class provided by some company in which I have a class (written in Objective-C) like:
#interface MainClassX : NSObject
#property (nonatomic, strong, readonly) NSString* code;
#property (nonatomic, strong, readonly) NSArray<XYZ*>* classification;
#end
And in my test want to create an object of this class and init at least 'code' property... but the setter is private so I can't do any 'inheritance trick'...? Is there any option to do it or should I do it another way? The problem is that I want to test a method which takes array of such objects and does sth with them.

It's pretty tricky because they wanted those properties to be readonly, why do you want to test them?
Regardless the purpose, you can do these steps:
1. Look into adding methods to that class using Category (in Objective C), or extension(in Swift).
2. Implement that new init method, set the code properpty using Key-Value Programming
I have managed to do it real quick in Objective C, it's pretty straight forward to convert to Swift.
#implementation MainClassX(Test)
-(instancetype)initWithCode:(NSString *)code {
self = [self init];
if (self) {
[self setValue:code forKey:#"code"];
}
return self;
}
#end
Test it:
MainClassX *test = [[MainClassX alloc] initWithCode:#"TEST"];
NSLog(#"code: %#", test.code); // Should print out "TEST" in the console
Swift:
extension MainClassX {
convenience init(_ code: String) {
self.init()
self.setValue(code, forKey: "code")
}
}
In the unit test:
import XCTest
#testable import YourAppModule
class YourAppModuleTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
let cls = MainClassX("TEST")
XCTAssert(cls.code == "TEST")
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}

You are likely looking for dependancy injection. This is a way that you can initialize a class with an optional value that can set values for testing, as you want to do.
The following is a simple example.
Create an optional initialize for your Objective-C class:
- (instancetype)initWithOption:(NSString *)option {
self = [super init];
if (self) {
self.option = option;
}
return self;
}
You can have it such that when you normally call the class, you call its default init. But for testing, initialize it with this function. Another thing to consider if you may want to have a protected header file (such as classname_protected.h) which you only use in your unit tests so that you do not expose this function to your application.
Without seeing more of your test, it is a bit difficult to add on to this, but DI is likely where you need to go for it.

Related

Is there a way to test an instance variable in objective c?

I need to test an instance variable in Objective C.
I have this snippet in myViewController.m
#implementation myViewController
{
NSView *myView;
}
I need to add a test in myViewControllerTest.mm that tests a case when myView.window happens to be nil
-(void) myTest
{
// 1. assert condition 1 is true
// 2. mock myView.window and set it to nil OR set myView.window directly to nil
// 3. assert condition 1 is false
}
I'd like to know the best approach to achieve step 2 in the test. Approaches I tried:
Add an explicit setter - (void)setMyView:(NSView*)myView just for the unit test purpose and use that in myTest.
I have a feeling that this is not a good way.
make myView as a property and put it in myViewController.h so that I'll have a setter without explicitly defining one.
Unsuccessful attempt at mocking using OCMock. (need to explore further)
Please suggest what the best approach is and what are the pros/cons of them.
Thanks !
Edit : Answer I ended up using in my test file.
[myViewController setValue:nil forKeyPath:#"myViewControl.window"];
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueCoding/BasicPrinciples.html#//apple_ref/doc/uid/20002170-BAJEAIEE
You can add setter only for testing in your test file myViewControllerTest.mm
This has no effect to your production code.
like below using Extension
// Write this in test file.
#interface MyViewController(Test)
- (void)setMyView:(NSView *)myView;
#end
or just use valueForKey: to get private field.
-(void) myTest
{
id myViewController = ...;
NSView *myView = [myViewController valueForKey:#"myView"];
myView.window = nil;
// ...
}

What is the equivalent of + (void)load {} in Swift 4? [duplicate]

In Objective-C, NSObject had a class method called load that gets called when the class is loaded for the first time. What is the equivalent in Swift?
#implementation MyClass
+ (void)load
{
[self registerClass];
}
#end
Prior to Swift 1.2:
override class func load() {
NSLog("load");
}
EDIT:
As of Swift 1.2 you can no longer override the load method. Look into the method initialize instead, it behaves different than load though, it get's called the first time the class is being referenced somewhere rather than on application initial load
Support for overriding load was removed in Swift 1.2
Update: Starting from Swift 5 class extensions and categories on Swift classes are not allowed to have +load methods, +initialize doesn’t seem to be prohibited, though.
While direct equivalent is not available with Swift, this can be achieved relatively elegantly with Objective-C and categories. Foo should still inherit from NSObject and have a class / static non-private swiftyLoad/Initialize methods, which get invoked from Objective-C in Loader.m, which you include in compile sources alongside Foo.swift:
# Loader.m
#import <Foundation/Foundation.h>
#import <MyProject/MyProject-Swift.h>
// This was a more concise solution in Swift 4.2 and earlier. If you try
// this with Swift 5 you'd get "Swift class extensions and categories
// on Swift classes are not allowed to have +load methods" runtime error.
// #implementation Foo (private)
// + (void)load { [self swiftyLoad]; }
// + (void)initialize { [self swiftyInitialize]; }
// #end
// This is what you'd want to use with Swift 5 onwards, basically
// just a pure Objective-C defined class.
#interface Loader : NSObject
#end
#implementation Loader : NSObject
+ (void)load { [Foo swiftyLoad]; }
+ (void)initialize { [Foo swiftyInitialize]; }
#end
# Foo.swift
import Foundation
public class Foo: NSObject {
#objc public static func swiftyLoad() {
Swift.print("Rock 'n' roll!")
}
#objc public static func swiftyInitialize() {
Swift.print("Hello initialize!")
}
}
The best part there's no need to mess with bridging headers or anything else, it just works. There are couple of gotchas, especially when using this approach in static libraries, check out the complete post on Medium for details! ✌️
in Swift 2.0, Please use this method
public override class func initialize()
What is the equivalent in Swift?
There is none.
Unlike stored instance properties, you must always give stored type properties a default value. This is because the type itself does not have an initializer that can assign a value to a stored type property at initialization time.
Source: "The Swift Programming Language", by Apple
Types simply don't have an initializer in Swift. As several answers here suggest, you may be able to work around that by using the Objective-C bridge and having your Swift class inherit from NSObject or by using a Objective-C loader bootstrap code but if you have a 100% Swift project, you basically only have two options:
Manually initialize your classes somewhere within the code:
class MyCoolClass {
static func initClass ( ) {
// Do your init stuff here...
}
}
// Somewhere in a method/function that is guaranteed to be called
// on app startup or at some other relevant event:
MyCoolClass.initClass()
Use a run once pattern in all methods that require an initialized class:
class MyCoolClass {
private static var classInitialized: Bool = {
// Do your init stuff here...
// This code will for sure only run once!
return true
}()
private static func ensureClassIsInitialized ( ) {
_ = self.classInitialized
}
func whateverA ( ... ) {
MyCoolClass.ensureClassIsInitialized()
// Do something...
}
func whateverB ( ... ) {
MyCoolClass.ensureClassIsInitialized()
// Do something...
}
func whateverC ( ... ) {
MyCoolClass.ensureClassIsInitialized()
// Do something...
}
}
This follows a clean pattern that no code in Swift runs "automagically", code only runs if other code instructs it to run, which provides a clear and trackable code flow.
There might be a better solution for special cases yet you have not provided any information as for why you need that feature to begin with.
The abilitity to use this method was removed in this PR.
It provides some rationale:
Swift's language model doesn't guarantee that type metadata will ever really be used, which makes overriding initialize() error-prone and not really any better than manually invoking an initialization function. Warn about this for Swift 3 compatibility and reject attempts to override +initialize in Swift 4.
For Swift 2 or 3 (i.e. post-Swift 1.2), you can use:
class MySwiftClass: NSObject {
internal override class func initialize() {
DoStuff()
super.initialize()
}
}
But, as you can see, your class needs to inherit (directly or indirectly) form NSObject. This is required because the initialize() is called by the ObjC runtime.
And the initialize() method will only be called when MySwiftClass is referenced. So it will not be as magic as load().
But it will also be safer. For example: including a framework (let's say, by just adding it to your Podfile) won't allow the framework to mysteriously start to behave as soon as your app launches, without the need to add a single line of code to your project (at least… I hope! 😉).
I get conclusion for swift 1.2~5 doable ways:
Doable |swift-load |swift-initialze|bridgeToOC-load|bridgeToOC-initialze|
--- |---- |--- |--- |--- |
swift1.2~4.2|X |O |O |O |
swift4.2~5.0|X |X |O |O |
swift5.0~? |X |X |X |O |
and how to make it ? check here
https://medium.com/post-mortem/using-nsobjects-load-and-initialize-from-swift-f6f2c6d9aad0
but I think here is another way to make it swifter. use runtime and protol without load/initialze.
http://jordansmith.io/handling-the-deprecation-of-initialize/
Swift 5
Method 'load()' defines Objective-C class method 'load', which is not permitted by Swift
You can not override load() method now but there is a legal way to call your custom swift_load() method of your swift classes on app startup. It is needed to make ObjC class which redirect its own load() method to your swift classes.
Just add next SwiftLoader.m file to your project:
// SwiftLoader.m
#interface SwiftLoader : NSObject
#end
#implementation SwiftLoader
+ (void)load {
SEL selector = #selector(swift_load);
int numClasses = objc_getClassList(NULL, 0);
Class* classes = (Class *)malloc(sizeof(Class) * numClasses);
numClasses = objc_getClassList(classes, numClasses);
for (int i = 0; i < numClasses; i++) {
Class class = classes[i];
Method method = class_getClassMethod(class, selector);
if (method != NULL) {
IMP imp = method_getImplementation(method);
((id (*)(Class, SEL))imp)(class, selector);
}
}
free(classes);
}
Now you can add swift_load class method to any of your swift classes to make setup tasks:
class MyClass {
#objc
class func swift_load() {
print("load")
}
}

Swift - Objective-C load class method?

In Objective-C, NSObject had a class method called load that gets called when the class is loaded for the first time. What is the equivalent in Swift?
#implementation MyClass
+ (void)load
{
[self registerClass];
}
#end
Prior to Swift 1.2:
override class func load() {
NSLog("load");
}
EDIT:
As of Swift 1.2 you can no longer override the load method. Look into the method initialize instead, it behaves different than load though, it get's called the first time the class is being referenced somewhere rather than on application initial load
Support for overriding load was removed in Swift 1.2
Update: Starting from Swift 5 class extensions and categories on Swift classes are not allowed to have +load methods, +initialize doesn’t seem to be prohibited, though.
While direct equivalent is not available with Swift, this can be achieved relatively elegantly with Objective-C and categories. Foo should still inherit from NSObject and have a class / static non-private swiftyLoad/Initialize methods, which get invoked from Objective-C in Loader.m, which you include in compile sources alongside Foo.swift:
# Loader.m
#import <Foundation/Foundation.h>
#import <MyProject/MyProject-Swift.h>
// This was a more concise solution in Swift 4.2 and earlier. If you try
// this with Swift 5 you'd get "Swift class extensions and categories
// on Swift classes are not allowed to have +load methods" runtime error.
// #implementation Foo (private)
// + (void)load { [self swiftyLoad]; }
// + (void)initialize { [self swiftyInitialize]; }
// #end
// This is what you'd want to use with Swift 5 onwards, basically
// just a pure Objective-C defined class.
#interface Loader : NSObject
#end
#implementation Loader : NSObject
+ (void)load { [Foo swiftyLoad]; }
+ (void)initialize { [Foo swiftyInitialize]; }
#end
# Foo.swift
import Foundation
public class Foo: NSObject {
#objc public static func swiftyLoad() {
Swift.print("Rock 'n' roll!")
}
#objc public static func swiftyInitialize() {
Swift.print("Hello initialize!")
}
}
The best part there's no need to mess with bridging headers or anything else, it just works. There are couple of gotchas, especially when using this approach in static libraries, check out the complete post on Medium for details! ✌️
in Swift 2.0, Please use this method
public override class func initialize()
What is the equivalent in Swift?
There is none.
Unlike stored instance properties, you must always give stored type properties a default value. This is because the type itself does not have an initializer that can assign a value to a stored type property at initialization time.
Source: "The Swift Programming Language", by Apple
Types simply don't have an initializer in Swift. As several answers here suggest, you may be able to work around that by using the Objective-C bridge and having your Swift class inherit from NSObject or by using a Objective-C loader bootstrap code but if you have a 100% Swift project, you basically only have two options:
Manually initialize your classes somewhere within the code:
class MyCoolClass {
static func initClass ( ) {
// Do your init stuff here...
}
}
// Somewhere in a method/function that is guaranteed to be called
// on app startup or at some other relevant event:
MyCoolClass.initClass()
Use a run once pattern in all methods that require an initialized class:
class MyCoolClass {
private static var classInitialized: Bool = {
// Do your init stuff here...
// This code will for sure only run once!
return true
}()
private static func ensureClassIsInitialized ( ) {
_ = self.classInitialized
}
func whateverA ( ... ) {
MyCoolClass.ensureClassIsInitialized()
// Do something...
}
func whateverB ( ... ) {
MyCoolClass.ensureClassIsInitialized()
// Do something...
}
func whateverC ( ... ) {
MyCoolClass.ensureClassIsInitialized()
// Do something...
}
}
This follows a clean pattern that no code in Swift runs "automagically", code only runs if other code instructs it to run, which provides a clear and trackable code flow.
There might be a better solution for special cases yet you have not provided any information as for why you need that feature to begin with.
The abilitity to use this method was removed in this PR.
It provides some rationale:
Swift's language model doesn't guarantee that type metadata will ever really be used, which makes overriding initialize() error-prone and not really any better than manually invoking an initialization function. Warn about this for Swift 3 compatibility and reject attempts to override +initialize in Swift 4.
For Swift 2 or 3 (i.e. post-Swift 1.2), you can use:
class MySwiftClass: NSObject {
internal override class func initialize() {
DoStuff()
super.initialize()
}
}
But, as you can see, your class needs to inherit (directly or indirectly) form NSObject. This is required because the initialize() is called by the ObjC runtime.
And the initialize() method will only be called when MySwiftClass is referenced. So it will not be as magic as load().
But it will also be safer. For example: including a framework (let's say, by just adding it to your Podfile) won't allow the framework to mysteriously start to behave as soon as your app launches, without the need to add a single line of code to your project (at least… I hope! 😉).
I get conclusion for swift 1.2~5 doable ways:
Doable |swift-load |swift-initialze|bridgeToOC-load|bridgeToOC-initialze|
--- |---- |--- |--- |--- |
swift1.2~4.2|X |O |O |O |
swift4.2~5.0|X |X |O |O |
swift5.0~? |X |X |X |O |
and how to make it ? check here
https://medium.com/post-mortem/using-nsobjects-load-and-initialize-from-swift-f6f2c6d9aad0
but I think here is another way to make it swifter. use runtime and protol without load/initialze.
http://jordansmith.io/handling-the-deprecation-of-initialize/
Swift 5
Method 'load()' defines Objective-C class method 'load', which is not permitted by Swift
You can not override load() method now but there is a legal way to call your custom swift_load() method of your swift classes on app startup. It is needed to make ObjC class which redirect its own load() method to your swift classes.
Just add next SwiftLoader.m file to your project:
// SwiftLoader.m
#interface SwiftLoader : NSObject
#end
#implementation SwiftLoader
+ (void)load {
SEL selector = #selector(swift_load);
int numClasses = objc_getClassList(NULL, 0);
Class* classes = (Class *)malloc(sizeof(Class) * numClasses);
numClasses = objc_getClassList(classes, numClasses);
for (int i = 0; i < numClasses; i++) {
Class class = classes[i];
Method method = class_getClassMethod(class, selector);
if (method != NULL) {
IMP imp = method_getImplementation(method);
((id (*)(Class, SEL))imp)(class, selector);
}
}
free(classes);
}
Now you can add swift_load class method to any of your swift classes to make setup tasks:
class MyClass {
#objc
class func swift_load() {
print("load")
}
}

Create categories, inherance or delegate?

I am having a little problem. My scenario is: I will build a project with a lot of target. I want have the "public code" (for all target) and "specific code". The problem is: In "public code", I NEED call function at "specific code".
My first try was using categories. I create "public.h" then "public+specific.h" codes using categories. The class that will use this class will need to:
#import "public+specific.h"
...
public *myClass = [[public alloc] init];
[myclass doSomething];
To use another specific class, i only need to change the #import and nothing more. The unique problem is that in "public class" i will need create a false function, like.
//public.h
#interface public : NSObject {}
...
- (void) doSomething {return };
//public+specific.h
#interface public (specific)
...
- (void) doSomething { //do what it really have to do };
The other problems is intrinsic to categories: I can't create local class variable, all will have to be declared in "public.h". I want have all specific things IN specific class...
Ok, so I try in another way: use Inheritance with delegates. In the classes "public.h" and "public+specific.h" it work very well, no need to use fake function, all was fine. BUT, (aways a but), I always will have to alloc the specific class, and if I don't want this, I can create a fake function only to call the delegate, so I have the same problem above. This is a sample:
//In public.h
#protocol publicDelegate
-(void)doSomething;
#end
#interface public : NSObject {
id <publicDelegate> myDelegate;
}
-(id)initWithDelegate (id <publicDelegate>)initDelegate{
myDelegate = initDelegate;
[myDelegate doSomehing];
}
//public+specific.h //The '+' isn't correct here :P
#include public.h
#interface public_specific : public <publicDelegate> {}
- (id)init{
return [super initWithDelegate:self];
}
- (void) doSomething { //do what it really have to do };
Like I say, the problem here is how I create this object
#import "public+specific.h"
...
public_specific *myClass = [[public_specific alloc] init];
[myClass doSomething];
With this, I will have to create a lot of #if defined , #elif defined... every time that I need to create a object call. With categories, I only need to do this with the "#include".
To solve this problem, I can have things like this:
//in "public.h"
- (void) doSomething {
return [myDelegate doSomehing]
};
Another time I will create fake function. And worst, for every new function in "public+specific.h" I will have to create another fake function.. zzz.. (in categories, i have to do this only with function with "public.h" call in "public+specific.h")
So, anyone have another idea to this problem?? It's a little problem, but I want to make my code good, easy to develop and clean...
in many cases, composition would serve you well.

Objective-C Static Class Level variables

I have a class Film, each of which stores a unique ID. In C#, Java etc I can define a static int currentID and each time i set the ID i can increase the currentID and the change occurs at the class level not object level. Can this be done in Objective-C? I've found it very hard to find an answer for this.
Issue Description:
You want your ClassA to have a ClassB class variable.
You are using Objective-C as programming language.
Objective-C does not support class variables as C++ does.
One Alternative:
Simulate a class variable behavior using Objective-C features
Declare/Define an static variable within the classA.m so it will be only accessible for the classA methods (and everything you put inside classA.m).
Overwrite the NSObject initialize class method to initialize just once the static variable with an instance of ClassB.
You will be wondering, why should I overwrite the NSObject initialize method. Apple documentation about this method has the answer: "The runtime sends initialize to each class in a program exactly one time just before the class, or any class that inherits from it, is sent its first message from within the program. (Thus the method may never be invoked if the class is not used.)".
Feel free to use the static variable within any ClassA class/instance method.
Code sample:
file: classA.m
static ClassB *classVariableName = nil;
#implementation ClassA
...
+(void) initialize
{
if (! classVariableName)
classVariableName = [[ClassB alloc] init];
}
+(void) classMethodName
{
[classVariableName doSomething];
}
-(void) instanceMethodName
{
[classVariableName doSomething];
}
...
#end
References:
Class variables explained comparing Objective-C and C++ approaches
As of Xcode 8, you can define class properties in Obj-C. This has been added to interoperate with Swift's static properties.
Objective-C now supports class properties, which interoperate with Swift type properties. They are declared as: #property (class) NSString *someStringProperty;. They are never synthesized. (23891898)
Here is an example
#interface YourClass : NSObject
#property (class, nonatomic, assign) NSInteger currentId;
#end
#implementation YourClass
static NSInteger _currentId = 0;
+ (NSInteger)currentId {
return _currentId;
}
+ (void)setCurrentId:(NSInteger)newValue {
_currentId = newValue;
}
#end
Then you can access it like this:
YourClass.currentId = 1;
val = YourClass.currentId;
Here is a very interesting explanatory post I used as a reference to edit this old answer.
2011 Answer: (don't use this, it's terrible)
If you really really don't want to declare a global variable, there another option, maybe not very orthodox :-), but works... You can declare a "get&set" method like this, with an static variable inside:
+ (NSString*)testHolder:(NSString*)_test {
static NSString *test;
if(_test != nil) {
if(test != nil)
[test release];
test = [_test retain];
}
// if(test == nil)
// test = #"Initialize the var here if you need to";
return test;
}
So, if you need to get the value, just call:
NSString *testVal = [MyClass testHolder:nil]
And then, when you want to set it:
[MyClass testHolder:testVal]
In the case you want to be able to set this pseudo-static-var to nil, you can declare testHolder as this:
+ (NSString*)testHolderSet:(BOOL)shouldSet newValue:(NSString*)_test {
static NSString *test;
if(shouldSet) {
if(test != nil)
[test release];
test = [_test retain];
}
return test;
}
And two handy methods:
+ (NSString*)test {
return [MyClass testHolderSet:NO newValue:nil];
}
+ (void)setTest:(NSString*)_test {
[MyClass testHolderSet:YES newValue:_test];
}
Hope it helps! Good luck.
On your .m file, you can declare a variable as static:
static ClassName *variableName = nil;
Then you can initialize it on your +(void)initialize method.
Please note that this is a plain C static variable and is not static in the sense Java or C# consider it, but will yield similar results.
In your .m file, declare a file global variable:
static int currentID = 1;
then in your init routine, refernce that:
- (id) init
{
self = [super init];
if (self != nil) {
_myID = currentID++; // not thread safe
}
return self;
}
or if it needs to change at some other time (eg in your openConnection method), then increment it there. Remember it is not thread safe as is, you'll need to do syncronization (or better yet, use an atomic add) if there may be any threading issues.
As pgb said, there are no "class variables," only "instance variables." The objective-c way of doing class variables is a static global variable inside the .m file of the class. The "static" ensures that the variable can not be used outside of that file (i.e. it can't be extern).
Here would be an option:
+(int)getId{
static int id;
//Do anything you need to update the ID here
return id;
}
Note that this method will be the only method to access id, so you will have to update it somehow in this code.
(Strictly speaking not an answer to the question, but in my experience likely to be useful when looking for class variables)
A class method can often play many of the roles a class variable would in other languages (e.g. changed configuration during tests):
#interface MyCls: NSObject
+ (NSString*)theNameThing;
- (void)doTheThing;
#end
#implementation
+ (NSString*)theNameThing { return #"Something general"; }
- (void)doTheThing {
[SomeResource changeSomething:[self.class theNameThing]];
}
#end
#interface MySpecialCase: MyCls
#end
#implementation
+ (NSString*)theNameThing { return #"Something specific"; }
#end
Now, an object of class MyCls calls Resource:changeSomething: with the string #"Something general" upon a call to doTheThing:, but an object derived from MySpecialCase with the string #"Something specific".
u can rename the class as classA.mm and add C++ features in it.
Another possibility would be to have a little NSNumber subclass singleton.