Delay updating label text SwiftPlaygrounds

Hello everybody,


I am having trouble in updating the text of a label. This only happens when I am about to call another function which is very strange.


Here is my code:


func updateWordLabelAndCheckVictory(letter: String, indexArray: [Int]) {
        
        if let currentLabel = wordLabel.text {
            
            var wordArray = Array(currentLabel)
            
            for i in indexArray {
                wordArray[i] = Character(letter)
            }
            
            wordLabel.text = String(wordArray)
                
            if (game.checkVictory(label: wordLabel.text!)) {
                currentGame += 1
                presentVictoryView()
            }
        }
    }


I update the label in line 11. When the condition in line 13 is met the presentVictoryView method (line 15) executes before the label is updated... I don't really know what I am doing wrong.


If I comment line 15 and don't call the presentVictoryView method the label is updated as usual...


Could you please help me?


Thank you.

Please, provide the needed information.

How is game defined ?

What is game.checkVictory doing ?

What is presentVictoryView doing ?


What I would try (but need to see more code to be sure): dispatch in another queue


            if (game.checkVictory(label: wordLabel.text!)) {
                currentGame += 1
                DispatchQueue.global().async {
                    presentVictoryView()
                }
            }
        }

Of course, if presentVictoryView calls UI func, they must be executed in main thread


You posted another question

https://forums.developer.apple.com/thread/132652


Is it the same presentVictoryView ?

Hello,


I have tried the dispatch queue. I don't know if I did that right.


Game is a public classe that is in another source file in the playground. And checkVictory is a method of that classe.


import Foundation
import UIKit

public class Game {
    
    public var word: String
    public var clue: String
    public var numberOfTriesLeft = 6
    
    public init(word: String, clue: String) {
        self.word = word
        self.clue = clue
    }
    
    public func checkLetterInWord(letter: String) -> [Int] {
        
        let wordCharacters = Array(word)
        
        var indexesInWord: [Int] = []
        var i = 0
        
        for char in wordCharacters {
            //print("\(char) and \(letter)")
            if char == Character(letter) {
                indexesInWord.append(i)
            }
            i += 1
        }
        
        return indexesInWord
    }
    
    public func checkVictory(label: String) -> Bool {
        
        for char in label {
            if char == "?" {
                return false
            }
        }
        
        return true
    }
    
    
}


The function updateWordLabelAndCheckVictory calls presentVictoryView method.


This is how I am using queues:


func updateWordLabelAndCheckVictory(letter: String, indexArray: [Int]) {
        
        if let currentLabel = wordLabel.text {
            
            var wordArray = Array(currentLabel)
            
            for i in indexArray {
                wordArray[i] = Character(letter)
            }
            
            DispatchQueue.main.async { [weak self] in
                self?.wordLabel.text = String(wordArray)
            }
                
            if (game.checkVictory(label: wordLabel.text!)) {
                print("Aqui")
                currentGame += 1
                
                DispatchQueue.main.async { [weak self] in
                    self?.presentVictoryView()
                }
            }
        }
    }


func presentVictoryView() {
        
        DispatchQueue.main.async { [weak self ] in
            self?.blackView.isHidden = false
                    
            for _ in 0 ... 30 {
                
                let objectView = UIView()
                objectView.translatesAutoresizingMaskIntoConstraints = false
                objectView.frame = CGRect(x: 0, y: 0, width: 20, height: 100)
                objectView.backgroundColor = .clear
                //objectView.alpha = CGFloat(0.9)
                objectView.isHidden = false
                
                let ballon = UILabel()
                ballon.translatesAutoresizingMaskIntoConstraints = false
                ballon.frame = CGRect(x: 0, y: 0, width: 20, height: 100)
                ballon.backgroundColor = .clear
                //ballon.alpha = CGFloat(0.9)
                ballon.text = ""
                ballon.font = UIFont.systemFont(ofSize: 60)
                objectView.addSubview(ballon)
                
                
                NSLayoutConstraint.activate([
                    ballon.centerXAnchor.constraint(equalTo: objectView.centerXAnchor),
                    ballon.centerYAnchor.constraint(equalTo: objectView.centerYAnchor)
                ])
                
                self?.blackView.addSubview(objectView)
                
                let randomXOffset = Int.random(in: -120 ..< 200)
                
                let path = UIBezierPath()
                path.move(to: CGPoint(x: 160 + randomXOffset, y: 1000))
                path.addCurve(to: CGPoint(x: 100 + randomXOffset, y: -300), controlPoint1: CGPoint(x: 300 - randomXOffset, y: 600), controlPoint2: CGPoint(x: 70 + randomXOffset, y: 300))
                
                let animation = CAKeyframeAnimation(keyPath: "position")
                animation.path = path.cgPath
                animation.repeatCount = 1
                
                let random = Double.random(in: 3.0 ..< 7.0)
                animation.duration = random
                //animation.timeOffset = Double(arc4random_uniform(50))
                
                objectView.layer.add(animation, forKey: "animate position along path")
                
            }

            self?.newGame()
        }
    }


Could you tell me how I should use the queues?

That's nearly this.


But you should dispatch to another thread.

You are already in the main thread.

Dispathicng to it does nothing.

Think of dispatch as selecting a railway turnout. You go to another track and let trains on the main track pass over. So, if you remain on same track, no effect !


DispatchQueue.global().async {


Try this:

func updateWordLabelAndCheckVictory(letter: String, indexArray: [Int]) {
      
        if let currentLabel = wordLabel.text {
            var wordArray = Array(currentLabel)
            for i in indexArray {
                wordArray[i] = Character(letter)
            }
          
            // NOT NEEDED DispatchQueue.main.async { [weak self] in
                self?.wordLabel.text = String(wordArray)
           //  }
              
            if (game.checkVictory(label: wordLabel.text!)) {
                print("Aqui")
                currentGame += 1
              
                // CHANGE THIS DispatchQueue.main.async { [weak self] in
                DispatchQueue.global().async { [weak self] in
                    self?.presentVictoryView()     // You return to main in presentVictoryView
                }
            }
        }
    }

Hello!


I tried an using global() crashes the playground...


With main it works.

Delay updating label text SwiftPlaygrounds
 
 
Q