Receive calls to delegate methods from multiple view controllers

I have an application with SplitViewController

. MasterViewController

is UITableViewController

. And DetailViewController

is UIViewController

with MapView

.

enter image description here

I am calling the API to get a list of events. In a tableView, I want to display event names and addresses. And in the mapView, I want to mark the locations of these events.

I have a class called DataLoader

in which I call the API, parse the JSON object and create the objects Event

and successfully put them into the array.

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 named DataLoaderDelegate

with a method called func eventDataReceived(items: [Event]?, error: NSError?)

that is called after all event objects are inserted into the array, so I can pass it through this method.

I need this method, implemented in both ListViewController

where I populate the tableView and MapViewController

where I populate the mapView. So I implemented a delegate method eventDataReceived

in both display dispatchers to get event 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 implementation in the ListViewController

. I think the reason is because I am creating a new instance of the class DataLoader

in MapViewController

.

What should I do to have one instance DataLoader

for the entire application and receive its delegate method calls from all view controllers?

I've uploaded the demo project to my Dropbox if you need to run it to get a better idea.

Thank.

+3


source to share


1 answer


This is a best practice example for Singleton-pattern. Your DataLoader must implement a static transfer containing a reference to the same self object.

static let sharedInstance = DataLoader()

      

If you need a constant reference to the same singleton object. Name itDataLoader.sharedInstance

Make sure to always use .sharedInstance and remember to wipe out calls to the standard class initializer because it will still create new instances unless you programmatically block this behavior.

Clarifications

Just adding the singleton constant (shown above) will NOT cause the DataLoader to always return a singleton instance. The existing initializer calls:

   var myDataLoader = DataLoader()

      



will still initiate a new object every time it is called. A singleton instance is achieved with:

   var mySingletonDataLoader = DataLoader.sharedInstance

      

You can change the standard (non-single) initializers, for example:

init() {
NSLog("No instances allowed, please use .sharedInstance for a singleton")
}

      

Complete solution

Singleton is just one piece to solve the whole problem. The delegate pattern used is great for sending information from one object to another and hence delegating work. The task from the questioner requires a different broadcasting mechanism from one object to many other objects. So he decided to implement the observer pattern with NSNotificationCenter

+2


source







All Articles