Tobias Tobias - 1 month ago 13
reST (reStructuredText) Question

REST API, Swift, Automatic Update

I'm currently struggling to find an easy-to-use programming approach/design pattern, which solves the following problem:

I've got an REST API where the iOS app can request the required data. The data is needed in different ViewControllers. But the problem is, that the data should "always" be up to date. So I need to set up a timer which triggers a request every 5-20 seconds, or sth like that. Everytime the data changes, the view needs to be updated (at the current viewcontroller, which is displayed).
I tried some stuff with delegation and MVC Pattern, but it's kind a messy. How is it done the right way?

In my current implementation I only can update the whole UICollectionView, not some specific cells, because I don't know how the data changed. My controller keeps track of the data from the api and updates only if the hash has changed (if data changed on the server). My models always holds the last fetched data.
It's not the perfect solution, in my opinion..

I also thought about models, that keep themselves up to date, to abstract or virtualise my Rest-API. In this case, my controller doesn't even know, that it isn't directly accessible data.

Maybe someone can help me out with some kind of programming model, designpattern or anything else. I'm happy about anything!




UPDATE: current implementation

The Controller, which handles all the data

import Foundation
import SwiftyJSON
import SwiftyTimer

class OverviewController {

static let sharedInstance = OverviewController()

let interval = 5.seconds
var delegate : OverviewControllerUpdateable?
var model : OverviewModel?
var timer : NSTimer!

func startFetching() -> Void {
self.fetchData()

timer = NSTimer.new(every: interval) {
self.fetchData()
}

timer.start(modes: NSRunLoopCommonModes)
}

func stopFetching() -> Void {
timer.invalidate()
}

func getConnections() -> [Connection]? {
return model?.getConnections()
}

func getConnectionsSlave() -> [Connection]? {
return model?.getConnectionsSlave()
}

func getUser() -> User? {
return model?.getUser()
}

func countConnections() -> Int {
if let count = model?.getConnections().count {
return count
}
return 0
}

func countConnectionsSlave() -> Int {
if let count = model?.getConnectionsSlave().count {
return count
}
return 0
}

func fetchData() {
ApiCaller.doCall(OverviewRoute(), completionHandler: { (data, hash) in

if let actModel = self.model {
if (actModel.getHash() == hash) {
//no update required
return
}
}

var connections : [Connection] = []
var connectionsSlave : [Connection] = []

for (_,connection):(String, JSON) in data["connections"] {
let connectionObj = Connection(json: connection)
if (connectionObj.isMaster == true) {
connections.append(connectionObj)
} else {
connectionsSlave.append(connectionObj)
}
}

let user = User(json: data["user"])

//model needs update
let model = OverviewModel()
model.setUser(user)
model.setConnections(connections)
model.setConnectionsSlave(connectionsSlave)
model.setHash(hash)

self.model = model

//prevent unexpectedly found nil exception
if (self.delegate != nil) {
self.delegate!.reloadView()
}

}, errorHandler: { (errors) in
}) { (progress) in
}
}
}

protocol OverviewControllerUpdateable {
func reloadView()
}


The model, which holds the data:

class OverviewModel {

var user : User!
var connections : [Connection]!
var connectionsSlave : [Connection]!
var connectionRequests : [ConnectionRequest]!
var hash : String!

...
}


And in the ViewController, I use it like this:

class OverviewVC: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, OverviewControllerUpdateable {

let controller = OverviewController.sharedInstance

override func viewDidLoad() {
super.viewDidLoad()
self.controller.delegate = self
self.controller.startFetching()
}

//INSIDE THE UICOLLECTIONVIEW DELEGATE METHODS
...
if let user : User = controller.getUser() {
cell.intervalTime = interval
cell.nameLabel.text = "Ihr Profil"
}
...

func reloadView() {
self.userCollectionView.reloadData()
}
}

Answer

You could use a Singleton object to fetch your data periodically, then post notifications (using NSNotificationCenter) when the data is updated. Each view controller dependent on the data would listen for these notifications, then reload UI based on the updated data.

Comments