Sync your app's time with server's UTC time

Overview

ServerSync for iOS Apps

Sometimes your app can't rely on the device's date and time settings being correct. This library will help you calibrate your app's (NB: Not the device) time to the correct UTC time using either your own server or Google's. This allows you to coordinate events between your server and your app.

You can use websockets, but that tends to be overkill and adds extra complexity client-and-server-side. Ideally, the app will periodically calibrate with the server. This library uses an exponential moving average to increase accuracy and reduce the effects of variations in latency.

Other solutions are Kronos and TrueTime.swift. They both work the same way but they miss the main point. They don't synchronize to your server. They synchronize to the true global UTC time, which means you have to also synchronize your server. They don't provide a complementary solution for your server. This is an extra hassle which is unnecessary (and probably overkill) for 99% of all apps.

The library is written in Swift 2. It will be converted to Swift 3 when my project makes the transition in a few months. If you want, you can change it and do a pull-request. It will be a trivial change.

Installation

CocoaPods (iOS 8+)

Not set up yet

Manual (iOS 7+)

Copy ServerSync.swift into your project.

(Optional) If you want to calibrate using Google's server, then Copy GoogleCalibrate.swift into your project.

Usage

"Client" will refer to the iPhone in the below contexts.

General Usage

In order to calibrate NSDate, you must first calibrate with a server. You can use Google or your own server (see below). If you do not calibrate, then this library will return the client's time unchanged - no detriment to you.

Ideally, you should run the calibration periodically or upon observing UIApplicationWillEnterForegroundNotification because you can't rely on one HTTP request having low latency. High latency will reduce the accuracy of the calibration. This library will utilize an exponential moving average to get as accurate as possible.

It is also wise to recalibrate after observing these notifications: UIApplicationSignificantTimeChangeNotification, NSSystemClockDidChangeNotification and NSSystemTimeZoneDidChangeNotification.

After calibrating, the functions of note are:

func toClientTime() -> NSDate - If you have a time that is calibrated to the server's time (i.e. server's UTC time) and you want to translate it to the client's time:

//Server states that something in your app should happen at this time: "2016-11-18 01:18:00" (UTC)
dateFormat = NSDateFormatter()
dateFormat.timeZone = NSTimeZone(abbreviation: "GMT")
dateFormat.dateFormat = "yyyy-MM-dd HH:mm:ss"

let serverDate: NSDate = dateFormat.dateFromString("2016-11-18 01:18:00")
let clientDate: NSDate = serverDate.toClientTime()

//Now use `clientDate` in your app. It has been calibrated.

static func reset() - Uncalibrates NSDate so it is no longer synchronized to the server.

NSDate.reset()

If you have your own server

Concepts

Let's assume that from the moment the client sends it's request, the total response time is made up of 3 components:

  • Lreq - The transmission time before your server can start processing the request.
  • Operation Duration - How long the server took to process the request
  • Lres - The transmission time afterwards

Your server must respond with it's UTC time in UNIX format (nanoseconds) - ideally generated as late as possible. It can be embedded in the JSON response for example. If not possible, you can use the Date header field which provides second-level accuracy. Whether the Date header field is generated as late as possible is dependent on the server.

Ideally, the server will also return how long it took to perform the operation (i.e. process the request). The units must be in nanoseconds. If this is not possible, you can approximate it as 0 nanoseconds, at the cost of some accuracy.

static func updateOffsetRaw(clientRequestUTCUnixNano: Int64, serverOperationDurationNano: Int64, serverUTCUnixNano: Int64) -> Int64

Parameters:

clientRequestUTCUnixNano - Before the client sends the request, you record the client-side UTC Unix time in nanoseconds

serverOperationDurationNano - The server should respond with how long it took to process the response from start of receiving request to start of sending out response. If you don't have access to the server, you can approximate this value to 0

serverUTCUnixNano - The server should respond with it's internal UTC time in UTC Unix time in nanoseconds - preferably as late as possible before sending response.

Server setup

Sample Go Code:

import (
	"time"
	"net/http"
)

type Response struct {
	SyncDuration    int64            `json:"SyncDuration"` //How long the request took to process in nanoseconds
	SyncUTC         int64            `json:"SyncUTC"`      //UTC time at end of response in UNIX time (nanoseconds)
}


func RequestHandler (w http.ResponseWriter, r *http.Request) {
	operationStartTime := time.Now().UTC()
	

	//Process stuff. Do what ever. Compile Response etc

	response := Response{}

	//As late as possible: For UTC server-client synchronization
	operationEndTime := time.Now().UTC()
	response.SyncDuration = operationEndTime.Sub(operationStartTime).Nanoseconds()
	response.SyncUTC = operationEndTime.UnixNano()
	ReturnSuccess(w, response) //Return response in JSON format
}

Client setup

The client must record the exact moment (in Unix nanosecond format) it sent the http request.

Sample Swift Code (Using AFNetworking):

let sm: AFHTTPSessionManager = AFHTTPSessionManager(baseURL: apiBaseUrl)

let clientRequestTime: Int64 = NSDate.UTCToUnixNano() //Store time of sending request in Unix nanosecond format
sm.GET(path, parameters: nil, progress: nil, success: {(task: NSURLSessionDataTask, responseObject: AnyObject?) -> Void in
	
	//response will have 2 json fields
	// `syncDuration` - See Go code above
	// `syncUTC` - See Go code above
	let response = f(responseObject)

	//UTC server-client synchronization
    NSDate.updateOffsetRaw(clientRequestTime, serverOperationDurationNano: response.syncDuration, serverUTCUnixNano: response.syncUTC)
})

