17

I need to do some testing that involves moving the "phone" backwards and forwards through several days. I'd like to do this on the simulator. Is there any available hack that allows the date as seen on the simulator to be changed, without having to change the Mac date?

Hot Licks
  • 47,103
  • 17
  • 93
  • 151
  • I found this to be the easiest: http://stackoverflow.com/questions/1699671/how-to-change-time-and-timezone-in-iphone-simulator – Hahnemann Sep 05 '15 at 14:40

4 Answers4

17

Looks like it's pretty tough... How about using xcodebuild to automate building and running the app from a script and using systemsetup -setdate <mm:dd.yy> to change date in between different runs? Then you wouldn't need to change anything in code.

JOM
  • 8,139
  • 6
  • 78
  • 111
5

You can use method swizzling to replace the implementation of [NSDate date] at runtime to return a value of your choosing.

For more info on method swizzling, see http://www.icodeblog.com/2012/08/08/using-method-swizzling-to-help-with-test-driven-development/.

tom
  • 18,953
  • 4
  • 35
  • 35
3

I needed to test my app automatically, which required changing the sys time. I did, what Tom suggested: happy hacky method swizzling.

For demonstrative purposes, I only change [NSDate date] but not [NSDate dateWithTimeIntervalSince1970:].

First your need to create your class method that serves as the new [NSDate date]. I implemented it to simply shift the time by a constant timeDifference.

int timeDifference = 60*60*24; //shift by one day
NSDate* (* original)(Class,SEL) = nil;

+(NSDate*)date{
    NSDate* date = original([NSDate class], @selector(date));
    return [date dateByAddingTimeInterval:timeDifference];
}

So far, pretty easy. Now comes the fun part. We get the methods from both classes and exchange implementation (it worked for me in the AppDelegate, but not in my UITests class). For this you will need to import objc/runtime.h.

Method originalMethod = class_getClassMethod([NSDate class], @selector(date));
Method newMethod = class_getClassMethod([self class], @selector(date));

//save the implementation of NSDate to use it later
original  = (NSDate* (*)(Class,SEL)) [NSDate methodForSelector:@selector(date)];

//happy swapping
method_exchangeImplementations(originalMethod, newMethod);

Obviously, make sure that you use non of this in your deployed application. Lastly, I can only agree with the answers before that it is quite questionable why Apple didn't add native support for this.

Sebastian Hojas
  • 4,158
  • 2
  • 27
  • 38
  • I liked your answer but don't understand how to do this. Can you elaborate this in detail? Where to write this custom +(NSDate*)date class method? In category(extension)? Please mention where to write this method in source code and how use then implementing this solution. This will be quite useful to others too who have same confusion as me. Thanks in advance. – Nitesh Borad Jan 12 '17 at 11:35
  • @NiteshBorad I'd just add everything in one file and then call it, if your application is launched in DEBUG mode (could be done in `main.m` or your AppDelegate). Here is an example implementation: https://gist.github.com/Sebastian-Hojas/01a23ccb5b1a0051997715ee5ec4862f – Sebastian Hojas Jan 14 '17 at 07:41
  • Will this work with swift date ? I am having same issue but with swift Date, not NSDate. https://stackoverflow.com/questions/72901908/how-to-swizzle-date-not-nsdate-in-swift-for-xcuitest – PJR Jul 14 '22 at 11:33
0

If you are creating an app, just change the date in the app for testing and you can remove it when you publish it.

  • 2
    Except if you need to test at 20 different dates you have to recompile 20 different times. (Though I suppose one could put the date into a plist.) And you may have `[NSDate date]` or equivalent several places in your code, or in a module for which you don't control the source. – Hot Licks Dec 17 '12 at 19:57