I am trying to make network requests to download data and put it into a UITableView. I am not sure what I am doing wrong but when I call viewModel.request() in the ViewController it is not populating the UITableView.
I left some of the code out to make it simple but the UITableView works fine when using URLSession, I just can't get it to work with Combine.
If I print viewModel.sections in viewDidLoad(), it prints an empty array:
ViewController
class ViewController: UIViewController {
var viewModel = TestViewModel()
override func viewDidLoad() {
super.viewDidLoad()
//TableView Setup
setUpTableView()
viewModel.request()
self.tableView.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return viewModel.sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.sections[section].sports.count
}
}
TestViewModel
class TestViewModel {
#Published var sections: [Sections] = []
var date: Date = Date()
var cancellables: AnyCancellable?
func request(){
let nfl = NetworkManager.download(endpoint: .nfl, date: date.query())
.decode(type: EventModel.self, decoder: JSONDecoder())
let nba = NetworkManager.download(endpoint: .nba, date: date.query())
.decode(type: EventModel.self, decoder: JSONDecoder())
let mlb = NetworkManager.download(endpoint: .mlb, date: date.query())
.decode(type: EventModel.self, decoder: JSONDecoder())
let nhl = NetworkManager.download(endpoint: .nhl, date: date.query())
.decode(type: EventModel.self, decoder: JSONDecoder())
self.cancellables = Publishers.Zip4(nfl, nba, mlb, nhl)
.eraseToAnyPublisher()
.sink(receiveCompletion: { (completion) in
switch completion {
case .finished:
break
case .failure(let error):
print("Error:",error.localizedDescription)
}
}, receiveValue: { [weak self] (nfl, nba, mlb, nhl) in
var section: [Sections] = []
if !nfl.events.isEmpty {
section.append(.init(icon: "football.fill", title: "NFL", sports: nfl.events, leagues: nfl.leagues))
}
if !nba.events.isEmpty {
section.append(.init(icon: "basketball.fill", title: "NBA", sports: nba.events, leagues: nba.leagues))
}
if !mlb.events.isEmpty {
section.append(.init(icon: "baseball.fill", title: "MLB", sports: mlb.events, leagues: mlb.leagues))
}
if !nhl.events.isEmpty {
section.append(.init(icon: "hockey.puck.fill", title: "NHL", sports: nhl.events, leagues: nhl.leagues))
}
self?.sections = section
})
// This is Empty
print(self.sections)
}
}
NetworkManager
class NetworkManager {
static func download(endpoint: Endpoint, date: String) -> AnyPublisher<Data, Error> {
let url = URL(string: "***API URL***")!
let final = url.appendingPathComponent(endpoint.build())
var components = URLComponents(url: final, resolvingAgainstBaseURL: true)!
components.queryItems = [
URLQueryItem(name: "dates", value: date),
]
let request = URLRequest(url: components.url!)
return
URLSession.shared.dataTaskPublisher(for: request)
.subscribe(on: DispatchQueue.global(qos: .background))
.tryMap{(data, response) -> Data in
guard let response = response as? HTTPURLResponse,
response.statusCode >= 200 && response.statusCode < 300 else {
throw URLError(.badServerResponse)
}
return data
}
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}
You’re using SwiftUI #Published method in UIKit. What works best is CurrentValueSubject for UIKit.
Change you sections to be var sections CurrentValueSubject<[Sections], Never>([]) in your VM. Then in your VM request function, build up the sections data (_values) and send by sections.send(_values)
In your controller you need to bind to the VM.sections by viewModel.sections.sink { in $0 } where $0 is your data.
Related
I have a problem with fetching data from API. The DayViewCalendar is creating View before events data is fetched from API.
My main view is in SwiftUI
struct CalendarScreen: View {
#StateObject private var viewModel: ViewModel = ViewModel()
var body: some View {
NavigationView {
ZStack(alignment: .trailing) {
CalendarKitDisplayView(viewModel: viewModel)
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
I have a ViewModel which is fetching events data from API
import Combine
import Foundation
extension NSNotification.Name {
static let onEventLoaded = Notification.Name("onEventLoaded")
}
extension CalendarScreen {
class ViewModel: ObservableObject {
let calendarService = CalendarService()
#Published var calendarEvents: [CalendarEvent]
var cancellable: AnyCancellable?
init() {
self.calendarEvents = [CalendarEvent()]
}
func fetchCalendarEvents() {
cancellable = calendarService.getEvents()
.sink(
receiveCompletion: { _ in },
receiveValue: {
calendarEvents in self.calendarEvents = calendarEvents
NotificationCenter.default.post(name: .onEventLoaded, object: nil)
})
}
}
}
Calendar Service is just a service for singletion of repository
import Foundation
import Combine
struct CalendarService {
private var calendarRepository = CalendarRepository()
func getEvents() -> AnyPublisher<[CalendarEvent], Error> {
return calendarRepository.getEvents()
}
}
And calendarRepository is just simple URL Request for my API
import Combine
struct CalendarRepository {
private let agent = Agent()
private let calendarurl = "\(api)/calendars_events"
func getEvents() -> AnyPublisher<[CalendarEvent], Error>{
let urlString = "\(calendarurl)"
let url = URL(string: urlString)!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("Bearer \(AuthManager.shared.token)", forHTTPHeaderField: "Authorization")
return agent.run(request)
}
}
Agent is handling the request
class Agent {
let session = URLSession.shared
var cancelBag: Set<AnyCancellable> = []
func run<T: Decodable>(_ request: URLRequest) -> AnyPublisher<T, Error> {
return session
.dataTaskPublisher(for: request)
.decode(type: T.self, decoder: JSONDecoder())
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}
Everything is going in CalendarViewController from CalendarKit library which stands as follow:
import SwiftUI
import UIKit
class CalendarViewController: DayViewController {
convenience init(viewModel: CalendarScreen.ViewModel) {
self.init()
self.viewModel = viewModel
}
var viewModel = CalendarScreen.ViewModel()
var refresh: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
subscribeToNotification()
}
func subscribeToNotification() {
NotificationCenter.default.addObserver(
self, selector: #selector(eventChanged(_:)), name: .onDataImported, object: nil)
}
#objc func eventChanged(_ notification: Notification) {
print("notification")
reloadData()
}
override func eventsForDate(_ date: Date) -> [EventDescriptor] {
// HOW CAN I WAIT FOR THIS LINE TO FINISH FETCH DATA FROM API
viewModel.fetchCalendarEvents()
//
let calendarKitEvents = viewModel.calendarEvents.filter {
dateTimeFormat.date(from: $0.start) ?? Date() >= date
&& dateTimeFormat.date(from: $0.end) ?? Date() <= date
}.map { item in
let event = Event()
event.dateInterval = DateInterval(
start: self.dateTimeFormat.date(from: item.start) ?? Date(),
end: self.dateTimeFormat.date(from: item.end) ?? Date())
event.color = UIColor(InvoiceColor(title: item.title))
event.isAllDay = false
event.text = item.title
return event
}
return calendarKitEvents
}
let dateTimeFormat: DateFormatter = {
let df = DateFormatter()
df.locale = Locale(identifier: "pl")
df.timeZone = TimeZone(abbreviation: "CET")
df.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
return df
}()
}
And the SwiftUI and UIKit is bridged by UIViewControllerRepresntable
import SwiftUI
import UIKit
struct CalendarKitDisplayView: UIViewControllerRepresentable {
#ObservedObject var viewModel: CalendarScreen.ViewModel
func makeUIViewController(context: Context) -> DayViewController {
let dayViewCalendar = CalendarViewController(viewModel: viewModel)
return dayViewCalendar
}
func updateUIViewController(_ uiViewController: DayViewController, context: Context) {
}
}
And the entity CalendarEvent which is coded to CalendarKit event
public struct CalendarEvent: Codable, Identifiable {
public var id: Int = 0
var title: String = ""
var start: String = ""
var end: String = ""
var note: String?
}
My goal is to wait for viewModel.fetchCalendarEvents() to fetch data from API and then start other tasks.
override func eventsForDate(_ date: Date) -> [EventDescriptor] {
// HOW CAN I WAIT FOR THIS LINE TO FINISH FETCH DATA FROM API
viewModel.fetchCalendarEvents()
//
I tried to implement NotificationCenter with variable refresh but when i added and changed functions
To the CalendarViewController variable var refresh: Bool = false and push notification to ViewModel
func fetchCalendarEvents() {
cancellable = calendarService.getEvents()
.sink(
receiveCompletion: { _ in },
receiveValue: {
calendarEvents in self.calendarEvents = calendarEvents
NotificationCenter.default.post(name: .eventChanged, object: nil)
})
}
After that i added subscribe to event in init() function in my CalendarViewController and #selector as follow
#objc func eventChanged(_ notification: Notification) {
print("notification")
refresh = true
reloadData()
}
I tried to add but it stay in infinite loop and variable never change
override func eventsForDate(_ date: Date) -> [EventDescriptor] {
viewModel.fetchCalendarEvents()
while refresh == true {
}
}
I was thinking about using conclusion or completion handler but i am new in Swift programming and dont really know how it should looks like.
Using a completion handler your function should look like this:
func fetchCalendarEvents(_ completion: #escaping () -> Void) {
cancellable = calendarService.getEvents()
.sink(
receiveCompletion: { _ in },
receiveValue: {
calendarEvents in self.calendarEvents = calendarEvents
NotificationCenter.default.post(name: .eventChanged, object: nil)
completion()
})
}
And when calling it:
fetchCalendarEvents {
//finished, run some code.
}
I am using SwiftUI, UIKit and external library CalendarKit in my project. The problem is that events don't load while initializing on first load of calendar. When I change date in the in the nav, update event or create new event everything works fine. Events are reloading and are showed up on screen.
I have few classes in my projects. First is CalendarScreen which renders the SwiftUI view, ViewModel of CalendarScreen which loads data fetched from API. Service which provides repository and Repository class which runs URLRequest. The UIKit class of DayViewController where everything is going in:
class CalendarViewController: DayViewController {
convenience init(viewModel: CalendarScreen.ViewModel) {
self.init()
self.viewModel = viewModel
}
var viewModel = CalendarScreen.ViewModel()
override func viewDidLoad() {
super.viewDidLoad()
reloadData()
}
override func reloadData() {
dayView.timelinePagerView.pagingViewController.children.forEach({ (controller) in
if let controller = controller as? TimelineContainerController {
controller.timeline.layoutAttributes = eventsForDate(Date()).map(EventLayoutAttributes.init)
}
})
}
override func eventsForDate(_ date: Date) -> [EventDescriptor] {
return viewModel.fetchCalendarEvents {
var calendarKitEvents = [Event()]
calendarKitEvents = self.viewModel.calendarEvents.compactMap { item in
let event = Event()
event.dateInterval = DateInterval(
start: self.dateTimeFormat.date(from: item.start) ?? Date(),
end: self.dateTimeFormat.date(from: item.end) ?? Date())
event.color = UIColor(InvoiceColor(title: item.title))
event.isAllDay = false
event.text = item.title
return event
}
self.eventsOnCeldanr = calendarKitEvents
}
}
}
And the classes corresponding to my APICall the main function is func fetchCalendarEvents which return Events to my Controller
class Agent {
let session = URLSession.shared
func run<T: Decodable>(_ request: URLRequest) -> AnyPublisher<T, Error> {
return
session
.dataTaskPublisher(for: request)
.decode(type: T.self, decoder: JSONDecoder())
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}
struct CalendarRepository {
private let agent = Agent()
func getEvents() -> AnyPublisher<[CalendarEvent], Error> {
let urlString = "\(calendarurl)"
let url = URL(string: urlString)!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
return agent.run(request)
}
}
struct CalendarService {
private var calendarRepository = CalendarRepository()
func getEvents() -> AnyPublisher<[CalendarEvent], Error> {
return calendarRepository.getEvents()
}
extension CalendarScreen {
class ViewModel: ObservableObject {
let calendarService = CalendarService()
#Published var calendarEvents: [CalendarEvent]
#Published var events: [Event]
var cancellable: AnyCancellable?
init() {
self.calendarEvents = [CalendarEvent()]
self.events = []
}
func fetchCalendarEvents(_ completion: #escaping () -> Void)
-> [EventDescriptor]
{
cancellable = calendarService.getEvents()
.sink(
receiveCompletion: { _ in },
receiveValue: {
calendarEvents in self.calendarEvents = calendarEvents
NotificationCenter.default.post(name: .onEventLoaded, object: nil)
self.createEvents()
completion()
})
return events
}
func createEvents() {
self.events = self.calendarEvents.compactMap({ (item) in
var event = Event()
event.dateInterval = DateInterval(
start: self.dateTimeFormat.date(from: item.start)!,
end: self.dateTimeFormat.date(from: item.end)!)
event.color = UIColor(InvoiceColor(title: item.title))
event.isAllDay = false
event.text = item.title
return event
})
}
}
}
}
So the problem is when I load view for the first time the reloadDate() returning empty [] array.
While i try to debug Events are in variable calendarKitEvents but without sucessful return and on first load function ends on reciveCompletion call instead of reciveValue call in fetchCalendarEvents function.
I want to get data from server and update my DB after that I'll show received data to the user. For this goal I have a method(getData()) in my view model that returns a Single I call and subscribe this method in the view controller(myVC.getData.subscribe({single in ...})) in this method at first I call and subscribe(#1)(getUnread()->Single) the method run but I can not get the single event, I can not understand why I can't get the event(#3) in call back(#4)
after that I want to save data with calling(#2)(save([Moddel])->single)
//I removed some part of this code it was to big
//This method is View Model
func getData() -> Single<[Model]> {
return Single<[Model]>.create {[weak self] single in
//#1
self!.restRepo.getUnread().subscribe({ [weak self] event in
//#4
switch event {
case .success(let response):
let models = response
//#2
self!.dbRepo.save(issues!).subscribe({ event in
switch event {
case .success(let response):
let models = response
single(.success(models))
case .error(let error):
single(.error(error))
}
}).disposed(by: self!.disposeBag)
case .error(let error):
single(.error(error))
}
}).disposed(by: self!.disposeBag)
return Disposables.create()
}
}
.
.
//I removed some part of this code it was to big
//This method is in RestRepo class
func getUnread() -> Single<[Model]> {
return Single<[Model]>.create { single in
let urlComponent = ApiHelper.instance.dolphinURLComponents(for: ApiHelper.ISSUES_PATH)
var urlRequest = URLRequest(url: urlComponent.url!)
ApiHelper.instance.alamofire.request(urlRequest).intercept().responseJSON { response in
debugPrint(response)
let statusCode = response.response?.statusCode
switch statusCode {
case 200:
do {
let models = try JSONDecoder().decode([Model].self, from: response.data!)
//#3
single(.success(models))
}catch{
print(error)
}
case 304:
debugPrint(response)
default:
single(.error(IssueResponseStatusCodeError(code: statusCode ?? 0)))
}
}
return Disposables.create()
}
First you need to change your thinking. You don't do anything in the app. At best, you lay out the Observable chains (which don't do anything anymore than water pipes "do" something.) Then you start the app and let the "water" flow.
So with that in mind, let's examine your question:
I want to get data from server...
It's not that "you" want to get the data. The request is made as a result of some action (probably a button tap) by the user or by some other side effect. What action is that? That needs to be expressed in the code. For the following I will assume it's a button tap. That means you should have:
class Example: UIViewController {
var button: UIButton!
var restRepo: RestRepo!
override func viewDidLoad() {
super.viewDidLoad()
let serverResponse = button.rx.tap
.flatMapLatest { [restRepo] in
restRepo!.getUnread()
.map { Result<[Model], Error>.success($0) }
.catchError { .just(Result<[Model], Error>.failure($0)) }
}
.share(replay: 1)
}
}
protocol RestRepo {
func getUnread() -> Observable<[Model]>
}
struct ProductionRestRepo: RestRepo {
func getUnread() -> Observable<[Model]> {
let urlComponent = ApiHelper.instance.dolphinURLComponents(for: ApiHelper.ISSUES_PATH)
let urlRequest = URLRequest(url: urlComponent.url!)
return URLSession.shared.rx.data(request: urlRequest)
.map { try JSONDecoder().decode([Model].self, from: $0) }
}
}
class ApiHelper {
static let ISSUES_PATH = ""
static let instance = ApiHelper()
func dolphinURLComponents(for: String) -> URLComponents { fatalError() }
}
struct Model: Decodable { }
The thing to notice here is that getUnread() is an effect that is caused by button.rx.tap. The above establishes a cause-effect chain.
Your question goes on to say "you" want to:
... update my DB...
Here, the cause is the network request and the effect is the DB save so we simply need to add this to the viewDidLoad (note that the code below uses RxEnumKit.):
let dbResponse = serverResponse
.capture(case: Result.success)
.flatMapLatest { [dbRepo] models in
dbRepo!.save(models)
.map { Result<Void, Error>.success(()) }
.catchError { .just(Result<Void, Error>.failure($0)) }
}
Your question also says that "you" want to:
... show received data to the user.
Note here that showing the received data to the user has nothing to do with the DB save. They are two independent operations that can be done in parallel.
Showing the received data to the user has the serverResponse as the cause, and the showing as the effect.
serverResponse
.capture(case: Result.success)
.subscribe(onNext: { models in
print("display the data to the user.", models)
})
.disposed(by: disposeBag)
Lastly, you don't mention it, but you also have to handle the errors:
So add this to the viewDidLoad as well:
Observable.merge(serverResponse.capture(case: Result.failure), dbResponse.capture(case: Result.failure))
.subscribe(onNext: { error in
print("an error occured:", error)
})
.disposed(by: disposeBag)
The code below is all of the above as a single block. This compiles fine...
import UIKit
import RxSwift
import RxCocoa
import EnumKit
import RxEnumKit
extension Result: CaseAccessible { }
class Example: UIViewController {
var button: UIButton!
var restRepo: RestRepo!
var dbRepo: DBRepo!
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
let serverResponse = button.rx.tap
.flatMapLatest { [restRepo] in
restRepo!.getUnread()
.map { Result<[Model], Error>.success($0) }
.catchError { .just(Result<[Model], Error>.failure($0)) }
}
.share(replay: 1)
let dbResponse = serverResponse
.capture(case: Result.success)
.flatMapLatest { [dbRepo] models in
dbRepo!.save(models)
.map { Result<Void, Error>.success(()) }
.catchError { .just(Result<Void, Error>.failure($0)) }
}
serverResponse
.capture(case: Result.success)
.subscribe(onNext: { models in
print("display the data to the user.", models)
})
.disposed(by: disposeBag)
Observable.merge(serverResponse.capture(case: Result.failure), dbResponse.capture(case: Result.failure))
.subscribe(onNext: { error in
print("an error occured:", error)
})
.disposed(by: disposeBag)
}
}
protocol RestRepo {
func getUnread() -> Observable<[Model]>
}
protocol DBRepo {
func save(_ models: [Model]) -> Observable<Void>
}
struct ProductionRestRepo: RestRepo {
func getUnread() -> Observable<[Model]> {
let urlComponent = ApiHelper.instance.dolphinURLComponents(for: ApiHelper.ISSUES_PATH)
let urlRequest = URLRequest(url: urlComponent.url!)
return URLSession.shared.rx.data(request: urlRequest)
.map { try JSONDecoder().decode([Model].self, from: $0) }
}
}
class ApiHelper {
static let ISSUES_PATH = ""
static let instance = ApiHelper()
func dolphinURLComponents(for: String) -> URLComponents { fatalError() }
}
struct Model: Decodable { }
I hope all this helps you, or at least generates more questions.
I'm trying to understand the Combine methodology of making a JSON network call. I'm
clearly missing something basic.
The closest I get fails with the URLSession cancelled.
class NoteDataStore: ObservableObject {
#Published var notes: [MyNote] = []
init() {
getWebserviceNotes()
}
func getWebserviceNotes() {
let pub = Webservice().fetchNotes()
.sink(receiveCompletion: {_ in}, receiveValue: { (notes) in
self.notes = notes
})
}
}
}//class
The data element:
struct MyNote: Codable, Identifiable {
let id = UUID()
var title: String
var url: String
var thumbnailUrl: String
static var placeholder: MyNote {
return MyNote(title: "No Title", url: "", thumbnailUrl: "")
}
}
The network setup:
class Webservice {
func fetchNotes() -> AnyPublisher<[MyNote], Error> {
let url = "https://jsonplaceholder.typicode.com/photos"
guard let notesURL = URL(string: url) else { fatalError("The URL is broken")}
return URLSession.shared.dataTaskPublisher(for: notesURL)
.map { $0.data }
.decode(type: [MyNote].self, decoder: JSONDecoder())
.receive(on: RunLoop.main)
.eraseToAnyPublisher()
}
}
The console output is:
Task <85208F00-BC24-44AA-B644-E0398FE263A6>.<1> finished with error
[-999] Error Domain=NSURLErrorDomain Code=-999 "cancelled"
UserInfo={NSErrorFailingURLStringKey=https://jsonplaceholder.typicode.com/photos,
NSLocalizedDescription=cancelled,
NSErrorFailingURLKey=https://jsonplaceholder.typicode.com/photos}
Any guidance would be appreciated. Xcode 11.4
let pub = Webservice().fetchNotes()
this publisher is released on exit of scope, so make it member, like
private var publisher: AnyPublisher<[MyNote], Error>?
func getWebserviceNotes() {
self.publisher = Webservice().fetchNotes()
...
Based on Asperi's answer - you will also want to add:
var cancellable: AnyCancellable?
And then you can sink to get the data:
func getWebserviceNotes() {
self.publisher = Webservice().fetchNotes()
guard let pub = self.publisher else { return }
cancellable = pub
.sink(receiveCompletion: {_ in },
receiveValue: { (notes) in
self.notes = notes
})
}
New to RxSwift here. I have a (MVVM) view model that represents a Newsfeed-like page, what's the correct way to subscribe to change in data model's properties? In the following example, startUpdate() constantly updates post. The computed properties messageToDisplay and shouldShowHeart drives some UI event.
struct Post {
var iLiked: Bool
var likes: Int
...
}
class PostViewModel: NSObject {
private var post: Post
var messageToDisplay: String {
if post.iLiked { return ... }
else { return .... }
}
var shouldShowHeart: Bool {
return iLiked && likes > 10
}
func startUpdate() {
// network request and update post
}
...
}
It seems to me in order to make this whole thing reactive, I have to turn each properties of Post and all computed properties into Variable? It doesn't look quite right to me.
// Class NetworkRequest or any name
static func request(endpoint: String, query: [String: Any] = [:]) -> Observable<[String: Any]> {
do {
guard let url = URL(string: API)?.appendingPathComponent(endpoint) else {
throw EOError.invalidURL(endpoint)
}
return manager.rx.responseJSON(.get, url)
.map({ (response, json) -> [String: Any] in
guard let result = json as? [String: Any] else {
throw EOError.invalidJSON(url.absoluteString)
}
return result
})
} catch {
return Observable.empty()
}
}
static var posts: Observable<[Post]> = {
return NetworkRequest.request(endpoint: postEndpoint)
.map { data in
let posts = data["post"] as? [[String: Any]] ?? []
return posts
.flatMap(Post.init)
.sorted { $0.name < $1.name }
}
.shareReplay(1)
}()
class PostViewModel: NSObject {
let posts = Variable<[Post]>([])
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
posts
.asObservable()
.subscribe(onNext: { [weak self] _ in
DispatchQueue.main.async {
//self?.tableView?.reloadData() if you want to reload all tableview
self.tableView.insertRows(at: [IndexPath], with: UITableViewRowAnimation.none) // OR if you want to insert one or multiple rows.
//OR update UI
}
})
.disposed(by: disposeBag)
posts.asObservable()
.bind(to: tableView.rx.items) { [unowned self] (tableView: UITableView, index: Int, element: Posts) in
let cell = tableView.dequeueReusableCell(withIdentifier: "postCell") as! PostCell
//for example you need to update the view model and cell with textfield.. if you want to update the ui with a cell then use cell.button.tap{}. hope it works for you.
cell.textField.rx.text
.orEmpty.asObservable()
.bind(to: self.posts.value[index].name!)
.disposed(by: cell.disposeBag)
return cell
}
startDownload()
}
}
func startDownload {
posts.value = NetworkRequest.posts
}
If you want to change anything then use subscribe, bind, concat .. There are many methods and properties which you can use.