REST API, Swift, Automatic Update - ios

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()
}
}

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.

Related

transfer data with protocol

I try to decode weather api
this is my struct class weatherModal :
import Foundation
struct WeatherModel:Decodable{
var main:Main?
}
struct Main:Decodable {
var temp : Double?
var feels_like : Double?
var temp_min:Double?
var temp_max:Double?
var pressure , humidity: Int?
}
I am trying to learn protocols. So this is where a make api call manager class :
protocol WeatherManagerProtocol:AnyObject {
func weatherData(weatherData:WeatherModel)
}
class WeatherManager{
var weather : WeatherModel?
weak var delegate :WeatherManagerProtocol?
public func callWeather(city:String) {
let url = "http://api.openweathermap.org/data/2.5/weather?q=\(city)&appid=1234"
URLSession.shared.dataTask(with:URL(string: url)!) { (data, response, err) in
if err != nil {
print(err!.localizedDescription)
} else {
do {
self.weather = try JSONDecoder().decode(WeatherModel.self, from: data!)
self.delegate?.weatherData(weatherData: self.weather!)
} catch {
print(error.localizedDescription)
}
}
}.resume()
}
}
In my ViewController what I want to do is user write city name on textfield and If user clicked the process button print the information about weather.
class ViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
var weatherManager = WeatherManager()
var data : WeatherModel?
override func viewDidLoad() {
super.viewDidLoad()
weatherManager.delegate = self
// Do any additional setup after loading the view.
}
#IBAction func processButtonClicked(_ sender: Any) {
if textField.text != "" {
weatherManager.callWeather(city: textField.text ?? "nil")
print(data?.main?.humidity) // it print nil
} else{
print("empty")
}
}
extension ViewController: WeatherManagerProtocol{
func weatherData(weatherData: WeatherModel) {
self.data = weatherData
print(self.data.main)
// in here I can show my data
}
}
When I clicked process button it always print nil. Why ? What am I doing wrong?
You seem not to understand how your own code is supposed to work. The whole idea of the protocol-and-delegate pattern you've set up is that the "signal" round-trips thru the weather manager on a path like this:
You (the ViewController) say weatherManager.callWeather
The weather manager does some networking.
The weather manager calls its own delegate's weatherData.
You (the ViewController) are that delegate, so your weatherData is called and that is where you can print.
So that is the signal path:
#IBAction func processButtonClicked(_ sender: Any) {
weatherManager.callWeather(city: textField.text ?? "nil") // < TO wm
}
func weatherData(weatherData: WeatherModel) { // < FROM wm
// can print `weatherData` here
}
You cannot short circuit this path by trying to print the weather data anywhere else. Stay on the path. You cannot turn this into a "linear" simple path; it is asynchronous.
If you do want it to look more like a "linear" simple path, use a completion handler instead of a delegate callback. That's what I do in my version of this same experiment, so my view controller code looks like this:
self.jsonTalker.fetchJSON(zip:self.currentZip) { result in
DispatchQueue.main.async {
// and now `result` contains the weather data, or an error
Even better, use the Combine framework (or wait until Swift 6 implements async/await).
You call to weatherManager.callWeather that start URLSession.shared.dataTask. the task is executed asynchronously, but callWeather return immediately. So, when you print data?.main?.humidity, the task did not finish yet, and data is still nil. After the task finish, you call weatherData and assign the response to data.

Detach from Firestore listener

I'm using Firestore together with Swift.
I have a singleton data class UserManager. I call this from my different ViewControllers to get data to populate my tableviews. I want the tableviews to automatically update when the collections are updated so I need to use a SnapshotListener. Everything works fine but I'm not sure how to detach from the listener when the Viewcontroller is closed.
In the singleton class I have methods like this below. The method gives a list of users and will be called from several different places around my app.
I also want to give back a reference to the listener so that I can detach from it when the Viewcontroller is closed. But I can't get it working. The below solution gives compiler error.
I've been trying to look at the reference, for example here
https://firebase.google.com/docs/firestore/query-data/listen but I need to get it working when the data is loaded in a singleton class instead of directly in the Viewcontroller. What is the way to go here?
In UserManager:
func allUsers(completion:#escaping ([User], ListenerRegistration?)->Void) {
let listener = db.collection("users").addSnapshotListener { querySnapshot, error in
if let documents = querySnapshot?.documents {
var users = [User]()
for document in documents {
let user = User(snapshot: document)
users.append(user)
}
completion(users, listener)
}
}
}
In ViewController:
override func viewDidLoad() {
super.viewDidLoad()
UserManager.shared.allUsers(completion: { (users, listener) in
self.users = users
self.listener = listener
self.tableView.reloadData()
})
}
deinit {
self.listener.remove()
}
I guess the compiler error that you see is referring to the fact that you are using listener into it's own defining context.
Try this for a change:
In UserManager:
func allUsers(completion:#escaping ([User])->Void) -> ListenerRegistration? {
return db.collection("users").addSnapshotListener { querySnapshot, error in
if let documents = querySnapshot?.documents {
var users = [User]()
for document in documents {
let user = User(snapshot: document)
users.append(user)
}
completion(users)
}
}
}
In ViewController:
override func viewDidLoad() {
super.viewDidLoad()
self.listener = UserManager.shared.allUsers(completion: { (users) in
self.users = users
self.tableView.reloadData()
})
}
deinit {
self.listener.remove()
}
I think that getDocument instead of addSnapshotListener is what you are looking for.
Using this method the listener is automatically detached at the end of the request...
It will be something similar to
func allUsers(completion:#escaping ([User])->Void) {
db.collection("users").getDocument { querySnapshot, error in
if let documents = querySnapshot?.documents {
var users = [User]()
for document in documents {
let user = User(snapshot: document)
users.append(user)
}
completion(users)
}
} }

Computer AI turns in a UIViewController

In a swift game using UIKit I am writing, a human player will interact with UIKit UIButtons, GUI elements to take actions.
In the game, the player will play against AI players.
But here's the thing; the human player presses buttons and interacts and the AI player does not.
Given a simple UIViewController;
class SampleViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buyBtnPressed(_ sender: UIButton) {
print ("pressed")
}
}
So what I'm trying to ascertain is, how does the AI player itself take actions and handling turns within the context of the current view controller?
I believe the best way to do this is that there should be a loop that will wait until all players have completed their respective turns.
But where does this loop go? In the view did load?
If so, won't it eat up memory, or potentially lead (if not careful) to an endless loop?
I'm finding it hard to ascertain how an AI player can take actions within the given context of a UIViewController considering GUI elements are for human interaction.
I don't mean the AI should be animating pressing buttons or interacting with the screen, I mean; I have a UIViewController, it has a view did load; what is the strategy of implementing AI turns and whether or not this should be be achieved in a "game loop" in the View did load or can this be achieved in another way?
My question is; given the context of a UIViewController; how can I code the handling of an AI player taking turns and can this be achieved with a loop or another strategy?
Many thanks
edit: Code is now added
I have written out a turn base manager using Swift playgrounds, and 2 examples one using a UIViewController and another is a loop.
code now follows;
import Foundation
import GameplayKit
class Player {
var name: String
public private(set) var isAI: Bool = false
public private(set) var turnOrder: Int = 0
init(name: String, isAI: Bool?) {
self.name = name
if let hasAI = isAI {
self.isAI = hasAI
}
}
func setTurnOrderIndex(number: Int) {
self.turnOrder = number
}
}
let p1 = Player.init(name: "Bob", isAI: false)
let p2 = Player.init(name: "Alex", isAI: true)
protocol TurnOrderManagerDelegate: NSObjectProtocol {
func turnOrderWasSet()
}
protocol TurnDelegate: class {
func turnIsCompleted()
}
class Turn: NSObject {
weak var player: Player?
weak var delegate: TurnDelegate?
public private(set) var completed: Bool = false {
didSet {
delegate?.turnIsCompleted()
}
}
init(player:Player, delegate: TurnDelegate) {
self.player = player
self.delegate = delegate
}
func setAsComplete() {
self.completed = true
}
}
class TurnOrderManager: NSObject, TurnOrderManagerDelegate, TurnDelegate {
static var instance = TurnOrderManager()
public private(set) var turnOrderIndex: Int = 0
public private(set) var turnOrder: [Turn] = [Turn]() {
didSet {
self.turnOrderWasSet()
}
}
var playerOnTurn: Player? {
let turnObj = self.turnOrder[turnOrderIndex]
return (turnObj.player)
}
var allTurnsCompleted: Bool {
let filtered = turnOrder.filter { (turnObj:Turn) -> Bool in
return (turnObj.completed)
}.count
return (filtered == turnOrder.count)
}
func setTurnOrder(players:[Player]) {
if (self.turnOrder.count == 0) {
for playerObj in players {
let turnObj = Turn.init(player: playerObj, delegate: self)
self.turnOrder.append(turnObj)
}
}
}
func turnOrderWasSet() {
for (index, turnObj) in self.turnOrder.enumerated() {
turnObj.player?.setTurnOrderIndex(number: index)
}
}
func next() {
if (turnOrderIndex < (self.turnOrder.count - 1)) {
turnOrderIndex += 1
}
else {
turnOrderIndex = 0
}
}
internal func turnIsCompleted() {
print (" - turnIsCompleted")
TurnOrderManager.instance.next()
}
}
class GameModel {
var turnOrderManager: TurnOrderManager
init() {
self.turnOrderManager = TurnOrderManager.instance
self.turnOrderManager.setTurnOrder(players:[p1,p2])
}
// other game model stuff [...]
}
class Phase1State : GKState {
var gameModel: GameModel!
init(gameModel:GameModel) {
super.init()
self.gameModel = gameModel
}
override func isValidNextState(_ stateClass: AnyClass) -> Bool
{
return false
}
override func didEnter(from previousState: GKState?) {
}
override func willExit(to nextState: GKState) {
}
// MARK: - Action
func buy() {
let index = self.gameModel.turnOrderManager.turnOrderIndex
let turn = self.gameModel.turnOrderManager.turnOrder[index]
turn.setAsComplete()
}
}
class SomeViewController: UIViewController
{
var gameModel: GameModel?
weak var gamePhase: Phase1State?
var isPhaseComplete: Bool {
return self.gameModel?.turnOrderManager.allTurnsCompleted ?? false
}
override func viewDidLoad() {
super.viewDidLoad()
self.gameModel = GameModel.init()
self.gamePhase = Phase1State.init(gameModel: self.gameModel!)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func buyButtonPressed() {
self.gamePhase?.buy()
self.finishTurn()
}
func finishTurn() {
guard let turnIndex = self.gameModel?.turnOrderManager.turnOrderIndex else {
return
}
guard let turn = self.gameModel?.turnOrderManager.turnOrder[turnIndex] else {
return
}
turn.setAsComplete()
if (self.isPhaseComplete)
{
print ("All turns are completed")
}
else {
//self.gameModel?.turnOrderManager.next()
self.gamePhase?.buy()
guard let playerOnTurn = self.gameModel?.turnOrderManager.playerOnTurn else {
print ("No player is on turn")
return
}
print ("\(playerOnTurn.name) is on turn")
if (playerOnTurn.isAI)
{
self.gamePhase?.buy()
self.finishTurn()
}
}
}
}
// EXAMPLE 1 -- first attempt ...
let vc = SomeViewController()
vc.viewDidLoad()
vc.buyButtonPressed()
// EXAMPLE 2 -- another attempt ....
let gameModel: GameModel = GameModel.init()
let gamePhase = Phase1State.init(gameModel: gameModel)
// player then takes an action
while (gameModel.turnOrderManager.allTurnsCompleted == false)
{
let turnIndex = gameModel.turnOrderManager.turnOrderIndex
let turnObj = gameModel.turnOrderManager.turnOrder[turnIndex]
guard let playerOnTurn = turnObj.player else {
break
}
print ("Player \(playerOnTurn.name) is on turn")
gamePhase.buy()
}
print ("All turns are completed, advance to next phase")
The issue is;
On the finishTurn, it only seems to work if it relies on the first player in the index is a human player. If its not, I have no idea how to make it fire the buy action.
On the second example, I use a loop; but I'm concerned using a loop could end up just looping forever.
My query is therefore clarifyed, how can I ensure my view controller will fire actions for AI players when they don't press buttons and loop through each player and execute their respective turn.
Many thanks
Further edit:
I do not know if I should have the while (gameModel.turnOrderManager.allTurnsCompleted == false) loop inside my viewDidLoad() to act like a game loop.
There is no need to specifically use Sprite Kit for this. SpriteKit would be more to do with how the UI is made rather than how the logic of the game works.
However, I would recommend looking at GameplayKit. It's a framework that contains lots of built in game logic tools. Specifically you want something like the GKDecisionTree. There are a few WWDC videos about it too. GameplayKit can be used with SpriteKit, UIKit, SSceneKit or any other game engine that you decide to use (or not).
Also, the question you're asking is a very general question about game development. Having the computer "decide" to do something is quite a complex subject.
I'd also suggest having a quick watch of this video from AI & Games and other videos from that channel.
It'll give you an idea of how to approach your problem.
Session 609 and 608 from WWDC 2015 and 2016 are prob good :D
Regarding updating the AI.
Your AI should be event driven. You have the concept of "turns" and "players". There is a point in the game at which it becomes a "player's" "turn". (Even at the very beginning of the game it is either Player 1 or Player 2's turn.
At this time there are two possibilities. Either the player is an AI, or the player is a person.
As soon as this happens there should be some sort of trigger (like a function call or something) that tells the player its turn has started.
If that player is the AI then you need to start some sort of calculation (maybe with a built in delay to make it realistic) so that it decides what to do.
Look, I'm not sure on what kind of game you're making, buy you should probably learn SpriteKit, specially SKActions. With that, you can easily control the flow of events from your game.
With that said, how is your AI implementation? Based on your code, I would begin with something like this:
class AI {
enum Decision {
case doSomething
case doAnotherThing
case dontDoAnything
}
public func decide() -> Decision {
// Decide which action the AI will take...
return .doSomething // This return is just a example!
}
public func act(on : Decision) {
// Do whatever the AI needs based on a decision...
}
}
Then, in your ViewController:
class SampleViewController: UIViewController {
var ai = AI()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buyBtnPressed(_ sender: UIButton) {
print ("pressed")
ai.act(on: ai.decide())
}
}
I hope that helps!

How to structure code to deal with asynchronous Firebase snapshot? [duplicate]

This question already has answers here:
Returning method object from inside block
(3 answers)
Closed 5 years ago.
I have an problem that I can not solve. A lot of questions are in JS and I don't really understand them.
I'm using Firebase as my database for my IOS app and Swift. Here is my situation:
I have a class file that contains functions that can retrieve values in my database. I'm calling these functions in some viewControllers.
The values retrieved by these functions are immediately used in these viewControllers.
My problem is that my app crash because of nil values returned by the class file functions. This happen because of the asynchronous Firebase snapshot.
The variables assumed to contain the values are used before their value is assigned => My app crash, or printed values are nil.
Then my question is simple: How can I structure my code to avoid this issue? I already tried completions, but that's not working for me: functions are still asynchronous.
Here is one of my function in the class file:
func initAverageMark(completionHandler: #escaping (_ mark: Double) -> ()) {
let userRef = ref.child("users").child((user?.uid)!).child("mark")
userRef.observeSingleEvent(of: .value, with: { (snapshot) -> Void in
if let mark: Double = snapshot.value as? Double {
completionHandler(mark)
}
}) { (error) in
print(error.localizedDescription)
}
}
One of my viewController code:
private var totalAsks: Double!
override func viewDidLoad() {
super.viewDidLoad()
initInfos()
}
func initInfos() {
mainUser().initTotalAsks{ total in
self.totalAsks = total
}
initLabels()
}
func initLabels() {
totalAsksLabel.text = " \(totalAsks!)" // it crashs here
}
Assuming you'd want to set some label or something in your viewController to the value of mark you'd do it like this.
mainUser().initTotalAsks { mark in
self.totalAsksLabel.text = " \(mark)"
}
Edit
Or if you absolutely want to use that Double.
private var totalAsks: Double? = nil {
didSet {
initLabels()
}
}
override func viewDidLoad() {
super.viewDidLoad()
initInfos()
}
func initInfos() {
mainUser().initTotalAsks{ total in
self.totalAsks = total
}
}
func initLabels() {
guard totalAsks != nil else {
return
}
totalAsksLabel.text = " \(totalAsks!)"
}

Receive delegate method calls from multiple view controllers

I have an app with a SplitViewController. The MasterViewController is a UITableViewController. And the DetailViewController is a UIViewController with a MapView.
I'm calling an API to get a list of events. In the tableView, I want to display the names and the addresses of the events. And in the mapView, I want to mark the locations of those events.
I have a class called DataLoader in which I call the API, parse the JSON object and create Event objects and put them in an array successfully.
import Foundation
import SwiftyJSON
#objc protocol DataLoaderDelegate {
func eventDataReceived(items: [Event]?, error: NSError?)
}
public class DataLoader {
private let api = ApiClient()
var delegate: DataLoaderDelegate?
init() { }
public func fetchEvents() {
api.getEvents({ (data) -> Void in
let json = JSON(data)
self.processEventData(json["data"])
}, failure: { (error) -> Void in
println("Error fetching events: \(error?.localizedDescription)")
self.delegate?.eventDataReceived(nil, error: error)
})
}
private func processEventData(data: JSON) {
var events = [Event]()
if let eventsArray = data.array {
for eventObj in eventsArray {
let event = Event(
id: eventObj["id"].int!,
type: EventType(rawValue: eventObj["type"].int!)!,
location: eventObj["location"].string!,
status: EventStatus(rawValue: eventObj["status"].int!)!
)
event.allDay = eventObj["allDay"].int!
event.title = eventObj["title"].string
event.description = eventObj["description"].string
event.latitude = eventObj["lat"].double
event.longitude = eventObj["lng"].double
event.startDate = NSDate(string: eventObj["start"].string!)
event.endDate = NSDate(string: eventObj["end"].string!)
event.createdAtDate = NSDate(string: eventObj["created_at"].string!)
event.updatedAtDate = NSDate(string: eventObj["updated_at"].string!)
event.creatorEmail = eventObj["creatorEmail"].string
event.organizerEmail = eventObj["organizerEmail"].string
event.googleCalendarId = eventObj["google_cal_id"].string!
event.googleEventId = eventObj["google_event_id"].string!
event.htmlLink = eventObj["htmlLink"].string!
events.append(event)
}
// Order the events by the startDate value
events.sort({ $0.startDate!.timeIntervalSince1970 < $1.startDate!.timeIntervalSince1970 })
self.delegate?.eventDataReceived(events, error: nil)
}
}
}
I have a delegate called DataLoaderDelegate with a method called func eventDataReceived(items: [Event]?, error: NSError?) which is called after all the event objects are inserted into an array so I can pass it through that method.
I need this method implemented in both the ListViewController where I populate the tableView and MapViewController where I populate the mapView. So I have implemented the eventDataReceived delegate method in both view controllers in order to get the events objects.
ListViewController.swift
import UIKit
class ListViewController: UITableViewController, DataLoaderDelegate {
let loader = DataLoader()
private var events = [Event]()
override func viewDidLoad() {
super.viewDidLoad()
loader.delegate = self
loader.fetchEvents()
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return events.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
let event = events[indexPath.row]
cell.textLabel?.text = event.title
cell.detailTextLabel?.text = event.location
return cell
}
// MARK: - DataLoaderDelegate
func eventDataReceived(items: [Event]?, error: NSError?) {
println("ListViewController got \(items?.count) events")
if let events = items {
self.events = events
tableView.reloadData()
}
}
}
MapViewController.swift
import UIKit
class MapViewController: UIViewController, DataLoaderDelegate {
let loader = DataLoader()
override func viewDidLoad() {
super.viewDidLoad()
loader.delegate = self
}
// MARK: - DataLoaderDelegate
func eventDataReceived(items: [Event]?, error: NSError?) {
println("MapViewController got \(items?.count) events")
}
}
But here's the problem. Only the implementation in the ListViewController gets executed. I think the reason is I create a new instance of DataLoader class in the MapViewController.
What should I do to have one instance of DataLoader across the entire application and receive its delegate method calls from all view controllers?
I've uploaded a demo project to my Dropbox if you need to run it to get a better idea.
Thanks.
This is a best-practice case for a Singleton-pattern
Your DataLoader should implement a static let holding the reference to the same object of self.
static let sharedInstance = DataLoader()
When you want the reference to constantly the same singleton-object. Call it with DataLoader.sharedInstance
Make sure to always use .sharedInstance then and do not forget to wipe any calls of the standard initializer of the class, because it will still make new instances unless you block this behavior programmatically.
Explained
Only inserting the singleton constant (shown above) will NOT make DataLoader class always return the singleton instance. The existing initializer calls like:
var myDataLoader = DataLoader()
will still instanciate a new object, everytime they are called. The singleton instance is reached with:
var mySingletonDataLoader = DataLoader.sharedInstance
You could change your standard (non singleton) initializers like:
init() {
NSLog("No instances allowed, please use .sharedInstance for a singleton")
}
Complete solution
The singleton is only one piece to solve the whole problem. The used delegate-pattern works great for sending information from one object to another and therefore delegating the work.
The problem from the questioner needs another broadcasting mechanism from one object to many other object. Therefore he decided to implement the Observer-pattern with NSNotificationCenter

Resources