1

I am new to MVC design pattern. I created "DataModel" it will make an API call, create data, and return data to the ViewController using Delegation and "DataModelItem" that will hold all data. How to call a DataModel init function in "requestData" function. Here is my code:

protocol DataModelDelegate:class {
    func didRecieveDataUpdata(data:[DataModelItem])
    func didFailUpdateWithError(error:Error)
}

class DataModel: NSObject {
    weak var delegate : DataModelDelegate?
    func requestData() {

    }
    private func setDataWithResponse(response:[AnyObject]){
        var data = [DataModelItem]()
        for item in response{
            if let tableViewModel = DataModelItem(data: item as? [String : String]){
                data.append(tableViewModel)
            }
        }
        delegate?.didRecieveDataUpdata(data: data)
    }
}

And for DataModelItem:

class DataModelItem{
    var name:String?
    var id:String?

    init?(data:[String:String]?) {
        if let data = data, let serviceName = data["name"] , let serviceId = data["id"] {
            self.name = serviceName
            self.id = serviceId
        }
        else{
            return nil
        }
    }
}

Controller:

class ViewController: UIViewController {
    private let dataSource = DataModel()
    override func viewDidLoad() {
        super.viewDidLoad()
        dataSource.delegate = self
    }

    override func viewWillAppear(_ animated: Bool) {
        dataSource.requestData()
    }

}
extension ViewController : DataModelDelegate{
    func didRecieveDataUpdata(data: [DataModelItem]) {
        print(data)
    }

    func didFailUpdateWithError(error: Error) {
        print("error:  \(error.localizedDescription)")
    }


}
Ahmad F
  • 30,560
  • 17
  • 97
  • 143
HariKarthick
  • 1,369
  • 1
  • 19
  • 47

3 Answers3

4

How to implement simple MVC design pattern in Swift?

As a generic answer, in iOS development you're already doing this implicitly! Dealing with storyboard(s) implies the view layer and controlling the logic of how they work and how they are connected to the model is done by creating view controller, that's the default flow.

For your case, let's clarify a point which is: according to the standard MVC, by default the responsible layer for calling an api should be -logically- the view controller. However for the purpose of modularity, reusability and avoiding to create massive view controllers we can follow the approach that you are imitate, that doesn't mean that its the model responsibility, we can consider it a secondary helper layer (MVC-N for instance), which means (based on your code) is DataModel is not a model, its a "networking" layer and DataModelItem is the actual model.


How to call a DataModel init function in "requestData" function

It seems to me that it doesn't make scene. What do you need instead is an instance from DataModel therefore you could call the desired method.

In the view controller:

