Building an enterprise mobile app using Realtime Code Hosting - Part II

  • Mar 20, 2015
  • Code Hosting, Cloud Storage

The Application

In this section we’ll guide you through some details of the iOS application (don't forget to check the GitHub repositories for the iOS and Android apps source code), namely how to authenticate a user, use the authenticated token to get the contents from the Cloud Storage backend, data filtering, content presentation and also the process of receiving a push notification.

User authentication

When the app is launched the users will see the login view, where they should enter their valid credentials according the contents of the Users table. You should use the CMS backoffice to manage your app users.

When the user taps the login button a REST POST is sent to the authenticate function stored in the previous steps in the Realtime Code Hosting backend.

Below you’ll find the Swift code to call the authenticate endpoint and retrieve the authenticated token.

class func authenticateStorageTokenForUser(user: String, pass: String) -> NSData?  {
    let url:String = "https://codehosting.realtime.co/\(APP_KEY)/authenticate?"
    
    var urlString:NSMutableString = NSMutableString()
    
    urlString.appendFormat("user=%@&", user)
    urlString.appendFormat("password=%@&", pass)
    urlString.appendFormat("role=%@", "iOSApp")
    
    var request:NSMutableURLRequest = NSMutableURLRequest(URL: NSURL(string: "\(url)\(urlString)")!, cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData, timeoutInterval: 10.0)
    
    var data:NSData = urlString.dataUsingEncoding(NSUTF8StringEncoding) as NSData!
    
    request.HTTPMethod = "POST"
    request.setValue("\(data.length)", forHTTPHeaderField: "Content-Length")
    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Accept")
    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
    
    var resp:NSURLResponse?
    var error:NSError?
    
    return NSURLConnection.sendSynchronousRequest(request, returningResponse: &resp, error: &error)
}

class func reAuth(callback:(result:Bool) -> Void) {
    var storageRef:StorageRef = StorageRef(APP_KEY, privateKey: nil, authenticationToken: token);
    
    storageRef.isAuthenticated(token, success: { (success) -> Void in
        var result:Bool
        if (success == 1)
        {
            result = true
            callback(result: true)
            return
        }
        else
        {
            result = false
        }
        
        var user:String? = NSUserDefaults.standardUserDefaults().objectForKey("user") as? String
        var pass:String? = NSUserDefaults.standardUserDefaults().objectForKey("pass") as? String
        
        if (user == nil || pass == nil)
        {
            Utils.goToLogin()
        }
        
        var rsp:NSData? = Utils.authenticateStorageTokenForUser(user!, pass: pass!) as NSData?
        if rsp == nil
        {
            Utils.goToLogin()
            return
        }
        
         Utils.jsonDictionaryFromString(NSString(data: rsp!, encoding: NSUTF8StringEncoding)!, onCompletion:
        { (jsonDict) -> Void in
            let json:NSDictionary = jsonDict as NSDictionary
            if (json.objectForKey("token") != nil)
            {
                token = json.objectForKey("token") as? String
                NSUserDefaults.standardUserDefaults().setObject(token, forKey: "token")
                NSUserDefaults.standardUserDefaults().synchronize()
                callback(result: false)
            }else
            {
                Utils.goToLogin()
            }
        }, onError: { (error) -> Void in
            Utils.goToLogin()
        })
        
    }) { (error) -> Void in

    }
}
							

Loading contents from Cloud Storage

To show the contents we’ve created a navigation menu divided into three sections:

  • Recent, in this section all contents are presented in creation date descending order;
  • Blog, this section presents the contents belonging to the blog type;
  • o White papers, this sections presents the contents belonging to the white papers type.

The Swift code below shows the data retrieval from Cloud Storage using the tableRef getItems method.

func getData(){

    if self.data == nil
    {
        self.data = NSMutableArray()
        self.dataIndex = NSMutableDictionary()
    }
    
    self.processing = true
    self.lastCount = 1
    NSNotificationCenter.defaultCenter().postNotificationName("startLoad", object: nil)
    
    self.tableRef!.limit(Int32(limit))
    self.tableRef?.desc().getItems({ (itemSnapshot) -> Void in
        isConnected = true
        
        if itemSnapshot != nil
        {
            var val:NSDictionary = itemSnapshot.val() as NSDictionary
            var item:DataObject = DataObject.loadDataObjectFromDictionary(val)
            
            item.isOffline = false
            self.data!.addObject(item)
            NSLog("%d - title: %@ month: %@ timestamp: %@",self.lastCount!, item.title!, item.monthYear!, item.timestamp!)
            if val.objectForKey("IMG") != nil
            {
                let img:String = val.objectForKey("IMG") as String
                ImageLoader(item: item, img: img)
            }
            
            self.setContent(item)
            self.lastCount!++
        }else
        {
            DataObject.resetContentsOnDisk()
            self.processing = false
            self.delegate?.didReciveData!(self.data!)               
            NSNotificationCenter.defaultCenter().postNotificationName("endLoad", object: nil)
        }
        
        }, error: { (error) -> Void in
            self.processing = false
            self.setOffLineContents()
            NSNotificationCenter.defaultCenter().postNotificationName("endLoad", object: nil)
    })
    
    if self.booted! == false
    {
        self.tableRef?.on(StorageEventType.UPDATE, callback: { (itemSnapshot) -> Void in
            var val:NSDictionary = itemSnapshot.val() as NSDictionary
            self.delegate?.didItemData!(DataObject.loadDataObjectFromDictionary(val))
        })
        
        self.tableRef?.on(StorageEventType.DELETE, callback: { (itemSnapshot) -> Void in
            var val:NSDictionary = itemSnapshot.val() as NSDictionary
            self.delegate?.didDeleteItemData!(DataObject.loadDataObjectFromDictionary(val))
        })
        
        self.booted = true
    }
}							

Content filtering

The hamburguer button menu

The options menu allows the user to filter the contents using the pre-defined tags.

Filtering data

The data filtering is performed locally for better performance. When there is a filter active an X will be shown in the top right corner. Tapping it will remove the current filter.

Mobile Push Notifications

When there is a new content available, the Code Hosting script attached to the trigger after put on the Contents table is fired and the mobile app receives a push notification. This happens because the application is subscribing the channel notifications using the Realtime Messaging Push Notifications. When the user taps the notification alert the application is launched and the user is redirected to the new content detail view.

Below is the Swift code for handling the push notification.

override func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
    super.application(application, didReceiveRemoteNotification: userInfo)
    var aps:NSDictionary? = userInfo as NSDictionary
    aps = aps?.objectForKey("aps") as? NSDictionary

    let state = application.applicationState
    
    if aps != nil && application.applicationState != UIApplicationState.Active
    {
        var data:NSMutableDictionary = NSMutableDictionary()
        data.setObject(aps?.objectForKey("Type") as String, forKey: "Type")
        data.setObject(aps?.objectForKey("Timestamp") as String, forKey: "Timestamp")
        
        var type:String = aps?.objectForKey("Type") as String
        var timestamp:String = aps?.objectForKey("Timestamp") as String
        
        notifications.setObject(data, forKey: "\(type)-\(timestamp)")
        NSNotificationCenter.defaultCenter().postNotificationName("notification", object: nil)
    }
}
							

GitHub repositories


In the next section we’ll have a look at the bootstrap CMS backoffice, the website you’ll use to manage your contents, tags and users.

Click here to proceed to the backoffice tutorial

If you find this interesting please share: