How to fix TableView numberOfRowsInSection returning nil? - ios

I'm trying to retrieve a 5 day forecast using the OpenWeatherMap API, I'm not sure why but each time I call my weatherCount() method it returns nil.
In the view model I use a print statement to verify the number of rows should be 40. I have tried to use guard statements and force unwrapping which just crashes the program. I tried implementing callback methods but don't think I did them correctly.
WeatherViewModel
import Foundation
class WeatherViewModel {
var weatherInfo: WeatherData?
weak var delegate: WeatherDelegate?
func getWeatherData() {
let weather = "https://api.openweathermap.org/data/2.5/forecast?q=London,GB&appid=fe3e0ecae7e573d25b37542f96f66f1a"
guard let url = URL(string: weather) else {
print("Could not reach the API endpoint") // this guard is not being hit
return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in //data task object, completion handler(trailing closure)
DispatchQueue.main.async {
guard error == nil else { // Checking for errors in the request
print("Error retrieved was: \(error)")
return
}
guard let weatherResponse = data else { //checks we got the data from request
print("Could not retrieve data instead got \(data)")
return }
}
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
let responseData = try decoder.decode(WeatherData.self, from: data!)
DispatchQueue.main.async {
// print("Delegate method shows \(self.delegate?.didRecieve(forecast: responseData))")
self.weatherInfo = responseData
print(self.weatherInfo)
print("Number of rows in section will be : \(self.weatherInfo?.list.count ?? 1)")
}
}
catch let e as Error {
print("Error creating current weather from JSON because: \(e.localizedDescription)")
print("Error in parsing the JSON")
NSLog("Error hit when calling weather service \(e)")
}
}
task.resume()
}
func weatherCount() -> Int {
let numberOfRows = self.weatherInfo?.list.count
print("Number of rows in weatherCount : \(numberOfRows)")
return numberOfRows ?? 1
}
}
WeatherTableViewController
import UIKit
import Foundation
class WeatherTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tableView: UITableView!
lazy var viewModel: WeatherViewModel = {
return WeatherViewModel()
}()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
DispatchQueue.main.async {
self.viewModel.getWeatherData()
self.tableView.reloadData()
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 5
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//print("Number of rows in section is: \(viewModel.weatherInfo?.list.count)")
//print("Rows: \(viewModel.weatherCount())")
return viewModel.weatherCount() ?? 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let weatherCell = tableView.dequeueReusableCell(withIdentifier: "WeatherCell", for: indexPath)
weatherCell.textLabel?.text = " The current temperature is : \(viewModel.weatherInfo?.list[indexPath.row].main?.temp ?? 0)"
print(viewModel.weatherInfo?.list[indexPath.row].main?.temp)
return weatherCell
}
}
numberOfRowsInSection should return 40 however returns nil
Weather
import Foundation
struct WeatherData: Decodable {
let cod: String
let message: Double
let cnt: Int
let list: [Info]
}
struct Info: Decodable {
let dt: Date
let main: WeatherInfo?
}
struct WeatherInfo: Decodable {
let temp: Double
let temp_min: Double
let temp_max: Double
let pressure: Double
let sea_level: Double
let grnd_level: Double
let humidity: Int
let temp_kf: Double
}
private enum CodingKeys: String, CodingKey {
case minTemp = "temp_min"
case maxTemp = "temp_max"
case seaLevel = "sea_level"
case temp
case pressure
case groundLevel = "grnd_level"
case humidity
case temp_kf
}

