NEW: Start my new Ultimate Portfolio App course with a free Hacking with Swift+ trial! >>

SOLVED: Question: Project9 -> challenge 2

Forums > 100 Days of Swift

This challenge is to modify project 8 to involves GCD.

The problem is when I put background queue directly inside function itself, then cluesString, answers label are empty when app is launched. Also letter buttons are default valued "WWW".

func loadLevel() {
      var clueString = ""
      var solutionString = ""
      var letterBits = [String]() .userInitiated).async { [weak self] in
        // code to load and parse the level.


      // Project9 challenge 2
      DispatchQueue.main.async { [weak self] in
          // Configure buttons and labels
          self?.cluesLabel.text = clueString.trimmingCharacters(in: .whitespacesAndNewlines)
          self?.answersLabel.text = solutionString.trimmingCharacters(in: .whitespacesAndNewlines)

          if letterBits.count == self?.letterButtons.count {
              if let letterButtons = self?.letterButtons {
                  for i in 0..<letterButtons.count {
                      self?.letterButtons[i].setTitle(letterBits[i], for: .normal)

If I put loadLevel() in background queue from viewDidLoad(), it works fine. So what is the different of this two way?

    override func viewDidLoad() {
        super.viewDidLoad() .userInitiated).async { [weak self] in


I found the issue, it is not related with GCD actually. This issue is in line Bundle.main.url(forResource: "level\(self?.level)", which produces a String interpolation warning. And result resource load get nil I guess.

As I used weak reference [weak self] as capture list here, I need to put self? before the global variable level in case to use it in closure. If I give it a default value like \(self?.level ?? 0), then this issue is fixed.

But is it that the property way to deal with this String interpolation here? Or some better approach should be involved here?

override func viewDidLoad() {

      // 1. call loadLevel

    func loadLevel() {
      var clueString = ""
      var solutionString = ""
      var letterBits = [String]() { [weak self] in
        if let levelFileURL = Bundle.main.url(forResource: "level\(self?.level)", withExtension: "txt") {
            if let levelContents = try? String(contentsOf: levelFileURL) {
                var lines = levelContents.components(separatedBy: "\n")
                self?.correctGuess = 0

                for (index, line) in lines.enumerated() {
                    let parts = line.components(separatedBy: ": ")
                    let answer = parts[0]
                    let clue = parts[1]

                    clueString += "\(index + 1). \(clue)\n"

                    let solutionWord = answer.replacingOccurrences(of: "|", with: "")
                    solutionString += "\(solutionWord.count) letters\n"

                    let bits = answer.components(separatedBy: "|")
                    letterBits += bits

        DispatchQueue.main.async {
          // 3. push UI code back to main thread


Hacking with Swift is sponsored by RevenueCat

SPONSORED Building and maintaining in-app subscription infrastructure is hard. Luckily there's a better way. With RevenueCat, you can implement subscriptions for your app in hours, not months, so you can get back to building your app.

Try it for free

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

Unknown user

You are not logged in

Log in or create account

Link copied to your pasteboard.