Stub -[NSDate init] - objective-c

Stubbing NSDate to return a mock date can easily be done using category except for -[NSDate init]. -[NSDate init] is not called unlike other methods. class_addMethod does not help. method_exchangeImplementations, method_setImplementation on -[NSDate init] actually change -[NSObject init] but no effect on -[NSDate init].
[NSDate setMockDate:[NSDate dateWithTimeIntervalSinceReferenceDate:0]];
NSDate *date1 = [NSDate date];
NSLog(#"%#", date1);
NSLog(#"%.0f", [date1 timeIntervalSinceNow]);
// _replacement_Method is not called!
NSDate *date2 = [[NSDate alloc] init];
NSLog(#"%#", date2);
NSLog(#"%.0f", [date2 timeIntervalSinceNow]);
// _replacement_Method is called
NSObject *object = [[NSObject alloc] init];
NSLog(#"%#", object);
// A class with empty implementation to test inherited init from NSObject
// _replacement_Method is called by -[MyObject init]
MyObject *myobject = [[MyObject alloc] init];
NSLog(#"%#", myobject);
The output is
2001-01-01 00:00:00 +0000
-0
2014-11-26 14:43:26 +0000
438705806
<NSObject: 0x7fbc50e19d90>
<MyObject: 0x7fbc50e4ad30>
NSDate+Mock.m
#import "NSDate+Mock.h"
#import <mach/clock.h>
#import <mach/mach.h>
#import <objc/runtime.h>
static NSTimeInterval sTimeOffset;
static IMP __original_Method_Imp;
id _replacement_Method(id self, SEL _cmd)
{
return ((id(*)(id,SEL))__original_Method_Imp)(self, _cmd);
}
#implementation NSDate (Mock)
+ (NSObject *)lock
{
static dispatch_once_t onceToken;
static NSObject *lock;
dispatch_once(&onceToken, ^{
lock = [[NSObject alloc] init];
});
return lock;
}
+ (void)setMockDate:(NSDate *)date
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method m1 = class_getInstanceMethod([NSDate class], #selector(init));
Method m2 = class_getInstanceMethod([NSDate class], #selector(initMock));
// method_exchangeImplementations(m1, m2);
// class_addMethod([NSDate class], #selector(init), (IMP)_replacement_Method, "##:");
__original_Method_Imp = method_setImplementation(m1, (IMP)_replacement_Method);
});
#synchronized([self lock]) {
sTimeOffset = [date timeIntervalSinceReferenceDate] - [self trueTimeIntervalSinceReferenceDate];
}
}
+ (NSTimeInterval)mockTimeOffset
{
#synchronized([self lock]) {
return sTimeOffset;
}
}
+ (NSTimeInterval)trueTimeIntervalSinceReferenceDate
{
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
NSTimeInterval now = mts.tv_sec + mts.tv_nsec * 1e-9 - NSTimeIntervalSince1970;
return now;
}
+ (NSTimeInterval)timeIntervalSinceReferenceDate
{
return [self trueTimeIntervalSinceReferenceDate] + [self mockTimeOffset];
}
+ (instancetype)date
{
return [[NSDate alloc] initWithTimeIntervalSinceNow:0];
}
+ (instancetype)dateWithTimeIntervalSinceNow:(NSTimeInterval)secs
{
return [[NSDate alloc] initWithTimeIntervalSinceNow:secs];
}
//- (instancetype)init
//{
// self = [super init];
// return self;
//}
//- (instancetype)initMock
//{
// self = nil;
// NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow:0];
// return date;
//}
- (instancetype)initWithTimeIntervalSinceNow:(NSTimeInterval)secs
{
return [self initWithTimeIntervalSinceReferenceDate:[NSDate timeIntervalSinceReferenceDate] + secs];
}
- (NSTimeInterval)timeIntervalSinceNow
{
NSTimeInterval t = [self timeIntervalSinceReferenceDate];
return t - [NSDate timeIntervalSinceReferenceDate];
}
#end

NSDate has to important characteristics:
It is a class cluster
It is immutable
In such a case, +alloc returns only a placeholder and you send -init… to that placeholder (of class __NSPlaceholderDate). Replacing -init (NSDate) has no effect, if -init (__NSPlaceholderDate or NSWhatever is implemented.)
This is, because +alloc cannot decide which (private) subclass to choose, because it has no parameters. (They are passed at the -init….)
You can
simply replace -init of __NSPlaceholderDate
replace -init of what +alloc returns.
replace +alloc to return your private placeholder and overwrite -init in it.

If you need mock dates e.g. in your tests, consider instantiating the NSDate objects with the Factory pattern and replacing the factory for production or tests. This way only your own classes end up with the mock dates and you don't have to worry about accidentally replacing methods that may be used by Apple's frameworks.

Related

Why do I get Use of undeclared identifier 'downloadDataFromURL' when method is defined in same class?

I have written a method that I want to reuse from another method in the same class but I am getting this error message and I don't understand why, how can it be undeclared when it is declared in the same class?
the h file looks like this
#import <Foundation/Foundation.h>
#import "AppDelegate.h"
#import "NWTillHelper.h"
#interface JoshuaWebServices : NSObject
+ (void)downloadDataFromURL:(NSURL *)url withCompletionHandler:(void (^)(NSData *))completionHandler;
- (void)downloadCollections;
#end
And the m file as follows
#import "JoshuaWebServices.h"
#implementation JoshuaWebServices
#synthesize xmlParser;
+ (void)downloadDataFromURL:(NSURL *)url withCompletionHandler:(void (^)(NSData *))completionHandler {
if([NWTillHelper isDebug] == 1) {
NSLog(#"%s entered", __PRETTY_FUNCTION__);
}
// Lots of irrelevant code here
}
- (void)downloadCollections {
// Prepare the URL that we'll get the neighbour countries from.
NSString *URLString = [NSString stringWithFormat:#"https://url.is.not.here"];
NSURL *url = [NSURL URLWithString:URLString];
// Download the data.
[downloadDataFromURL:url withCompletionHandler:^(NSData *data) {
// Make sure that there is data.
if (data != nil) {
self.xmlParser = [[NSXMLParser alloc] initWithData:data];
self.xmlParser.delegate = self;
// Initialize the mutable string that we'll use during parsing.
self.foundValue = [[NSMutableString alloc] init];
// Start parsing.
[self.xmlParser parse];
}
}];
}
Why can I not use the method declared in same class?
Your method needs a receiver. Unlike functions that can just be called on there own. Methods must be called by something, either the class, or an instance of a class. In your case you should use a class because it's a class method.
Change
[downloadDataFromURL:url withCompletionHandler:^(NSData *data) {
// Make sure that there is data.
if (data != nil) {
self.xmlParser = [[NSXMLParser alloc] initWithData:data];
self.xmlParser.delegate = self;
// Initialize the mutable string that we'll use during parsing.
self.foundValue = [[NSMutableString alloc] init];
// Start parsing.
[self.xmlParser parse];
}
}];
to be
[JoshuaWebServices downloadDataFromURL:url withCompletionHandler:^(NSData *data) {
// Make sure that there is data.
if (data != nil) {
self.xmlParser = [[NSXMLParser alloc] initWithData:data];
self.xmlParser.delegate = self;
// Initialize the mutable string that we'll use during parsing.
self.foundValue = [[NSMutableString alloc] init];
// Start parsing.
[self.xmlParser parse];
}
}];

Class Factory Method with Multiple Parameters

I am practicing my Objective C skills and have come across a small issue, although I can't seem to find a straight answer to this issue anywhere I look. In the Apple developer guides I am reading, there is nothing in there telling me how to use a class factory method with multiple parameters (say 3 parameters) and return the initialized object via the overridden init method.
Here I have a simple class called XYZPerson.
#implementation XYZPerson
// Class Factory Method
+ (id)person:(NSString *)firstName with:(NSString *)lastName andWith:(NSDate *)dateOfBirth {
// need to return [ [self alloc] init with the 3 paramaters]; here
// Or some other way to do so..
}
// Overridden init method
- (id)init:(NSString *)firstName with:(NSString *)lastName andWIth:(NSDate *)dateOfBirth {
self = [super init];
if (self) {
_firstName = firstName;
_lastName = lastName;
_dateOfBirth = dateOfBirth;
}
return self;
}
// Use the initialized instance variables in a greeting
- (void)sayHello {
NSLog(#"Hello %# %#", self.firstName, self.lastName);
}
And then in my main I am instantiating an XYZPerson object
XYZPerson *person = [XYZPerson person:#"John" with:#"Doe" andWith:[NSDate date]];
[person sayHello];
Can anybody give me a small pointer on how to do this correctly?
If I understand your question, you want the following:
+ (id)person:(NSString *)firstName with:(NSString *)lastName andWith:(NSDate *)dateOfBirth {
XYZPerson *result = [[self alloc] init:firstName with:lastName andWith:dateOfBirth];
return result;
}
If you aren't using ARC, add an autorelease to the return.
BTW - change the return type of the init method to instancetype instead of id.
#implementation XYZPerson
// Class Factory Method
+ (instanceType ) person:(NSString *)firstName with:(NSString *)lastName andWith:(NSDate *)dateOfBirth {
return [[[self class] alloc]init: firstName with: lastName andWith:dateOfBirth];
}
- (instanceType ) init:(NSString *)firstName with:(NSString *)lastName andWIth:(NSDate *)dateOfBirth {
self = [super init];
if (self) {
_firstName = [firstName copy];
_lastName = [lastName copy];
_dateOfBirth = [dateOfBirth copy];
//nb added copy to each of these, we do not own these objects, they could be lost to us..
/// or you could do this instead..
//assuming you synthesised getters/setters for (strong) properties..
[self setFirstName:firstName];
[self setLastName:lastName];
[self setDateOfBirth: dateOfBirth];
}
return self;
}

Initialize an array of another class type in Objective C

I have a variable usuario of type TuplaUsuario. When I insert its data, I want to initialize NSMutableArray cups to an Array of type TuplaCups. How can I do it?
Here is my code up to date:
TuplaUsuario:
//TuplaUsuario.h:
#interface TuplaUsuario : NSObject
{
NSMutableString* mensaje;
NSString* usuario;
NSString* password;
NSMutableArray* cups;
//More variables
}
//Property for each variable
- (id)initWithString:(NSString *)identifier;
//TuplaUsuario.m:
//Synthesize for each variable
- (id)initWithString:(NSString *)identifier {
if ( self = [super init] ) {
mensaje = [[NSMutableString alloc] initWithString:identifier];
usuario = [[NSString alloc] initWithString:identifier];
password = [[NSString alloc] initWithString:identifier];
cups = [[NSMutableArray alloc] init];
}
return self;
}
TuplaCups:
//TuplaCups.h:
#interface TuplaCups : NSObject {
NSString* cups;
NSString* tarifa;
NSString* direccion;
NSString* nombreUsuario;
NSMutableArray* facturas;
}
//Property for each variable
- (id)initWithString:(NSString *)identifier;
//TuplaCups.m:
//Synthesize for each variable
- (id)initWithString:(NSString *)identifier {
if ( self = [super init] ) {
cups = [[NSMutableString alloc] initWithString:identifier];
tarifa = [[NSString alloc] initWithString:identifier];
direccion = [[NSString alloc] initWithString:identifier];
nombreUsuario = [[NSString alloc] initWithString:identifier];
facturas = [[NSMutableArray alloc] init];
}
return self;
}
Arrays (as in NSArray and NSMutableArray) in Objective-C are not typed, they can hold any object type and you can't easily restrict what's going in. So you are responsible for ensuring that only objects of the type you want go in. Usually, this isn't a problem unless you expose the mutable array so that other objects might put stuff into your array. In this case, it's better to provide accessors:
- (void)addFoo:(Foo *)foo {
[myMutableArray addObject:foo];
}
- (void)removeFoo:(Foo *)foo {
[myMutableArray removeObject:foo];
}
// Return an array of Foo instances.
- (NSArray *)foos {
return [[myMutableArray copy] autorelease];
}
Objective-C doesn't have a concept of Generics like C# or C++ do. There are no templates after all. You just have to know what type of objects you've put into an array. If you are going to intermix them, you can use isKindOfClass: to test for the class.

How to load an Objective C Singleton using NSCoding?

I am writing a basic game, I am using a GameStateManager which is a singleton and handles all state management, such as saves, loads, delete methods.
I have a separate singelton which handles all the Game stuff. The game object is inside the game state manager so I can save it out using NSCoding and Archiving.
When I save the state there appears to be no issues and the Game object (singleton) is saved properly.
However, when I try to load the state (by relaunching the app), the game object is always null.
Strangley, if I remove the singleton properties and make it a standard class, this issue goes away and I can load the class and all its properties without any major issues.
In summary, I do this:
GameStateManager = Singleton, handles all game state management (load, save) and has a game object (game)
Game = Singleton which handles things within the game and has NSCoding protocol employed.
Saving the game state with the game object is fine, the object is clearly there.
Loading the game state seems to make the game object null. It should be there, but for some reason it never loads it.
If I remove all the properties that make the game class a singelton and make it a normal class, the issue seems to go away.
I think it has something to do with the fact that Game is never init'd, but this does not make sense because I can get Game to load when it has no singleton properties.
Code now follows.
// GameStateManager.m
-(void)saveGameState
{
CCLOG(#"METHOD: saveGameState()");
self.lastSaveDate = [NSDate date];
NSMutableData *data;
NSString *archivePath = [NSTemporaryDirectory() stringByAppendingPathComponent:kGameSaveFile];
NSKeyedArchiver *archiver;
BOOL result;
data = [NSMutableData data];
archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:self.lastSaveDate forKey:#"lastSaveDate"];
[archiver encodeObject:self.game forKey:#"game"];
[archiver finishEncoding];
result = [data writeToFile:archivePath atomically:YES];
[archiver release];
}
-(void)loadGameState
{
CCLOG(#"METHOD: loadGameState()");
NSData *data;
NSKeyedUnarchiver *unarchiver;
NSString *archivePath = [NSTemporaryDirectory() stringByAppendingPathComponent:kGameSaveFile];
data = [NSData dataWithContentsOfFile:archivePath];
unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
// Customize unarchiver here
self.game = [unarchiver decodeObjectForKey:#"game"];
self.lastSaveDate = [unarchiver decodeObjectForKey:#"lastSaveDate"];
[unarchiver finishDecoding];
[unarchiver release];
CCLOG(#"Last Save Date = %#", self.lastSaveDate);
NSLog(#"game = %#", self.game);
}
// END OF GAMESTATEMANAGER
// -------------------------
// Game.h
#interface Game : NSObject
<NSCoding>
{
NSMutableArray *listOfCities;
NSMutableArray *listOfColors;
NSMutableArray *listOfPlayers;
}
#property (nonatomic, retain) NSMutableArray *listOfCities;
#property (nonatomic, retain) NSMutableArray *listOfColors;
#property (nonatomic, retain) NSMutableArray *listOfPlayers;
+(Game *) sharedGame;
//
// Game.m
// This is a cut-down version of the game object
// Game is a singelton
// The listOfCities, etc are arrays
//
#implementation Game
SYNTHESIZE_SINGLETON_FOR_CLASS(Game)
#synthesize listOfCities, listOfPlayers;
#pragma mark - NSCoding
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self)
{
self.listOfCities = [aDecoder decodeObjectForKey:#"listOfCities"];
self.listOfPlayers = [aDecoder decodeObjectForKey:#"listOfPlayers"];
NSLog(#"Cities = %d", [self.listOfCities count]);
NSLog(#"Players = %d", [self.listOfPlayers count]);
}
return self;
}
-(void)encodeWithCoder:(NSCoder *)aCoder
{
// Archive objects
NSLog(#"Cities = %d", [self.listOfCities count]);
NSLog(#"Players = %d", [self.listOfPlayers count]);
[aCoder encodeObject:self.listOfCities forKey:#"listOfCities"];
[aCoder encodeObject:self.listOfPlayers forKey:#"listOfPlayers"];
}
#end
My question is, how do I successfully save and load an Objective C singelton using NSCoding, and the Archiver?
Edit;
I have tried:
// In the GameStateManager
#pragma mark - NSCoding
-(id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
self.lastSaveDate = [[decoder decodeObjectForKey:#"lastSaveDate"] retain];
self.game = [[decoder decodeObjectForKey:#"game"] retain];
}
return self;
}
-(void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.lastSaveDate forKey:#"lastSaveDate"];
[encoder encodeObject:self.game forKey:#"game"];
}
and
// In Game.m
self.listOfCities = [[aDecoder decodeObjectForKey:#"listOfCities"] retain];
self.listOfPlayers = [[aDecoder decodeObjectForKey:#"listOfPlayers"] retain];
NSLog(#"Cities = %d", [self.listOfCities count]);
NSLog(#"Players = %d", [self.listOfPlayers count]);
Since you're dealing with a singleton, you only ever want a single instance of the class to exist at any time. So you will want to archive and unarchive that single instance only.
Consider this code (assumes ARC is enabled):
#interface Singleton : NSObject <NSCoding>
+ (id)sharedInstance;
#property (strong, nonatomic) NSString *someString;
#end
#implementation Singleton
+ (id)sharedInstance {
static Singleton instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[Singleton alloc] init];
});
return instance;
}
#pragma mark - NSCoding implementation for singleton
- (id)initWithCoder:(NSCoder *)aDecoder {
// Unarchive the singleton instance.
Singleton *instance = [Singleton sharedInstance];
[instance setSomeString:[aDecoder decodeObjectForKey:#"someStringKey"]];
return instance;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
// Archive the singleton instance.
Singleton *instance = [Singleton sharedInstance];
[aCoder encodeObject:[instance someString] forKey:#"someStringKey"]];
}
#end
Using this template, you can archive/unarchive the singleton, like this:
// Archiving calls encodeWithCoder: on the singleton instance.
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:[Singleton sharedInstance]] forKey:#"key"];
// Unarchiving calls initWithCoder: on the singleton instance.
[[NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:#"key"]];
try this in your initWithCoder method :
self.listOfCities = [[aDecoder decodeObjectForKey:#"listOfCities"] retain];
self.listOfPlayers = [[aDecoder decodeObjectForKey:#"listOfPlayers"] retain];
Easiest way:
1. Load singleton:
+ (AppState *)sharedInstance
{
static AppState *state = nil;
if ( !state )
{
// load NSData representation of your singleton here.
NSData *data =[[NSUserDefaults standardUserDefaults] objectForKey:#"appStateData"];
if ( data )
{
state = [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
else
{
state = [[AppState alloc] init];
}
}
return state;
}
2. Save singleton on a disk
- (BOOL)save
{
NSData *appStateData = [NSKeyedArchiver archivedDataWithRootObject:self];
// now save NSData to disc or NSUserDefaults.
[[NSUserDefaults standardUserDefaults] setObject:appStateData forKey:#"appStateData"];
}
3. Implement NSCoding methonds:
- (id)initWithCoder:(NSCoder *)coder;
- (void)encodeWithCoder:(NSCoder *)coder;
Done!
I'm using a hybrid approach encompassing answers by Eric Baker and skywinder.
+ (GameStateManager *)sharedInstance {
static GameStateManager *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:#"key"];
if (data) {
instance = [NSKeyedUnarchiver unarchiveObjectWithData:data];
} else {
instance = [[GameStateManager alloc] init];
}
});
return instance;
}

Objective-c : initialize instance var

I am new to objective-c. My object reportingTime is nil in init method. What I am doing wrong ? How can I initialize reportingTime and set firstweekday ?
I have the following interface and implementation
My interface file Calculation.h
#interface Calculate : NSObject {
NSCalendar *reportingTime;
....
....
}
#property NSCalendar *reportingTime;
#end
And my implementation file Calculate.m
#import "Calculate.h"
#implementation Calculate
#synthesize reportingTime;
- (id)init
{
if(self = [super init]) {
reportingTime = [[NSCalendar alloc] init];
[reportingTime setFirstWeekday:1];
// reportingTime is nil ??????
}
return self;
}
Use
- (id)initWithCalendarIdentifier:(NSString *)string
instead of standart init method.
See more: https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSCalendar_Class/Reference/NSCalendar.html
Instead of
reportingTime = [[NSCalendar alloc] init];
use:
reportingTime = [[NSCalendar currentCalendar] retain];
The retain because this is a direct assignment to the #property ivar and [NSCalendar currentCalendar] is a convenience method that returns an autoreleased var. If you are using ARC the retain is not necessary.