Use a completion handler to get notify from the weather data parsing and then reload tableView as below,
func getWeatherData(_ completion: #escaping () -> Void) {
let weather = "https://api.openweathermap.org/data/2.5/forecast?q=London,GB&appid=fe3e0ecae7e573d25b37542f96f66f1a"
guard let url = URL(string: weather) else {
print("Could not reach the API endpoint") // this guard is not being hit
return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in //data task object, completion handler(trailing closure)
guard error == nil else { // Checking for errors in the request
print("Error retrieved was: \(error)")
return
}
guard let weatherResponse = data else { //checks we got the data from request
print("Could not retrieve data instead got \(data)")
return
}
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
let responseData = try decoder.decode(WeatherData.self, from: data!)
DispatchQueue.main.async {
// print("Delegate method shows \(self.delegate?.didRecieve(forecast: responseData))")
self.weatherInfo = responseData
completion()
}
}
catch let e as Error {
print("Error creating current weather from JSON because: \(e.localizedDescription)")
print("Error in parsing the JSON")
NSLog("Error hit when calling weather service \(e)")
}
}
task.resume()
}
Update viewDidLoad as,
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
self.viewModel.getWeatherData() {
self.tableView.reloadData()
}
}

Related

Network call is successful but the data not showing (swift)

I am trying to make a very simple app in MVVM and I must be missing something here but I can't figure it out. I have all the error handling in my NewsService class and I print success if all goes right and it receives the data. I get that success every time, the issue is the "print(articles)" are not printing anything at all.
class NewsTableViewModel {
var articles = [Article]() {
didSet {
print(articles)
}
}
func fetchNews() {
NewsService.shared.fetchNews { [weak self] articles in
guard let self = self else { return }
self.articles = articles
print(articles)
}
}
}
class NewsTableVC: UITableViewController, NewsTableViewModelDelegate {
private let reuseIdentifier = "ArticleCell"
private let newsTableVM = NewsTableViewModel()
// var article = [Article]() {
// didSet {
// DispatchQueue.main.async {
// self.tableView.reloadData()
// }
// }
// }
override func viewDidLoad() {
super.viewDidLoad()
newsTableVM.delegate = self
newsTableVM.fetchNews()
updateUI()
}
func updateUI() {
tableView.register(ArticleCell.self, forCellReuseIdentifier: reuseIdentifier)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// return article.count
return self.newsTableVM.articles.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! ArticleCell
// cell.articleTitleLabel.text = article[indexPath.row].title
// cell.articleDescriptionLabel.text = article[indexPath.row].description
cell.articleTitleLabel.text = newsTableVM.articles[indexPath.row].title
cell.articleDescriptionLabel.text = newsTableVM.articles[indexPath.row].description
return cell
}
}
struct Response: Codable {
let articles: [Article]
}
struct Article: Codable {
let title: String
let description: String
}
class NewsService {
static let shared = NewsService()
func fetchNews(completion: #escaping ([Article]) -> (Void)) {
if let urlString = URL(string: "") {
let task = URLSession.shared.dataTask(with: urlString) { data, response, error in
if let _ = error {
print("error")
return
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { return }
guard let data = data else {
return
}
let decoder = JSONDecoder()
do {
print("success")
let articles = try decoder.decode(Response.self, from: data).articles
completion(articles)
} catch {
return
}
}
task.resume()
}
}
}
In my view controller viewDidLoad, I call NewsTableViewModel().fetchNews(). And here is the entire NewsTableViewModel class. Ignore the double use of print(articles), I'm just trying to figure out where it's going wrong.
you did not cover all the cases, put debug print at:
guard let self = self else {
print("self is nill")
return completion([])
}
and:
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
print("error: \(response)")
return completion([])
}
guard let data = data else {
print("error: data is nill")
return completion([])
}
and:
do {
print("success")
let articles = try decoder.decode(Response.self, from: data).articles
completion(articles)
} catch (let error){
print("catch an error: \(error)
completion([])
}
also put the completion([]) in the error cases instead of return only.

iOS UITableView Not Showing

I'm kinda new to iOS, was working on network fetching from the GitHub API but not able to show the users in the table view. Below is the code,
View Controller:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var avatarImage: UIImageView!
#IBOutlet weak var userName: UILabel!
#IBOutlet weak var usersTableView: UITableView!
var network = Network()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
network.delegate = self
usersTableView.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
network.network()
}
}
extension ViewController: NetworkDelegate {
func updateTableView() {
DispatchQueue.main.async {
self.usersTableView.reloadData()
}
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let users = network.users {
print(users.count)
return users.count
} else {
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("CALLED")
let cell = tableView.dequeueReusableCell(withIdentifier: "userCell", for: indexPath) as! UserViewCell
return cell
}
}
btw, the identifier is from the .xib file, the identifier matches, I don't think the problem is occurring here.
Network File
import Foundation
protocol NetworkDelegate {
func updateTableView()
}
class Network {
var users: [GitHub]?
var delegate: NetworkDelegate?
func network() {
let url = "https://api.github.com/users"
let request: URLRequest?
if let URL = URL(string: url) {
request = URLRequest(url: URL)
URLSession.shared.dataTask(with: request!) { result, response, error in
if let data = result {
// print(String(data: data, encoding: .utf8)!)
self.users = self.parseJSON(data)
self.delegate?.updateTableView()
} else {
print(error!.localizedDescription)
}
}
.resume()
}
}
private func parseJSON(_ data: Data) -> [GitHub]? {
let json = JSONDecoder()
do {
let decodedData = try json.decode([GitHub].self, from: data)
// print(decodedData)
return decodedData
} catch {
print(error.localizedDescription)
}
return nil
}
}
The GitHub API Model
struct GitHub: Codable {
let login: String
let id: Int
let node_id: String
let avatar_url: String
let gravatar_id: String
let url: String
let html_url: String
let followers_url: String
let following_url: String
let gists_url: String
let starred_url: String
let subscriptions_url: String
let organizations_url: String
let repos_url: String
let events_url: String
let received_events_url: String
let type: String
let site_admin: Bool
}
When I run this code on the simulator, the output is blank (Below the label)
Not able to figure out where I'm doing wrong
Thanks In Advance.
Try to refactor you code using a completion handler without using the delegation pattern.
in your network file:
enum ApiError: Error {
case network(Error)
case genericError
case httpResponseError
case invalidData
case decoding
// you can handle your specific case
}
func network(completion: #escaping ( _ error: ApiError?, _ users: [GitHub]?)-> Void) {
let url = "https://api.github.com/users"
let request: URLRequest?
if let URL = URL(string: url) {
request = URLRequest(url: URL)
URLSession.shared.dataTask(with: request!) { result, response, error in
if let error = error {
completion(.network(error), nil)
return
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
completion( .httpResponseError, nil)
return
}
guard let data = result else {
completion(.invalidData, nil)
return
}
do {
let decodedData = try JSONDecoder().decode([GitHub].self, from: data)
completion(nil, decodedData)
} catch {
completion(.decoding, nil)
}
}
.resume()
}
}
then inside your ViewController you can use it this way:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
network.network { [weak self] error, users in
guard let self = self else { return }
if let error = error {
print(error)
return
}
DispatchQueue.main.async {
guard let users = users else { return }
self.users = users
self.tableView.reloadData()
}
}
}
if it still doesn't show and cellForRow doesn't get called, you probably have a problem with your constraints and the tableView frame is zero (either height, width or both).
Try to debug setting a breakpoint inside numberOfRowsInSection and then in your debug area po tableView or just print the tableView and check if width or height is zero. it will be probably get called a few times. The first time the frame should be zero but at some point you should get a frame with height and width. If don't then check your constraints.
You can check my example which has a table view 375 x 641
If your cell is a xib file then you have to register your cell with tableView.
before calling datasource in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
usersTableView(UINib(nibName: "userCell", bundle: nil), forCellReuseIdentifier: "userCell")
network.delegate = self
usersTableView.dataSource = self
}

Getting an error when my app tries to download JSON

I finally started updating the BonusData.json file that my app uses. Now I am getting an error when it tries to load the data. The complete code is below but I am getting "JSON Download Failed", which is contained in the downloadJSON function.
If I'm reading my code right, that would mean that I'm encountering an error in the
let posts = try JSONDecoder().decode(JsonFile.self, from: data)
completed(posts.bonuses)
section, but I'm not sure how to troubleshoot that any further. What is supposed to be happening is that the app looks at the server, downloads the JSON and then saves it locally to be used to populate the UITableView. If there is no data connection, then it should not care, and just use the local saved version. Because the app is loading up blank, I'm assuming it is also not working as it is intended.
Here is the complete code:
import UIKit
import os.log
import Foundation
class BonusListViewController: UITableViewController {
var bonuses = [JsonFile.JsonBonuses]()
var filteredBonuses = [JsonFile.JsonBonuses]()
var detailViewController: BonusDetailViewController? = nil
let defaults = UserDefaults.standard
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
// MARK: Search Support
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Enter two letter state to filter"
navigationItem.searchController = searchController
definesPresentationContext = true
// MARK: Settings Data Struct
struct Constants {
struct RiderData {
let riderNumToH = "riderNumToH"
let pillionNumToH = "pillionNumToH"
}
struct RallyData {
let emailDestinationToH = "emailDestinationToH"
}
}
//MARK: Load the bonuses
print("About to call loadBonuses")
loadBonuses { [weak self] bonuses in
self?.bonuses = bonuses ?? []
DispatchQueue.main.async {
self?.tableView.reloadData()
}
print("loadBonuses called")
}
}
// MARK: - Table View Configuration
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isFiltering() {
print("Showing \(filteredBonuses.count) Filtered Results")
return filteredBonuses.count
}
print("Found \(bonuses.count) rows in section.")
return bonuses.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "BonusListViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? BonusListViewCell else {
fatalError("The dequeued cell is not an instance of BonusListViewCell.")
}
// let bonus = bonuses[indexPath.row]
let bonus: JsonFile.JsonBonuses
if isFiltering() {
bonus = filteredBonuses[indexPath.row]
} else {
bonus = bonuses[indexPath.row]
}
let urlString = "http://tourofhonor.com/appimages/"+(bonus.imageName)
let url = URL(string: urlString)
cell.primaryImage.downloadedFrom(url: url!)
cell.nameLabel.text = bonus.name.capitalized
cell.bonusCodeLabel.text = bonus.bonusCode.localizedUppercase
cell.categoryLabel.text = bonus.category
cell.valueLabel.text = "\(bonus.value)"
cell.cityLabel.text = "\(bonus.city.capitalized),"
cell.stateLabel.text = bonus.state.localizedUppercase
return cell
}
// MARK: Functions
// MARK: - Fetch JSON from ToH webserver
func downloadJSON(completed: #escaping ([JsonFile.JsonBonuses]?) -> ()) {
let url = URL(string: "http://tourofhonor.com/BonusData.json")!
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error == nil, let data = data {
do {
let posts = try JSONDecoder().decode(JsonFile.self, from: data)
completed(posts.bonuses)
print("URLSession did not fail")
} catch {
print("JSON Download Failed")
}
} else {
print("downloadJSON completed")
completed(nil)
}
}.resume()
}
func saveBonuses(_ bonuses: [JsonFile.JsonBonuses], to url: URL) {
try? FileManager.default.removeItem(at: url)
do {
let data = try JSONEncoder().encode(bonuses)
try data.write(to: url)
print("saveBonuses successful")
} catch {
print("Error saving bonuses to file:", error)
}
}
func loadBonusesFromFile(_ url: URL) -> [JsonFile.JsonBonuses]? {
do {
let data = try Data(contentsOf: url)
let bonuses = try JSONDecoder().decode([JsonFile.JsonBonuses].self, from: data)
print("loadBonusesFromFile successful")
return bonuses
} catch {
print("Error loading bonuses from file:", error)
return nil
}
}
func loadBonuses(completion: #escaping ([JsonFile.JsonBonuses]?) -> Void) {
let localBonusesURL = try! FileManager.default
.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("BonusData.json")
downloadJSON { bonuses in
if let bonuses = bonuses {
completion(bonuses)
self.saveBonuses(bonuses, to: localBonusesURL)
} else {
print("versions did not match")
completion(self.loadBonusesFromFile(localBonusesURL))
}
}
}
func searchBarIsEmpty() -> Bool {
// Returns true if the text is empty or nil
return searchController.searchBar.text?.isEmpty ?? true
}
func filterContentForSearchText(_ searchText: String, scope: String = "All") {
filteredBonuses = bonuses.filter({( bonus: JsonFile.JsonBonuses) -> Bool in
return bonus.state.localizedCaseInsensitiveContains(searchText)
})
tableView.reloadData()
}
func isFiltering() -> Bool {
return searchController.isActive && !searchBarIsEmpty()
}
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? BonusDetailViewController {
destination.bonus = bonuses[(tableView.indexPathForSelectedRow?.row)!]
}
}
}
extension BonusListViewController: UISearchResultsUpdating {
// MARK: - UISearchResultsUpdating Delegate
func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchController.searchBar.text!)
}
}
The JSON is hosted here: http://tourofhonor.com/BonusData.json
It looks like the JSON you're trying to download is not formatted correctly. It's missing a comma between objects, and it has an extra comma at the end of the list.
There are a number of tools to validate JSON, but one accessible one is https://jsonlint.com/. If you paste the output from http://tourofhonor.com/BonusData.json there, it will highlight the formatting errors for you and give you some guidance on how to fix them.
I'm going to focus on what I think is the core of your question rather than the technical fix.
I'm not sure how to troubleshoot that any further.
do {
// ...
let posts = try JSONDecoder().decode(JsonFile.self, from: data)
// ...
} catch let error {
// Do something with this error.
}
decode throws details about the exception which you can do when you get the error.

