Я могу распечатать данные, но не могу назначить их метке в Swift

Я отправил свои данные из моего вызова API в свой InfoController viewDidLoad. Там я смог безопасно сохранить его в константе skillName, а также распечатать, получив всю информацию с консоли.

Проблема возникает, когда я пытаюсь назначить эту переменную своему skillLabel.

override func viewDidLoad() {
    super.viewDidLoad()
    configureViewComponents()
    fetchPokemons { (names) in
        guard var skillName = names as? String else { return }
        self.pokemon?.skillName = skillName
        
        self.allNames = skillName
        print(self.allNames)
    }
}

Там, когда я печатаю allNames, консоль показывает все нужные мне данные. Вот как выглядят данные: Data Example

И вычисляемое свойство, в котором я хочу использовать эти данные, выглядит так:

var pokemon: Pokemon? {
    didSet {
        guard let id        = pokemon?.id else { return }
        guard let data      = pokemon?.image else { return }

        
        navigationItem.title = pokemon?.name?.capitalized
        infoLabel.text = pokemon?.description
        infoView.pokemon = pokemon
        
        if id == pokemon?.id {
            imageView.image = UIImage(data: data)
            infoView.configureLabel(label: infoView.skillLabel, title: "Skills", details: "(allNames)")
        }
    }
}

PD: allNames - это строковая переменная, которая у меня есть на уровне класса InfoController.

Вот как мое приложение выглядит при запуске: PokeApp

Моя цель - получить этот параметр подробностей, чтобы показать данные skillName, но он возвращает ноль, понятно почему. Есть совет?

EDIT1: Моя функция, которая извлекает данные Pokemon из моего класса обслуживания, следующая:

func fetchPokemons(handler: @escaping (String) -> Void) {
    controller.service.fetchPokes { (poke) in
        DispatchQueue.main.async {
            self.pokemon? = poke
            
            guard let skills = poke.abilities else { return }
            
            for skill in skills {
                
                guard let ability = skill.ability else { return }
                
                guard var names = ability.name!.capitalized as? String else { return }
                
                self.pokemon?.skillName = names
                handler(names)
            }
        }
    }
}

EDIT2: Класс InfoView выглядит так:

class InfoView: UIView {

// MARK: - Properties
var delegate: InfoViewDelegate?

//  This whole block assigns the attributes that will be shown at the InfoView pop-up
//  It makes the positioning of every element possible
var pokemon: Pokemon? {
    didSet {
        guard let pokemon   = self.pokemon else { return }
        guard let type      = pokemon.type else { return }
        guard let defense   = pokemon.defense else { return }
        guard let attack    = pokemon.attack else { return }
        guard let id        = pokemon.id else { return }
        guard let height    = pokemon.height else { return }
        guard let weight    = pokemon.weight else { return }
        guard let data      = pokemon.image else { return }
        
        if id == pokemon.id {
            imageView.image = UIImage(data: data)
        }
        nameLabel.text = pokemon.name?.capitalized
        
        configureLabel(label: typeLabel, title: "Type", details: type)
        configureLabel(label: pokedexIdLabel, title: "Pokedex Id", details: "(id)")
        configureLabel(label: heightLabel, title: "Height", details: "(height)")
        configureLabel(label: defenseLabel, title: "Defense", details: "(defense)")
        configureLabel(label: weightLabel, title: "Weight", details: "(weight)")
        configureLabel(label: attackLabel, title: "Base Attack", details: "(attack)")
    }
}

let skillLabel: UILabel = {
    let label = UILabel()
    return label
}()

let imageView: UIImageView = {
    let iv = UIImageView()
    iv.contentMode = .scaleAspectFill
    return iv
}()
. . .
}

infoView.configureLabel это:

func configureLabel(label: UILabel, title: String, details: String) {
    let attributedText = NSMutableAttributedString(attributedString: NSAttributedString(string: "(title):  ", attributes: [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 16), NSAttributedString.Key.foregroundColor: Colors.softRed!]))
    attributedText.append(NSAttributedString(string: "(details)", attributes: [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16), NSAttributedString.Key.foregroundColor: UIColor.gray]))
    label.attributedText = attributedText
}

