Search code examples
iosarraysswiftrx-swiftjsondecoder

How can I print all the relevant elements of an array fetched from an API as labels in a tableview cell using rxswift?


My model is like this;

import Foundation

struct TimeStamp: Decodable
{
    let status : String
    let message: String
    let zones: [Zone]
}

struct Zone: Decodable
{
    let countryCode : String
    let countryName : String
    let zoneName: String
    let gmtOffset : Int
    let timestamp : Int
}

TimeStampViewModel

import Foundation
import RxSwift
import RxCocoa

class TimeStampViewModel
{
    let timeStamps : PublishSubject<[TimeStamp]> = PublishSubject()
    let error : PublishSubject<String> = PublishSubject()
    let loading : PublishSubject<Bool> = PublishSubject()
    
    func requestData()
    {
        self.loading.onNext(true)
        let url = URL(string: "http://api.timezonedb.com/v2.1/list-time-zone?key=QQFJMAE732I4&format=json")!
        WebService().downloadTimeStamp(url: url)
        {
            result in
            self.loading.onNext(false)
            switch result
            {
            case.success(let timeStamps):
                self.timeStamps.onNext(timeStamps)
            case.failure(let error):
                switch error
                {
                case.parsingError:
                    self.error.onNext("Parsing Error")
                case.serverError:
                    self.error.onNext("Server Error")
                }
            }
        }
    }
}

WebService

import Foundation

enum TimeStampError : Error
{
    case serverError
    case parsingError
}


class WebService
{
    func downloadTimeStamp(url: URL, completion: @escaping (Result<[TimeStamp], TimeStampError>) -> ())
    {
        URLSession.shared.dataTask(with: url)
        {
            data, response, error in
            
            if let _ = error
            {
                completion(.failure(TimeStampError.serverError))
            }
            
            else if let data = data
            {
                let timeStampList = try? JSONDecoder().decode(TimeStamp.self, from: data)

                if let timeStampList = timeStampList
                {
                    completion(.success([timeStampList]))
                }
                else
                {
                    completion(.failure(.parsingError))
                }
            }
        }.resume()
    }
}

CountryHoursVC ViewController

import UIKit
import RxSwift
import RxCocoa

class CountryHoursVC: UIViewController, UITableViewDelegate
{
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var indicatorView: UIActivityIndicatorView!
    
    let timeStampVM = TimeStampViewModel()
    let disposeBag = DisposeBag()
    
    var timeStampList = [TimeStamp]()
    
    override func viewDidLoad()
    {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        view.backgroundColor = .black
        tableView.rx.setDelegate(self).disposed(by: disposeBag)
        setupBindings()
        timeStampVM.requestData()
    }
    
    private func setupBindings()
    {
        timeStampVM.loading.bind(to: self.indicatorView.rx.isAnimating).disposed(by: disposeBag)
        
        timeStampVM.error.observe(on: MainScheduler.asyncInstance).subscribe
        {
            errorString in
            print(errorString)
        }.disposed(by: disposeBag)
        
        
        timeStampVM.timeStamps.observe(on: MainScheduler.asyncInstance).bind(to: tableView.rx.items(cellIdentifier: "CountryHourCell", cellType: CountryHourTableViewCell.self))
        {
            row, item, cell in
            cell.item = item
        }.disposed(by: disposeBag)
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
    {
        return 70
    }
}

CountryHourTableViewCell

import UIKit

var countryList = [String]()

class CountryHourTableViewCell: UITableViewCell
{
    @IBOutlet weak var countryNameLabel: UILabel!
    @IBOutlet weak var hourLabel: UILabel!

    
    override func awakeFromNib()
    {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) 
    {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }
    
    public var item : TimeStamp!
    {
        didSet
        {
            countryNameLabel.text = item.zones.first?.countryName
            let date = Date(timeIntervalSince1970: TimeInterval(item.zones.first?.timestamp ?? 0))
            
            // Saat dilimini HH:mm formatında ayarla
            let formatter = DateFormatter()
            formatter.dateFormat = "HH:mm"
            hourLabel.text = formatter.string(from: date)
        }
    }
}

I assigned the data I received with jsondecode into the zones array. I want to print the countryNames and timestamps in the zones array with didSet{} in the tableviewcell, but I can only write the first or last element of the array. Is there a chance to write them all down cell by cell?

My app looks like this

What I expect to happen is in this style of implementation


Solution

  • The problem is in your WebService. You have it returning an array of TimeStamp objects when the URL you are hitting only returns a single object. Your cells should each display a single Zone object.

    I suggest you get rid of the WebService completely and just use:

    timeStamps = URLSession.shared.rx.data(request: URLRequest(url: url))
        .decode(type: TimeStamp.self, decoder: JSONDecoder())
        .map { $0.zones }
        .catch { [error] in
            error.onNext($0.localizedDescription)
            return Observable.just([])
        }