Group Group Group Group Group Group Group Group Group

Your Second iOS and SwiftUI App · Observable Objects | raywenderlich.com


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/5662524-your-second-ios-and-swiftui-app/lessons/13
1 Like

The final example code download for the “Observable Object” lesson seems to have a bug (at least on my machine, I am running Xcode 11.1). When you press the + button and add a new task, when the modal is dismissed the + button becomes unresponsive and you can’t display the new task modal dialog again. I noticed in the next lesson “Deleting From List” final download code, it has the same problem BUT if you try to delete a task (just initiate the delete but don’t commit it), the + button becomes functional again. I wonder if this is a bug in Xcode 11.1.

2 Likes

Thanks for reporting this!

This bug is new to me; I didn’t see it in the Xcode 11.0-11.1 beta lifecycle. But SwiftUI has been notorious for introducing new bugs with every new Xcode release. :crying_cat_face: And it’s still occurring in the Xcode 11.2 / iOS 13.2 beta. I’ll report it to Apple and update the materials for these episodes if necessary.

1 Like

Thank you. I just wanted to report the bug is still happening as of November 20th in XCode Version 11.2.1 (11B500)/iOS 13.2. I hope it gets fixed soon!

2 Likes

Thought I’d add that I was able to make the + button work by dragging in the window so that the Tasks heading disappeared off the top of the screen. When I let go the list slid back into place and the button was clickable again.

I actually found a workaround which avoid using enviroment by setting the modalIsPresented binded with NewTaskView

Edit the original code as following:

Step 1: Make sure modalIspresented is a state variable.

Step 2: Pass this state variable to NewTaskView

NewTaskView(taskStore: self.taskStore, isPrensented: self.$modalIsPresented)

Step 3: Add property of this binding in NewTaskView

@Binding var isPrensented:Bool

Step 4: Replace the action of “Add” button to the following:

{
   self.taskStore.tasks.append(Task(name: self.text))
   self.isPrensented = false
}

Step 5: Delete unnecessary parts related with @Enviroment

Step 6: Preview may get crazy and can be fixed with

struct NewTaskView_Previews: PreviewProvider {
    static var previews: some View {
        NewTaskView(taskStore: TaskStore(), isPrensented: .constant(true))
    }
}

Result:

This workaround on my iphone 8 ios13.2 is bugless and works fine.

However, in simulator, this version and the originial version both suffer from having to drag the screen down a little bit to click the + button again.

The original version which I downloaded failed on my iphone 8 too.

I guess something is wrong with the simulator and the @Enviroment.

PS: I later learned the original dissmiss method is originally a simplification such that we do not need to pass the binding by ourselves. I hope it get fixed quickly.

1 Like

Yep, that worked for me. Thanks. So weird though.

@lywang Thank you for sharing your solution - much appreciated! :]

The error mentioned is still working. You are able to add a task. When you try to add the second one you get this error message:

Can’t end BackgroundTask: no background task exists with identifier 2 (0x2), or it may have already been ended. Break in.

Please fix asap.

I had the same problem in latest iOS-13.4 and Xcode-11.4. I tried many many things but the bug still appearing. Finally I solved the bug by just putting both views in the same file (ContentView and TaskView).
But I guess it breaks the mvc :frowning:
Here is my code in case someone find it usefull.

struct CoolTaskView: View {
    var myTaskStore : TaskStore
    @Binding var isPrensented : Bool
    @State var text : String = ""
    
    var body: some View {
        Form {
            TextField("Task name", text: $text)
            Button("Add"){
                self.myTaskStore.tasks.append(Task(name: self.text))
                //self.presentationMode.wrappedValue.dismiss()
                print(self.$text)
                self.isPrensented.toggle()
                print(self.myTaskStore.tasks)
            }
            .disabled(text.isEmpty)
        }
    }
}

struct ContentView: View {
    
    @ObservedObject var myTaskStore : TaskStore
    @State var modalIsPresented : Bool = false
    
    var body: some View {
        
        NavigationView {
            List{
                ForEach(myTaskStore.tasks) { task in
                Text(task.name)
                }
                .onMove{ sourceIndices, destinationIndex in
                    self.myTaskStore.tasks.move(fromOffsets: sourceIndices, toOffset: destinationIndex)
                }
                .onDelete{ indexSet in
                    self.myTaskStore.tasks.remove(atOffsets : indexSet)
                }
            }//list closure
                .navigationBarTitle("Tasks", displayMode: .inline)
                .navigationBarItems(
                    leading : EditButton(),
                    trailing:
                    Button(action: {
                        print("Add button has been pressed")
                        self.modalIsPresented.toggle()
                    }) {
                        Image(systemName: "plus")
                    }
            )
            .sheet(isPresented: $modalIsPresented) {
                CoolTaskView(myTaskStore: self.myTaskStore, isPrensented : self.$modalIsPresented)
            }
        }// NavView
    } // some View
}// ContentView : View