Home > database >  Swift. I pull out pictures in cells through api. Everything is being built, but instead of pictures
Swift. I pull out pictures in cells through api. Everything is being built, but instead of pictures

Time:06-24

I'm a beginner. I pull out pictures in cells through api. Everything is built, but instead of pictures - it's empty. Returns nil. I've been sitting here all day and can't figure it out!

API link - https://swiftbook.ru//wp-content/uploads/api/api_courses

If this answer is somewhere, I apologize, and if it's not difficult to give a link, send it please, thank you.

Thank you very much in advance for your help and clarification!!!

enter image description here

import UIKit

class CourseCell: UITableViewCell {
    @IBOutlet var courseImage: UIImageView!
    @IBOutlet var courseNameLabel: UILabel!
    @IBOutlet var numberOfLessons: UILabel!
    @IBOutlet var numberOfTests: UILabel!
    
    func configure(with course: Course) {
        
        courseNameLabel.text = course.name
        numberOfLessons.text = "Number of lessons \(course.number_of_lessons ?? 0)"
        numberOfTests.text = "Number of tests \(course.number_of_tests ?? 0)"
        
        DispatchQueue.global().async {
            
            guard let stringUrl = course.imageUrl,
                  let imageURL = URL(string: stringUrl),
                  let imageData = try? Data(contentsOf: imageURL)
            else {
                return
            }
            
            DispatchQueue.main.async {
                self.courseImage.image = UIImage(data: imageData)
            }      
   
        }   
    }
}

Model for decode by JSON

Course.swift

struct Course: Decodable {
    let name: String?
    let imageUrl: String?
    let number_of_lessons: Int?
    let number_of_tests: Int?
}

struct WebsiteDescription: Decodable {
    let courses: [Course]?
    let websiteDescription: String?
    let websiteName: String?
}

And piece of code with JSON from CoursesViewController.swift

extension CoursesViewController {
    func fetchCourses() {
        guard let url = URL(string: URLExamples.exampleTwo.rawValue) else { return }
        
        URLSession.shared.dataTask(with: url) { (data, _, _) in
            guard let data = data else {
                return
            }
            
            do {
                // получаем курсы в переменную
                self.courses = try JSONDecoder().decode([Course].self, from: data)
                // и мы должны перезагрузить таблицу
                DispatchQueue.main.async {
                    self.tableView.reloadData()
                }
            } catch let error {
                print(error)
            }
        }.resume()
    }
}

And here is i get nil probably (please see screenshot below)

enter image description here

CodePudding user response:

I tried to make another version of your code and it's able to run. You can check my code and compare with your own.

CoursesViewController

class CoursesViewController: UIViewController {
    private lazy var tableView: UITableView = {
        let tableView = UITableView(frame: .zero, style: .plain)
        tableView.translatesAutoresizingMaskIntoConstraints = false

        tableView.register(CourseCell.self, forCellReuseIdentifier: "CourseCell")

        tableView.delegate = self
        tableView.dataSource = self

        return tableView
    }()

    private var courses: [Course] = []

    override func viewDidLoad() {
        super.viewDidLoad()

        setupViews()
        setupLayout()

        fetchCourses()
    }

    private func setupViews() {
        view.addSubview(tableView)
    }

    private func setupLayout() {
        tableView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
    }

    private func fetchCourses() {
        guard let url = URL(string: "https://swiftbook.ru//wp-content/uploads/api/api_courses") else {
            return
        }

        URLSession.shared.dataTask(with: url) { (data, _, _) in
            guard let data = data else {
                return
            }

            do {
                // получаем курсы в переменную
                self.courses = try JSONDecoder().decode([Course].self, from: data)
                // и мы должны перезагрузить таблицу
                DispatchQueue.main.async {
                    self.tableView.reloadData()
                }
            } catch let error {
                print(error)
            }
        }.resume()
    }
}

extension CoursesViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        courses.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "CourseCell", for: indexPath) as? CourseCell else {
            return UITableViewCell()
        }

        cell.configure(with: courses[indexPath.row])

        return cell
    }

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

    func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        120
    }
}

Cell

class CourseCell: UITableViewCell {
    private lazy var nameLabel: UILabel = {
        let label = UILabel()

        label.numberOfLines = 0
        label.textColor = .black
        label.font = .systemFont(ofSize: 14, weight: .bold)

        return label
    }()

    private lazy var courseImage = UIImageView(frame: .zero)

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        setupViews()
        setupLayout()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func configure(with course: Course) {
        nameLabel.text = course.name

        DispatchQueue.global().async {
            guard let stringUrl = course.imageUrl,
                  let imageURL = URL(string: stringUrl),
                  let imageData = try? Data(contentsOf: imageURL)
            else {
                return
            }

            DispatchQueue.main.async {
                // Make sure it's the same course
                self.courseImage.image = UIImage(data: imageData)
            }
        }
    }

    private func setupViews() {
        courseImage.contentMode = .scaleAspectFill
        contentView.addSubview(nameLabel)
        contentView.addSubview(courseImage)
    }

    private func setupLayout() {
        nameLabel.snp.makeConstraints { make in
            make.top.leading.trailing.equalToSuperview().inset(8)
        }

        courseImage.snp.makeConstraints { make in
            make.centerX.equalToSuperview().inset(8)
            make.top.equalTo(nameLabel.snp.bottom).offset(12)
            make.height.width.equalTo(80)
        }
    }
}
  • In my opinion, you should check your UI layout to make sure that the image view can be loaded and displayed properly.

Some Improvement suggestion

  • Course.swift: Please use lower camel case convention for variables name because it's the common Swift convention
  • CourseCell.swift: Since the course don't have ID so after a while you load image from background, this cell might be used by another because of the reuse cell mechanism.
DispatchQueue.main.async {
    // Make sure it's the same course
    if course.id == self.course.id {
        self.courseImage.image = UIImage(data: imageData)
    }
}
  • Use caching mechanism every time you load image from the server so that next time you don't need to fetch from the server again (you can set timeout for cache)
  • Instead of handing loading image by yourself, you can use well-known 3rd party libs like SDWebImage or KingFisher.

CodePudding user response:

The answer above is Excellent !!! It's an additional valuable experience for me!

But main reason was in blocked .ru domains in my country. WHEN I ACCIDENTALLY TURNED ON THE VPN ON THE MAC AND BUILD APP, THEN EVERYTHING LOADED!!! Because I am in Ukraine now, and we have all .ru domains blocked, and the API URL is just on .ru

  • Related