Module 5 : Complete! (with Bug-fix)

Due to the way Xcode has been altered since the tutorial video of iOS Foundation’s Module 5 from CodeWithChris, the Learning App crashes when a certain sequence is performed.

  • Run the Learning App,
  • Tap “Swift Test”,
  • Select “let” as the answer, but do NOT click “Submit”,
  • Click “Get Started” to return to menu,
  • Click “Learn SwiftUI”.

The reason for this is that we’re using .onAppear to trigger when the chosen module has changed, but this doesn’t happen quickly enough for Xcode.

If, for instance, we’re viewing the lessons of a module with 10 entries and we go back to the main screen, and then we view the lessons of a module with 9 entries, the iOS simulator initiates listing the lesson entries for the module with 10 entries, after which .onAppear triggers and changes the module to the one with 9 entries. The simulator will still try to complete its iteration of the initial 10 lessons but now there’s only 9 entries. When it tries to list the 10th entry, there isn’t one any more so it produces an “index out of range” error and crashes.

It sounds complicated, but it is pretty logical. To fix this issue, Chris (with bug fixing efforts from friends), provides us with Lesson 15 of the module, which ultimately incorporates the following fixes.

In ContentModel, we add the following guard statement to top of the hasNextLesson() method:

guard currentModule != nil else { 
     return false
}

In ContentViewRow, we remove the following from var body: some View { ... }

let lesson = model.currentModule!.content.lessons[index]

and add the following before var body: some View { ... }

var lesson: Lesson {
    if model.currentModule != nil &&
        index < model.currentModule!.content.lessons.count {
        return model.currentModule!.content.lessons[index]
    }
    else {
        return Lesson(id: 0, title: "", video: "", duration: "", explanation: "")
    }
}

And finally, in HomeView, we add the following .onChange modifiers to the main VStack:

.onChange(of: model.currentContentSelected) { changedValue in
    if changedValue == nil {
        model.currentModule = nil
    }
}
.onChange(of: model.currentTestSelected) { changedValue in
    if changedValue == nil {
         model.currentModule = nil
    }
}

As always, I add these code snippets here in the hope they’ll one day make far more sense to me than they do now and I can look back on them to see where my learning began.