UITableView, cellForRowAt indexPath is not being called

I have been working on a launch database for SpaceX and I have successfully parsed my data but the function to create and add the data to the cell is not working. I have added the delegates and data sources but I still cannot find out why it won't run.
import UIKit
struct launchData : Decodable
{
let flight_number : Int
let launch_date_utc : String
struct rocketInfo : Decodable
{
let rocket_name : String
}
let rocket : rocketInfo
}
class LaunchViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var launchTableView: UITableView!
var arrayOfLaunchData:[launchData] = []
override func viewDidLoad()
{
super.viewDidLoad()
self.launchTableView.delegate = self
self.launchTableView.dataSource = self
getJsonData()
self.launchTableView.reloadData()
}
func getJsonData()
{
let jsonUrlString = "https://api.spacexdata.com/v2/launches"
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let launchDataDecoded = try JSONDecoder().decode([launchData].self, from: data)
print(launchDataDecoded)
} catch let jsonErr {
print("Error Serialization json:", jsonErr )
}
}.resume()
print("getJsonData ran")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("")
print(arrayOfLaunchData.count)
print("")
print("TableView number of rows ran")
return arrayOfLaunchData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellID")
let launch = self.arrayOfLaunchData[indexPath.row]
let flightNumber = launch.flight_number
let rocketName = launch.rocket.rocket_name
cell?.textLabel?.text = "Mission " + String(flightNumber)
let launchDate = launch.launch_date_utc
cell!.detailTextLabel!.text = "Launch Date: " + launchDate + "Rocket Used: " + rocketName
self.launchTableView.reloadData()
print("TableView cellForRowAt ran")
return cell!
}
}
First of all never call reloadData() in cellForRowAt! Delete the line
Two major issues:
reloadData() is called too soon.
The data source array is not populated after receiving the data.
The solution is to delete the line
self.launchTableView.reloadData()
(also) in viewDidLoad() and change getJsonData() to
func getJsonData()
{
let jsonUrlString = "https://api.spacexdata.com/v2/launches"
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
self.arrayOfLaunchData = try JSONDecoder().decode([launchData].self, from: data)
print(launchDataDecoded)
DispatchQueue.main.async {
self.launchTableView.reloadData()
}
} catch {
print("Error Serialization json:", error )
}
}.resume()
print("getJsonData ran")
}
because dataTask works asynchronously.
Note:
Please conform to the naming convention that struct and class names start with a capital letter (LaunchData, RocketInfo) and all names are supposed to be camelCased rather than snake_cased.
Remove self.launchTableView.reloadData() from viewDidLoad()
and put on getting successfully data
do {
let launchDataDecoded = try JSONDecoder().decode([launchData].self, from: data)
print(launchDataDecoded)
self.launchTableView.reloadData()
} catch let jsonErr {
print("Error Serialization json:", jsonErr )
}
}.resume()
getJsonData() is follow asynchronous. hope this help!

Populate tableview with multiple Alamofire chained requests

I am new at Alamofire and I am following the book IOS Apps with REST APIs, where I try to parse JSON and populate the tableview but the problem is that it calls numberOfRowsInSection before the function loadGists finishes calling the Alamofire chained requests so I can populate the Array and then populate the tableview accordingly, it seems that .responseArray function is called Asynchronous or called after numberOfRowsInSection function.
var gists: [Gist]!
#IBOutlet var tabelView1: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tabelView1.delegate = self
self.tabelView1.dataSource = self
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
loadGists()
}
func loadGists() {
GitHubAPIManager.sharedInstance.getPublicGists() { result in
guard result.error == nil else {
print(result.error)
// TODO: display error
return
}
if let fetchedGists = result.value {
self.gists = fetchedGists
}
DispatchQueue.main.async {
self.tableView1.reloadData()
}
}
}
Where GitHubAPIManager Class has:
class GitHubAPIManager: NSObject {
static let sharedInstance = GitHubAPIManager()
func getPublicGists(completionHandler: (Result<[Gist]>) -> Void) {
Alamofire.request(GistRouter.GetPublic())
.responseArray { (response) in
completionHandler(response.result)
}
}
}
And responseArray is:
public func responseArray<T: ResponseJSONObjectSerializable>(
completionHandler: Response<[T]) -> Self {
let serializer = ResponseSerializer<[T]> { request, response, data, error in
guard error == nil else {
return .Failure(error!)
}
guard let responseData = data else {
let failureReason = "Array could not be serialized because input data was nil."
let error = Error.errorWithCode(.DataSerializationFailed,
failureReason: failureReason)
return .Failure(error)
}
let JSONResponseSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
let result = JSONResponseSerializer.serializeResponse(request, response,
responseData, error)
switch result {
case .Success(let value):
let json = SwiftyJSON.JSON(value)
var objects: [T] = []
for (_, item) in json {
if let object = T(json: item) {
objects.append(object)
}
}
return .Success(objects)
case .Failure(let error):
return .Failure(error)
}
}
return response(responseSerializer: serializer, completionHandler: completionHandler)
}
For table view:
func tableView(tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return gists.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath
indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let gist = gists[indexPath.row]
cell.textLabel!.text = gist.description
cell.detailTextLabel!.text = gist.ownerLogin
return cell
}
GistRouter is:
enum GistRouter: URLRequestConvertible {
static let baseURLString:String = "https://api.github.com"
case getPublic()
var method: HTTPMethod {
switch self {
case .getPublic:
return .get
}
}
func asURLRequest() throws -> URLRequest {
let result: (path: String, parameters: [String: AnyObject]?) = {
switch self {
case .getPublic:
return ("/gists/public", nil)
}
}()
let URL = Foundation.URL(string: GistRouter.baseURLString)!
var UrlRequest: URLRequest? = URLRequest(url: URL.appendingPathComponent(result.path))
UrlRequest = try URLEncoding.default.encode(UrlRequest!, with: result.parameters)
UrlRequest?.httpMethod = method.rawValue
return UrlRequest!
}
And Gist Class is:
class Gist: ResponseJSONObjectSerializable {
var id: String?
var description: String?
var ownerLogin: String?
var ownerAvatarURL: String?
var url: String?
required init(json: SwiftyJSON.JSON) {
self.description = json["description"].string
self.id = json["id"].string
self.ownerLogin = json["owner"]["login"].string
self.ownerAvatarURL = json["owner"]["avatar_url"].string
self.url = json["url"].string
}
required init() {
}
}
I have tried DispatchQueue.global(qos: .utility).async and also
let semaphore = DispatchSemaphore(value: 0) in different ways with no luck, I need to have Alamofire chained requests all done first and then numberOfRowsInSection called, please help.
It worked for me with:
var gists = [Gist]() {
didSet {
self.tabelView1.reloadData()
}
}

Resources