let object = DataModel()
object.delegate = self // if you want to handle it in the view controller itself
object.requestData()
Ahmad F
  • 30,560
  • 17
  • 97
  • 143
  • Please read [MVC wiki](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) and [discussion](https://softwareengineering.stackexchange.com/questions/293288/where-should-i-put-an-api-request-in-mvc) – Bill Feb 27 '19 at 02:52
  • Thanks for your answer Ahamad. But my request is i have to call a DataModel init function in networking model "requestData" function. – HariKarthick Feb 27 '19 at 06:42
  • @Bill I understand your point. However, please check: https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/Model-View-Controller/Model-View-Controller.html#//apple_ref/doc/uid/TP40010810-CH14-SW14, you can clearly read that "A model class shouldn't depend on anything other than other model classes". – Ahmad F Feb 27 '19 at 10:04
2

I am just sharing my answer here and I am using a codable. It will be useful for anyone:

Model:

import Foundation

struct DataModelItem: Codable{
    struct Result : Codable {
        let icon : String?
        let name : String?
        let rating : Float?
        let userRatingsTotal : Int?
        let vicinity : String?

        enum CodingKeys: String, CodingKey {
            case icon = "icon"
            case name = "name"
            case rating = "rating"
            case userRatingsTotal = "user_ratings_total"
            case vicinity = "vicinity"
        }
    }
    let results : [Result]?
}

NetWork Layer :

import UIKit

protocol DataModelDelegate:class {
    func didRecieveDataUpdata(data:[String])
    func didFailUpdateWithError(error:Error)
}

class DataModel: NSObject {
    weak var delegate : DataModelDelegate?
    var theatreNameArray = [String]()
    var theatreVicinityArray = [String]()
    var theatreiconArray = [String]()
    func requestData() {
        Service.sharedInstance.getClassList { (response, error) in
            if error != nil {
                self.delegate?.didFailUpdateWithError(error: error!)
            } else if let response = response{
                self.setDataWithResponse(response: response as [DataModelItem])
            }
        }
    }

    private func setDataWithResponse(response:[DataModelItem]){
        for i in response[0].results!{
            self.theatreNameArray.append(i.name!)
            self.theatreVicinityArray.append(i.vicinity!)
            self.theatreiconArray.append(i.icon!)

        }
        delegate?.didRecieveDataUpdata(data: theatreNameArray)
        print("TheatreName------------------------->\(self.theatreNameArray)")
        print("TheatreVicinity------------------------->\(self.theatreVicinityArray)")
        print("Theatreicon------------------------->\(self.theatreiconArray)")

    }
}

Controller :

class ViewController: UIViewController {
    private let dataSource = DataModel()
    override func viewDidLoad() {
        super.viewDidLoad()
        dataSource.delegate = self
    }

    override func viewWillAppear(_ animated: Bool) {
        dataSource.requestData()
    }

}
extension ViewController : DataModelDelegate{
    func didRecieveDataUpdata(data: [DataModelItem]) {
        print(data)
    }

    func didFailUpdateWithError(error: Error) {
        print("error:  \(error.localizedDescription)")
    }


}

APIManager :

class Service : NSObject{
    static let sharedInstance = Service()    
    func getClassList(completion: (([DataModelItem]?, NSError?) -> Void)?) {
        guard let gitUrl = URL(string: "") else { return }
        URLSession.shared.dataTask(with: gitUrl) { (data, response
            , error) in
            guard let data = data else { return }
            do {
                let decoder = JSONDecoder()
                let gitData = try decoder.decode(DataModelItem.self, from: data)
                completion!([gitData],nil)

            } catch let err {
                print("Err", err)
                completion!(nil,err as NSError)
            }
            }.resume()
    }
}
HariKarthick
  • 1,369
  • 1
  • 19
  • 47
-1

I would recommend using a singleton instance for DataModel, since this would be a class you would be invoking from many points in your application. You may refer its documentation at : Managing Shared resources using singleton With this you wont need to initialise this class instance every time you need to access data.

Abhinav Saldi
  • 44
  • 1
  • 5
  • 2
    Do you think that there is a question we might ask: Why singletons or why not? :) – Ahmad F Feb 26 '19 at 17:16
  • Sure. Singleton object reference is an approach to implement single source data availability in MVC. A data model in MVC is the source to drive content in application, keeping it singleton gives all View controllers to consume data from any invocation point. Not making Data or its controller as singleton will introduce redundancy of calls, initialisers, memory leaks in this case. Every developer is free to choose though. It's my recommendation as I find it practical for data. Please let me know if you have more questions regarding the approach. – Abhinav Saldi Feb 26 '19 at 17:27
  • I understand the appeal of a singletons, but it’s generally frowned upon for data models. – Rob Feb 26 '19 at 17:29
  • I agree on data model, but above example data model class is interacting with network + saving data & behaving as a network layer as well, so was the approach to use singleton came in. – Abhinav Saldi Feb 26 '19 at 17:32
  • I think it might worth to review the following point of view(s): https://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons https://cocoacasts.com/are-singletons-bad https://www.swiftbysundell.com/posts/avoiding-singletons-in-swift https://www.reddit.com/r/iOSProgramming/comments/49rt5f/are_singletons_always_a_bad_choice/ – Ahmad F Feb 26 '19 at 17:35
  • Thanks for input @AhmadF, I'll take a look at this post. – Abhinav Saldi Feb 26 '19 at 17:42
  • @AbhinavSaldi - OK, I get what you’re saying. But the answer isn’t “make `DataModel` a singleton”, but rather “this network API call stuff simply doesn’t belong in an object called `DataModel`”. – Rob Feb 26 '19 at 17:43