Search code examples
swiftxcodeapidelegatesviewcontroller

One network handler class for multiple ViewControllers


I have an app which has 5 different view controllers, accessible by a UITabBar.

Each of these ViewControllers will access API Data through 5 different URLs taken from the same API source.

How can I re-use the below API request code so I do not have to re-type this block 5 times in my code? The below code is my current set-up and the delegate is the first view controller.

protocol ClubFixturesManagerDelegate {

    func didUpdateTable (club: [ClubData])
    func didFailWithError(error: Error) 
}


struct MyClubFixturesManager{

    let getclubListURL = "someURL"
    var delegate: ClubFixturesManagerDelegate?

    func getClubID(clubID: String){

        let getClubResultsURL = "someURL"
        performRequest(with: getClubResultsURL)

    }


    func performRequest(with urlString: String){

        if let url = URL(string: urlString){

            var request = URLRequest(url: url)
            request.httpMethod = "GET"
            request.setValue("APIToken", forHTTPHeaderField: "Authorization")

            //create URL session
            let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in

                if error != nil{
                    self.delegate?.didFailWithError(error: error!)
                    return
                }

                if let safeData = data {

                    if let clubResults = self.parseJSON(safeData){

                        self.delegate?.didUpdateTable(club: clubResults)

                    }

                }

            }

            task.resume()

        }

    }

    func parseJSON(_ teamData: Data) -> [ClubData]?{

        let decoder = JSONDecoder()

        do{

            var clubInfo: [ClubData] = []
            let decodedData = try decoder.decode(ClubSeasonData.self, from: teamData)
            for entry in decodedData.matches{

                let updatedDate = ISO8601Converter.converter(for: entry.utcDate)

                let clubInstance = ClubData(utcDate: updatedDate, homeTeam: entry.homeTeam.name, awayTeam: entry.awayTeam.name, homeTeamScore: entry.score.fullTime.homeTeam ?? nil, awayTeamScore: entry.score.fullTime.awayTeam ?? nil)
                clubInfo.append(clubInstance)

            }

            return clubInfo

        }

        catch{

              print(error)

            }

            return nil

        }

}

Solution

  • You can simply create a new instance of your api manager in your viewDidLoad and then assign the delegate and perform the request. Something like this:

    var apiManager: MyClubFixturesManager?
    
    func viewDidLoad() {
      super.viewDidLoad()
      apiManager = MyClubFixturesManager()
      apiManager.delegate = self // Assuming self conforms to ClubFixturesManagerDelegate protocol
      apiManager?.performRequest(with: "yourUrl")
    
    }
    

    As a side note I would make the delegate weak to avoid retain cycles and memory leak. You just need to do something like this:

    weak var delegate: ClubFixturesManagerDelegate?
    

    And to do so you would need to make the protocol class bound:

    protocol ClubFixturesManagerDelegate: class {
    
        func didUpdateTable (club: [ClubData])
        func didFailWithError(error: Error) 
    }