Complex use case for shared state and null exception

Hello Friends,

I have a complex use case but a real-world problem. To demonstrate that I started doing a whole project mapping and possibly can be added to the TCA example project in the future if it's good enough.

You will find the whole project here, download, and run!

What is it:

Consider an application that has different sections, and each section has it's own data set. Each of these data can present in different ways:

  • Horizontally
  • Vertically

Please check these images.

We can see that, and it's a shared state problem and different view asking data differently. So I decided to go with computed property. Please take a look at this code here.

Users can do toggle on each presentable item, and keep records that one user chooses. Now, if you are in a horizontal view and user toggle it, then you want to reflect that changes, and it will as it's computed property. Check these images:

Problems:

  • When I'm trying to toggle item from a vertical presentation, which is another level deep view, somehow the id is getting null, and an exception is thrown rightfully from the TCA IdentifiedArrayOf class. I'm just not getting why it's happening.

Fatal error: Can't update element with identifier 19F52639-D643-4C9C-932C-14D7DF81088E with nil. If you are trying to remove an element from the array, use the "remove(id:) method.": file

  • Maybe my strategy of presenting a vertical view isn't right. Do you have better suggestions to solve it? Right now, I cover two items per row and rows offered in a list. To do that, I had to do a three-level reducer or view breakdown. Here are reducers.

I'm still digging and trying to think differently, but if anyone has time to build and run, that will be helpful!

Thanks

The problem is that every time verticalState is calculated it produces new UUID when it constructs PresentableState. Your state is mutating every time you access the verticalState property.

I think this unit test is correct

func testVerticalStateIsIdempotent() throws {
		var state = CollectionState(collection: .one)
		state.items = [.init(title: "one")]
		state.isNavigationActive = true
		XCTAssertNotNil(state.verticalState)
		XCTAssertEqual(state.verticalState, state.verticalState)
    }

You are absolutely right. I changed the unnecessary UUID things. Here the changes.

Thanks a lot for pointing it. Right now I'm adding more unit tests to find issues like that! I still some concerns regarding the approach I took! To change the view presentation I had to do a lot of works which I'm not liking. Do you see any better approach @toddwbates?

Thanks again!

Also imagine that I have thousands of item and I need to do the mapping and calculation every-time which is heavy CPU intensive I don't think the main thread will be happy with that :frowning:

I don't know if I can give a better approach. I am just learning this style and find debugging a very useful learning tool, so thank you for the opportunity. One of the interesting finds I had was that the stack trace was less useful than what I am used to. It consisted of mostly frames from TCA. I do prefer that files be limited to 100 lines or so, but that is just my preference. Unit tests are a great idea and this framework has beautiful support for testing.

Yes, the stack trace has no value. Its' like RxSwift! Regarding file yes, I always separate it but I was doing POC. I'll break it down. Keep playing with that, I think there are lot of use cases you will find TCA hard to grasp or use easily. Like that vertical presentation for example, I just can't accept I had to do all of these juggling!!!

I hope there is better approach otherwise I'll be heartbroken!

@toddwbates I did better structure now! :stuck_out_tongue: