You may be a top app development company or a successful developer, your server working 24*7, but it is crucial to have a robust offline app development framework for your app that tackles your app's offline capabilities in the best way and provides an end to end solution to its users.

Network issues, battery constraints, global users operating from various regions and time zones, and many such factors make it extremely important for your web & mobile apps to have offline capabilities. In this blog, we help you learn the complete development cycle for offline mobile app development, crucial for ios app development companies, and android app development.

Table of content

  1. Categories of offline app.
  2. Importance of offline capabilities, offline mode.
  3. Offline app architecture and implementation.

We can define best offline apps into three broad categories:

  1.  Offline data storage – In such cases of data being stored offline with no editing functionality like in case of GPS data being stored offline temporarily in an Uber type app.
  2. Offline editing of data by users and sync it online - Like in case of a note-taking app, editing a note-taking app as Google keeps offline on your phone. When your phone gets network, it updates the info online, and when you open keep.google.com, you see all the edits being done.
  3.  Edit others data - Users can edit other user's data (or data shared with other users) offline, like in a baby tracker app. It's nice to have offline capabilities in the baby tracker newborn log so that users can log the baby activity even when an internet connection is not available. Else users might forget to record the activity or may not use the app altogether.

Why is offline mode important? 

Nowadays, users want their apps to work even if offline; else, it may lead to poor user experience. People want apps to be agile, responsive, and snappy. An app is not helping them in case of a weak network zone, connectivity issue, the immense loading time will let the users switch to another app.

 The main solutions available to make the app work offline are: 

 Development Of Offline App Architecture


Let's start with the implementation.

1. Offline Data Storage and Access capabilities

While making your app work offline, you will often need to store data directly on the client's device, allowing your application to work effectively even when there is no connection. There are various methods or levels of offline data storage that make an app run offline. 

 The most common database solutions are Core Data and Realm, though many people prefer to use SQLite directly. You define models, load data from the network, and insert it into your database. Then, your view controllers listen to queries on the database and update whenever the data changes. 

 There's a fascinating tool for iOS Developers. That is the Realm. 

The Realm is a mobile database that aims at replacing CoreData and SQLite. The most impressive part is that Realm is not only very well built but also very fast. Surprisingly way faster than CoreData and SQLite! 

  1. Install Realm Pod. - Add pod 'RealmSwift' in your Pod File. 
  2. Store And Retrieve Data From Realm.
// Get the default Realm
let realm = try! Realm()
// Persist your data easily
try! realm.write {
   realm.add(Your_Class_Array/Object)
}
// Query Realm to retrive data
let myClassArray = realm.objects(Class_Name.self)


2. Data synchronization

If your app is online or offline, you need to store data in a database such as a Realm and show that data from the Realm will always help show data every time online and offline.

If you are performing local changes such as edit post or delete a post in offline mode you need to store it in the database; if your app is offline then you need to store the parameters such as post ID or post URL in the local storage using the shared preference when you are online then you need to send that pending parameter to server, and after successful send, you need to remove the parameters from the shared preference.

To know you are online or offline i.e. you have active internet connection or not, use network reachability. This will help us to notify when the network is connected or disconnected; if notify reachable, then you need to send the pending request. This will help us to sync with the server.

We need to store our URL and its associated parameters if server communication fails due to internet connection. There are many ways to check the reasons for server connection failure. We are going to use the "Alamofire" library as it is easy to use.

func sendAction(param:[String:AnyObject]) {
   let url = "http://httpbin.org/post"
   AF.request(url, method: .post, parameters: param, encoding: JSONEncoding.default, headers: nil).responseJSON {
   	response in
   	   switch response.result {
      	     case .success:
      	        print("handle your response data")
	        break
      	     case .failure(let error):
      	        print(“failed due to Internet connection”)  
      	        EVSendLater.sharedManager.saveForLater(url: url, params: param)
      	        EVSendLater.sharedManager.synchronizeSaves()                         
   	  }
   }
}

Function sendAction() is used to send post parameters to the server; when result status is failed, then We need to store URL and Parameters to the local database. For that, we are using a helper class named as EVSendLater. 

EVSendLater allows our app to silently handle failed POST requests due to internet connectivity issues and stop bugging our users about sending them again. When a POST request fails to send due to internet issues or other errors, you may wish to handle, save its parameters using EVSendLater.sharedManager.save for later(URL, params: params)

This will save the parameters to an NSDictionary.

 EVSendLater.sharedManager.synchronizeSaves()
 // (similar to NSUserDefaults)

 Now we need to send requests which are pending when an internet connection is available. For this, we need to use the Reachability class to notify when a connection is available.

//declare this property where it won't go out of scope relative to your listener
 let reachability = try! Reachability()
//declare this inside of viewWillAppear
 NotificationCenter.default.addObserver(self, selector: #selector
 (reachabilityChanged(note:)), name: .reachabilityChanged, object: reachability)
  do {
    try reachability.startNotifier()
  } catch {
    print("could not start reachability notifier")
  }
      	
