У меня есть этот JSON из API:
{
"oldest": "2019-01-24T00:00:00+00:00",
"activities": [
{
"message": "<strong>Henrik</strong> didn't resist a guilty pleasure at <strong>Starbucks</strong>.",
"amount": 2.5,
"userId": 2,
"timestamp": "2019-05-23T00:00:00+00:00"
},
{
"message": "<strong>You</strong> made a manual transfer.",
"amount": 10,
"userId": 1,
"timestamp": "2019-01-24T00:00:00+00:00"
}
]
}
У него намного больше мероприятий. Как я могу получить к нему доступ и заполнить свои ячейки его данными? Пока у меня есть этот код:
MainViewController:
struct Activities: Decodable {
var oldest: String
var activities: [Activity]
}
struct Activity: Decodable {
var message: String
var amount: Float
var userId: Int
var timestamp: String
}
class MainTableViewController: UITableViewController, UITableViewDataSourcePrefetching {
var activityList: [Activities] = []
var activity: [Activity] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.prefetchDataSource = self
let activitiesJSONURLString = "https://qapital-ios-testtask.herokuapp.com/activities?from=2016-05-23T00:00:00+00:00&to=2019-05-23T00:00:00+00:00"
guard let activitiesURL = URL(string: activitiesJSONURLString) else { return }
URLSession.shared.dataTask(with: activitiesURL) { (data, response, err) in
// perhaps check err
// also perhaps check response status 200 OK
guard let data = data else { return }
do {
// Activities
let activities = try JSONDecoder().decode(Activities.self, from: data)
} catch let jsonErr {
print("Error serializing json: ", jsonErr)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}.resume()
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return activityList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ActivityCell", for: indexPath) as! MainTableViewCell
return cell
}
// Prefetching
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
// if indexPaths.contains(where: isLoadingCell) {
// viewModel.fetchModerators()
// }
}
}
Но мне кажется, что-то не так. Или я понятия не имею, как начать. Я действительно мог бы использовать некоторую помощь или любые советы, которые вы можете мне дать. Пожалуйста и спасибо вам!
Спасибо! Переместил reloadData в блок Do





Поскольку вы используете activityList для определения количества строк, я предполагаю, что вы хотите использовать данные из activityList для заполнения ваших ActivityCells. То есть, если вы не хотели, чтобы ActivityList был одним экземпляром Activities вместо массива Activities, и в этом случае вы, вероятно, использовали бы activityList.activities.count для определения количества строк. В любом случае давайте просто вызовем массив данных, которые вы хотите использовать для заполнения ячеек activityList.
В этом случае вы должны обязательно обновить activityList действия, которые вы получили из API. Когда у вас есть activityList, вы можете использовать reloadData, который вызовет ваши методы делегата табличного представления. Затем в tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) вы можете использовать activityList, чтобы обновить ячейку, удаленную из очереди.
Что-то вроде этого может быть тем, что вы хотите:
class MainTableViewController: UITableViewController, UITableViewDataSourcePrefetching {
var activityList: Activities!
var activity: [Activity] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.prefetchDataSource = self
let activitiesJSONURLString = "https://qapital-ios-testtask.herokuapp.com/activities?from=2016-05-23T00:00:00+00:00&to=2019-05-23T00:00:00+00:00"
guard let activitiesURL = URL(string: activitiesJSONURLString) else { return }
URLSession.shared.dataTask(with: activitiesURL) { (data, response, err) in
// perhaps check err
// also perhaps check response status 200 OK
guard let data = data else { return }
do {
// Activities
let activities = try JSONDecoder().decode(Activities.self, from: data)
self.activityList = activities
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch let jsonErr {
print("Error serializing json: ", jsonErr)
}
}.resume()
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return activityList.activities.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ActivityCell", for: indexPath) as! MainTableViewCell
if let activity = activityList?[indexPath.row] {
// UPDATE CELL ACCORDING TO activity
}
return cell
}
}
Привет! Большое спасибо за ваш ответ. Но на линии let activityForCell = activityList[indexPath.row] получаю Value of optional type 'Activities?' must be unwrapped to refer to member 'subscript' of wrapped base type 'Activities'
Во-первых, названия структур довольно запутаны. Назовите корневой объект чем-то несвязанным, например Response или Root.
И мы собираемся расшифровать временные метки как Date
struct Root: Decodable {
var oldest: Date
var activities: [Activity]
}
struct Activity: Decodable {
var message: String
var amount: Float
var userId: Int
var timestamp: Date
}
Во-вторых, поскольку данные получены во всем, соответствие UITableViewDataSourcePrefetching бессмысленно. Удалите его, а также удалите метод prefetchRowsAt.
Объявите только один массив источников данных и назовите его activities
var activities = [Activity]()
и удалить
var activityList: Activities!
В обработчике завершения задачи данных расшифруйте Root и назначьте массив действий массиву источника данных.
do {
// Activities
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let result = try decoder.decode(Root.self, from: data)
self.activities = result.activities
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print("Error serializing json: ", error)
}
Методы источника данных табличного представления:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return activities.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ActivityCell", for: indexPath) as! MainTableViewCell
let activity = activities[indexPath.row]
// assign the activity data to the UI for example
// cell.someLabel = activity.amount
return cell
}
Привет! Большое спасибо, что нашли время ответить. Пара вещей: 1) Мне действительно нужна предварительная выборка, я просто поместил ее туда, чтобы сделать это позже. 2) var message: Date должно быть String, но я думаю, что это была просто ошибка. 3) Как вы можете видеть, в сообщении действия есть некоторый HTML (<strong></strong>), поэтому, если я использую cell.descriptionLabel.text = activity.message, я получаю это, должен ли я использовать NSAttributedString или что-то в этом роде? Еще раз большое спасибо за вашу помощь!
2) Да, это была ошибка. 3) Если вы хотите рассмотреть формат HTML, вы должны преобразовать его в NSAttributedString. Либо добавьте дополнительную структуру синглКонтейнер, либо напишите собственный инициализатор.
не относится к вашему вопросу, но вам следует перезагружать данные табличного представления только в случае успеха декодера.