Using Google's server

You can asynchronously calibrate using Google's servers. The library will attempt to use the lowest-latency server available.

NSDate.calibrate()

Other Useful Packages

Check out "github.com/pjebs/Obfuscator-iOS" library. Secure your app by obfuscating all the hard-coded security-sensitive strings embedded in the binary.

Check out "github.com/pjebs/GAE-Toolkit-Go" package. Escape CloudSQL and save money by using an external MYSQL database with Google App Engine - Go.

Final Notes

If you found this package useful, please Star it on github. Feel free to fork and/or provide pull requests. Any bug reports will be warmly received.

SkyLovely Pty Ltd

You might also like...
The demo project to show how to organize code to make SwiftUI apps easy to be test.
The demo project to show how to organize code to make SwiftUI apps easy to be test.

TestableApp I combined the idea to use functional programming instead of an loader instance in ModelView(I prefer to think of it as a service) and Res

CS193p---Assignments - Assignment Solutions for Stanford CS193p - Developing Apps for iOS

Assignment Solutions for Stanford CS193p - Developing Apps for iOS Note: This is ongoing work Task done Programming Assignment 1 x Programming Assignm

Adventures-with-Swift - Building Native iOS Apps with UIKit and SiwftUI  
Adventures-with-Swift - Building Native iOS Apps with UIKit and SiwftUI 

Adventures with Swift, UIKit, & SwiftUI As I have experience working with React Native and have dabbled a bit with Flutter, I've decided to dive in th

App07 - This is part of the 31 days of small Xcode apps for January 2022
App07 - This is part of the 31 days of small Xcode apps for January 2022

App07 This is part of the 31 days of small Xcode apps for January 2022 Multiplic

WeatherApps - Simple Weather Apps Using Core Location
WeatherApps - Simple Weather Apps Using Core Location

Simple WeatherApps Using Core Location To Get The Actual Position Using API from

Super basic iOS app to browse open-source-ios-apps
Super basic iOS app to browse open-source-ios-apps

Super basic iOS app to browse open-source-ios-apps

A simple confetti view for apps using SwiftUI.
A simple confetti view for apps using SwiftUI.

ConfettiView Create fun animated confetti views with ease! Installation Use Swift Package Manager to install this package: https://github.com/benlmyer

This framework contains SBB (Swiss Federal Railways) UI elements for iOS SwiftUI Apps
This framework contains SBB (Swiss Federal Railways) UI elements for iOS SwiftUI Apps

Framework: Design System Mobile for iOS & SwiftUI This framework contains SBB (Swiss Federal Railways) UI elements for iOS SwiftUI Apps. It allows an

Lockdown is an open source firewall that blocks trackers, ads, and badware in all apps

Lockdown Privacy (iOS) Lockdown is an open source firewall that blocks trackers, ads, and badware in all apps. Product details at lockdownprivacy.com.

Owner
null
Stopwatch is a Swift App that measures amount of time elapsed from a particular time.

Stopwatch Stopwatch is a Swift App that measures amount of time elapsed from a particular time. It highly mocks the stopwatch of Apple's offical App c

Kushal Shingote 3 Feb 20, 2022
A platform to showcase your side projects/apps

A platform to discuss/showcase your side projects What is this? Inspired by this twitter thread Indie Apps Showcases is a platform for indie app devel

An Tran 25 Dec 27, 2022
Create a beautiful Onabording for your iOS/iPadOS apps in just a few minutes.

Create a beautiful Onabording for your iOS/iPadOS apps in just a few minutes.

Jem Alvarez 6 Sep 9, 2022
Visualize your dividend growth. DivRise tracks dividend prices of your stocks, gives you in-depth information about dividend paying stocks like the next dividend date and allows you to log your monthly dividend income.

DivRise DivRise is an iOS app written in Pure SwiftUI that tracks dividend prices of your stocks, gives you in-depth information about dividend paying

Kevin Li 78 Oct 17, 2022
An iPhone Simulator "Wrapper" for SwiftUI Apps on macOS

SwiftUIPhone Run a SwiftUI app (or any SwiftUI view) in an iPhone Simulator "wrapper", directly on macOS! To be clear, this is not an iPhone Simulator

Justin Kaufman 7 May 20, 2022
iOS framework for making Turbo native apps

Turbo Native for iOS Note: The Hotwire frameworks are presented in beta form. We're using them all in production with HEY, but expect that significant

Hotwire 493 Jan 1, 2023
Angela Yu iOS tutorials TODO apps practice

Todoey ✓ Our Goal The objective of this tutorial is to understand how to save data in iOS. We'll look at various choices and learn to use UserDefaults

Ferdous 0 Jan 1, 2022
Client library for making in-app purchases on iOS and macOS Automattic apps

MobilePayKit Client library for making in-app purchases on iOS and macOS Automattic apps Introduction MobilePayKit is a client library for making in-a

Automattic 8 Oct 20, 2022
This repository contains code for building Universal Apps with SwiftUI.

MindLikeWater This Repo This repository contains code for building Universal Apps with SwiftUI. The same codebase can be compiled to produce binaries

Jorge D. Ortiz Fuentes 1 Nov 23, 2021
A curated list of Open Source example iOS apps developed in Swift

Example iOS Apps A curated list of Open Source example iOS apps developed in Swift. How to Use Example-iOS-Apps is an amazing list for people who are

null 1 Dec 15, 2021