@objc func reachabilityChanged(note: Notification) {
  let reachability = note.object as! Reachability
      switch reachability.connection {
         case .wifi:
            print("Reachable via WiFi")
         case .cellular:
            print("Reachable via Cellular")
         case .unavailable:
            print("Network not reachable")
       }
}

This function helps us to notice when the connection is reachable. 

No need to send all pending requests that are stored. In order to retrieve this to reattempt sending, use You can also go through all URLs to send everything using

EVSendLater.sharedManager.getAllSaves(true)
EVSendLater.sharedManager.removeFromSaves(url: urlString, params: param)

The delete parameter will assume that the POSTS will go through.  Simply handle their errors again using 

EVSendLater.sharedManager.saveForLater(url, params: params)

Example

class="language-plaintext">  @objc func syncAction() {
    for (url, parameters) in EVSendLater.sharedManager.getAllSaves(){
        if let urlString = url as? String, let URLType = URL(string: urlString) {
          if let params = parameters as? [[String: AnyObject]]{
            for param in params{
               AF.request(URLType, method: .post, parameters: param,encoding: JSONEncoding.default, headers: nil).responseJSON {
               response in
                 switch response.result {
                    case .success:
                      print(response)
                      EVSendLater.sharedManager.remove
                      FromSaves(url: urlString, params: param)
                      EVSendLater.sharedManager.synchronizeSaves()
                      break
                    case .failure( _): break
                 }
            }
          }
        }
    }
}

After successfully sending the request, we need to delete the request from the directory.

 The final will look like

@objc func reachabilityChanged(note: Notification) {
	let reachability = note.object as! Reachability
     switch reachability.connection {
     	case .wifi:
           syncAction()
        case .cellular:
           syncAction()
        case .unavailable:
           print("Network not reachable")
     }
}

EVSendLater Class - Let's look at EVSendLater class; you can save it in any swift file inside your project.

import Foundation
public class EVSendLater : NSObject {
   private var saves: NSMutableDictionary!
   private var savesChanged = false
   private var savesCreated = false
   private let path = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as 	         NSString).appendingPathComponent("EVSendLaters")
   public var saveImmediately = false
   public class var sharedManager : EVSendLater {
      struct Static {
         static let instance : EVSendLater = EVSendLater()
      }

      Static.instance.initializeSaves()
      return Static.instance
   }
 
   private func initializeSaves() {
      if !savesCreated {
         let fileManager = FileManager.default
         if !fileManager.fileExists(atPath: path) {
            saves = NSMutableDictionary()
         } else {
            saves = NSMutableDictionary(contentsOfFile: path)
         }
         savesCreated = true
      }
   }

   public func saveForLater(url:String, params:[String:AnyObject]){
      if let list = saves.object(forKey: url) as? NSMutableArray{
         list.add(params)
      } else{
         saves.setObject(NSMutableArray(array: [params]), forKey: url as NSCopying)
      }
      setSavesChanged()
   }

   public func synchronizeSaves(){
      if savesChanged {
         savesChanged = !saves.write(toFile: path, atomically: true)
      }
   }
 
   public func getSavesForUrl(url:String, delete:Bool) -> [[String:AnyObject]]?{
      return (saves.object(forKey: url) as AnyObject).copy() as? [[String:AnyObject]]
   } 

   public func getAllSaves() -> NSDictionary {
      return saves
   }
 
   public func removeFromSaves(url:String, params:[String:AnyObject]){
      if let array = saves.object(forKey: url) as? NSMutableArray{
         array.remove(params)
         setSavesChanged()
      }
   }
 
   func setSavesChanged(){
      savesChanged = true
      if saveImmediately{
         synchronizeSaves()
      }   
    }
}
 

Conclusion

In this way, you can implement offline data synchronization in your iOS application, android app development. Your synchronization logic ensures that data is up to date on the mobile and server sides simultaneously. Offline mode is like an icing on your app's cake. If you think your app needs offline mode, our custom web and mobile app development company, we'll be glad to implement it for you. Read our other blogs for startup company tips.

Do you have any product idea or business need?

How TO MAKE YOUR APP WORK OFFLINE

HOW TO MAKE YOUR APP WORK OFFLINE

Offline mobile app development is critical for users to sync their data properly, when offline. Here, we help you learn the process from implementation to data synchroniz

Omnivore POS integration - Ensuring Agility To Your Restaurant Businesses

Omnivore POS integration - Ensuring Agility To Your Restaurant Businesses

Omnivore software offers a point-of-sales integration API making the restaurant system agile, more customer engaging and adaptive to fast changing business environments.

Unit Testing using Mockk.io

Unit Testing using mockK.io in Kotlin

Learn about unit testing using mockk.io in Kotlin.