background
When it comes to system time in iOS development, there’s one thing to consider: timing. Some services are time-free, or can be based on the user’s time, such as animation time, some calendar applications, etc. However, most e-commerce related businesses cannot directly use the time on the device, but need to calibrate the time with the server. For example:
- Interval judgment: For some promotional activities, you need to judge whether the current promotion period is within the app end. If the device time is incorrect, incorrect information will be sent to the user, leading to complaints.
- Countdown: various seconds to kill, limited time promotion, the failure of unpaid orders, etc. If the user device time is not correct, the page will be refreshed after the countdown, and the state will not change. You can test the app of the e-commerce factory, and the countdown is still correct after any dial meter.
- Synchronization: If data synchronization is required, the device time is incorrect. As a result, old data may overwrite new data, resulting in data loss.
- Request timestamp: For paged data, a common solution is to add a timestamp parameter to the request list to prevent the data from being corrupted when the page is turned, and background filtering only shows the data after the timestamp. If the user device table is slow, the latest data will not be displayed, resulting in new posts not appearing in the list.
As you can see, this need for timing is very common. However, it is not difficult to achieve, here to share our experience.
The solution
It’s called a solution because it’s not just a few lines of code on the app side, it’s a combination of the front and back. The general idea is as follows:
- What the back end needs to do: Each network request returns data with the current timestamp of the server
- The network framework on the app side retrieves the timestamp at the common callback of the network request
- Caches the difference between server time and local time locally
- If time needs to be used, use the time difference between the local time and the cache time to calculate the corresponding server time
Network request callback
The server’s timestamp can be added to the Response Body as a public field. In my project, there are a few GET requests, so I put them in the Response header. The code looks like this:
+ (void)handleSuccessResponse:(id)responseObject operation:(AFHTTPRequestOperation *)operation responseType:(Class)responseClass success:(void (^)(id))successBlock failure:(void (^)(NSError *))failureBlock {
long long timestamp = [[operation.response.allHeaderFields objectForKey:@"Response-Timestamp"] longLongValue];
[HAMDateTimeUtils updateServerTime:timestamp];
}Copy the code
Update the cache of the time difference each time a network request succeeds.
One small note is that it is best to always use the long long type for timestamp. Because timestamp is traditionally measured in milliseconds (although NSTimeInteval is measured in seconds on iOS, a weird system), neither long nor NSInteger can be stored on 32-bit systems and will overflow. Of course, 32-bit devices are not common these days.
Time difference cache
When updating the cache, the difference between the server time and the current local time is stored in a singleton.
HAMDateTimeUtils.m
- (void)updateServerTime:(long long)timestamp {
NSTimeInterval timeInteval = timestamp / 1000.0 - [[NSDate date] timeIntervalSince1970];
[self sharedInstance].timeIntevalDifference = timeInteval;
}Copy the code
Provide the calibrated time
If time needs to be used, calculate the time after calibration according to the difference between the current time and the cached time:
HAMDateTimeUtils.m
+ (NSDate*)currentTime {
NSDate* serverDate = [NSDate dateWithTimeIntervalSinceNow:[self sharedInstance].timeIntevalDifference];
return serverDate;
}
// in milliseconds
+ (long long)currentTimeStamp {
NSTimeInterval localTime = [[NSDate date] timeIntervalSince1970];
NSTimeInterval timeDifference = [WNYDateTimeUtils sharedInstance].timeIntevalDifference;
return (long long)((localTimeStamp + timeDifference) * 1000);
}Copy the code
To use it, simply call [HAMDateTimeUtils currentTime] or [HAMDateTimeUtils currentTimeStamp].
discuss
-
Q: Is this accurate? A: There may be some error. The reason is that the timestamp returned by the server is the time when the data was returned from the server, and there is a slight delay until the client receives it. However, for our backend, this delay is generally less than 100 ms, which has no impact on our business. If the accuracy requirements are higher, you can consider using a special timing interface, I wonder if the National Astronomical Observatory has…… In addition, this timing scheme is only used to optimize the display at the UI level, and does not prevent malicious tampering by users. Always remember that client-side timestamps cannot be trusted, and that the back-end business must always use the server’s time.
-
Q: When caching, why only in the singleton, not persistent storage? A: I have also considered this, mainly because I think the time difference may change when I start again, so I don’t think there is much need for persistence. If necessary, you can also save a copy in userDefault and retrieve it at startup.