РЕДАКТИРОВАТЬ 3: Дизайн структур

struct Pokemon: Codable {
    var results: [Species]?
    var abilities: [Ability]?
    var id, attack, defense: Int?
    var name, type: String?
...
}

struct Ability: Codable {
    let ability: Species?
}

struct Species: Codable {
    let name: String?
    let url: String?
}

Всего 1 ответ

Автоматический диспенсер мыльной пены от xiaomi.


Перейдите к абзацу Edit2, чтобы получить окончательный ответ!

Первый ответ:

Похоже, ваш пользовательский интерфейс не обновляется после того, как контроллер извлекает все данные.

Поскольку весь код конфигурации пользовательского интерфейса находится внутри var pokemon / didSet, рекомендуется выделить его в отдельный метод.

private func updateView(with pokemon: Pokemon?, details: String?) {
    guard let id = pokemon?.id, let data = pokemon?.image else { return }

    navigationItem.title = pokemon?.name?.capitalized
    infoLabel.text = pokemon?.description
    infoView.pokemon = pokemon

    if id == pokemon?.id {
        imageView.image = UIImage(data: data)
        infoView.configureLabel(label: infoView.skillLabel, title: "Skills", details: details ?? "")
    }
}

и теперь вы можете легко позвонить в didSet

var pokemon: Pokemon? {
    didSet { updateView(with: pokemon, details: allNames) }
}

и fetchPokemons завершение, а также

override func viewDidLoad() {
    super.viewDidLoad()
    configureViewComponents()
    fetchPokemons { (names) in
        guard var skillName = names as? String else { return }
        self.pokemon?.skillName = skillName

        self.allNames = skillName
        print(self.allNames)
        DispatchQueue.main.async {
            self.updateView(with: self.pokemon, details: self.allNames)
        }
    }
}

Очень важно выполнить настройку пользовательского интерфейса в основной очереди.

Изменить:

Проблема может быть вызвана функцией выборки! вы вызываете обработчик несколько раз:

func fetchPokemons(handler: @escaping (String) -> Void) {
    controller.service.fetchPokes { (poke) in
        DispatchQueue.main.async {
            self.pokemon? = poke
            guard let skills = poke.abilities else { return }
            let names = skills.compactMap { $0.ability?.name?.capitalized }.joined(separator: ", ")
            handler(names)
        }
    }
}

Edit2:

Посмотрев на свою кодовую базу, вам нужно изменить пару вещей:

1. fetchPokemons реализация

обработчик controller.service.fetchPokes вызывается для каждого покемона, поэтому нам нужно проверить, является ли выбранный покемон текущим (self.pokemon), а затем вызвать handler с должным образом сформированными навыками.

func fetchPokemons(handler: @escaping (String) -> Void) {
    controller.service.fetchPokes { (poke) in
        guard poke.id == self.pokemon?.id else { return }
        self.pokemon? = poke
        let names = poke.abilities?.compactMap { $0.ability?.name?.capitalized }.joined(separator: ", ")
        handler(names ?? "-")
    }
}

2. обновить viewDidLoad()

теперь просто передайте метке значение names.

override func viewDidLoad() {
    super.viewDidLoad()
    configureViewComponents()
    fetchPokemons { (names) in
        self.pokemon?.skillName = names
        self.infoView.configureLabel(label: self.infoView.skillLabel, title: "Skills", details: names)
    }
}

3. Рефакторинг var pokemon: Pokemon? наблюдатель didSet

var pokemon: Pokemon? {
    didSet {
        guard let pokemon = pokemon, let data = pokemon.image else { return }
        navigationItem.title = pokemon.name?.capitalized
        infoLabel.text = pokemon.description!
        infoView.pokemon = pokemon
        imageView.image = UIImage(data: data)
    }
}

Есть идеи?

10000