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

@alexz Do you still have issues with this?

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

This behavior drove me crazy on this and other beginner SwiftUI projects. The behavior that requires us to swipe the list before we can use the add (+) is still present even using the latest available toolchains obtained today; the May 8th trunk of Swift 5.2 and the May 4th build of Swift 5.3 Development.

However, the other day I happened to come across a similar project where the “Add” button was placed outside the navigation bar; similar to Jessy’s “Add Task” button on the iPad demonstration. If we place it there, then everything appears to work for me; at least with the iPhone 11 Pro Max simulator, iPad Pro simulator, and my physical iPhone 11. Here is the modified NavigationView (placed inside of ContentView.swift). You may or may not like the changes to look and feel, and I don’t know what impact this will have later on in this course – but give it a try and see if it works for you too.

        NavigationView {
            VStack(alignment: .leading) {
                List(taskStore.tasks) { task in
                    Text(task.name)
                }
                Button(action: { self.modalIsPresented = true }) {
                    HStack {
                        Image(systemName: "plus.circle.fill")
                            .resizable()
                            .frame(width: 20, height: 20)
                        Text("New Task")
                    }
                }
                .padding()
                .accentColor(Color(UIColor.systemRed))
            } // VStack
            .sheet(isPresented: $modalIsPresented) {
                NewTaskView(taskStore: self.taskStore)
            }
            .navigationBarTitle("Tasks")
        } // NavView
[details="Summary"]
This text will be hidden
[/details]

@kronos5807 Thank you for sharing your solution - much appreciated!

The bug where ‘+’ button becomes unresponsive after adding a task on modal view both on simulator and iPhone as well is till present as of iOS 13.5.1 Xcode 11.5. Someone pointed out in the previous video the workaround/solution for this is to add the line below in the ContentView as well.

@Environment(.presentationMode) var presentation

This works in simulator as well as device.

Could the instructor please explain how does this make it work? Thanks.

I’m just going through the course now interestingly when I tried to add observable object in ContentView.Swift
@ObservableObject var taskStore: TaskStore

I get unknown attribute ObservableObject

Did you maybe forget to import Combine?

Nope its clearly a bug because I copied the text from a completed project for just that line… and bingo it worked… Spelling and everything was the same!!! So very odd…

Thanks for the response… btw this part of the course scared the S@@T out of me… But I am persevering all the same :slight_smile:

1 Like

I noticed that the app clears every time it is restarted. Should the code that we worked through in this program save the data locally each time it is closed and relaunched?

This course does not feature any data persistence. Continue on to Saving Data in iOS, to add that feature!

The new version of Your Second iOS and SwiftUI App, which is in editing, does feature data persistence.