pointfreeco / swift-identified-collections

A library of data structures for working with collections of identifiable elements in an ergonomic, performant way.
MIT License
531 stars 45 forks source link

Getting "Swift/ContiguousArrayBuffer.swift:600: Fatal error: Index out of range" error when trying to nil a state #42

Closed olcayertas closed 1 year ago

olcayertas commented 1 year ago

Hi I have following case and state and I am trying to clear a state when user dismiss a view controller:

var userProfileStates: IdentifiedArrayOf<User.State> = .init()

case let .clearStates(id):
    if !id.isEmpty, state.userProfileStates[id: id] != nil {

        // try to remove by getting a mutable compy of array
        var states = state.userProfileStates
        states[id: id] = nil
        state.userProfileStates = states //This crashes

        // Try to remove by ID
        state.userProfileStates[id: id] = nil //Also crashes

        // Try to remove by remove
        state.userProfileStates.remove(id: id) //Also crashes

         // Try to remove by removeAll
        state.userProfileStates.removeAll { $0.id == id } //Also crashes
    }
    state.addedUserState = nil
    state.followingState = nil
    state.followerState = nil
    return .none

But I am getting this error with all four of the approaches above:

Swift/ContiguousArrayBuffer.swift:600: Fatal error: Index out of range

These are the steps I produce the error:

viewStore .publisher .addedUserState .sink { [weak self] state in self?.handleNewUserStore(state?.id ?? "") } .store(in: &cancellables) ...

internal func handleNewUserStore(_ id: String) { guard let nc = getNavController() else { return } guard let index = viewStore.userProfileStates.firstIndex (where: { $0.id == id }) else { return } showUserProfile( nc, store: store.scope( state: .userProfileStates[index], action: { .userProfileAction(id: id, action: $0) } ) ) }

...

internal func showUserProfile( _ navController: UINavigationController?, store: UserStore ) { guard let parent = viewStore.parentViewController else { return } let view = UserProfileView( store: store, parent: parent, delegate: self ) let controller = UIHostingController(rootView: view) controller.hidesBottomBarWhenPushed = false navController?.pushViewController(controller, animated: true) navController?.setNavigationBarHidden(true, animated: true) }

- Tap back button on UserProfileView and send the user state id with the back button delegate method to clear out the state

If i set a break point before trying to nil the state I can see that the particular state is exist:

(lldb) po state.userProfileStates

▿ 1 element ▿ 0 : State

Lib version: 0.5.0

UPDATE: Sending the clear action with a delay partially solved my problem:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
    self?.viewStore.send(.clearStates(id))
}

This trick solves the problem when user taps to back button and clears out the particular state.

But if I push multiple user screens and tap a different tab bar item, all the sub view controllers are dismissed by tab bar and I should remove all the child states. For this one app still crashes.

Here is the stack trace for the case where user tapped to a tab bar item:

Thread 1 Queue : com.apple.main-thread (serial)

#0  0x000000018bfa78e0 in _swift_runtime_on_report ()
#1  0x000000018c034580 in _swift_stdlib_reportFatalErrorInFile ()
#2  0x000000018bce2e10 in closure #1 in closure #1 in closure #1 in _assertionFailure(_:_:file:line:flags:) ()
#3  0x000000018bce2b78 in closure #1 in closure #1 in _assertionFailure(_:_:file:line:flags:) ()
#4  0x000000018bce2564 in _assertionFailure(_:_:file:line:flags:) ()
#5  0x000000018bd23ca0 in ContiguousArray.subscript.getter ()
#6  0x0000000103a7833c in OrderedDictionary.Values.subscript.getter at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-collections/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Values.swift:284
#7  0x0000000103a52324 in IdentifiedArray.subscript.read at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-identified-collections/Sources/IdentifiedCollections/IdentifiedArray/IdentifiedArray+MutableCollection.swift:5
#8  0x0000000103bed6b4 in key path getter for IdentifiedArray.subscript(_:) : IdentifiedArray<String, User.State>StringUser.State ()
#9  0x000000018bdcddcc in RawKeyPathComponent._projectReadOnly<τ_0_0, τ_0_1, τ_0_2>(_:to:endingWith:) ()
#10 0x000000018bdcd8d4 in closure #1 in KeyPath._projectReadOnly(from:) ()
#11 0x000000018bdd1040 in swift_getAtKeyPath ()
#12 0x0000000103bed538 in implicit closure #2 in implicit closure #1 in MainTabBarViewController.handleNewUserStore(_:) ()
#13 0x0000000103bed7e0 in partial apply for implicit closure #2 in implicit closure #1 in MainTabBarViewController.handleNewUserStore(_:) ()
#14 0x00000001038cf00c in closure #3 in ScopedReducer.rescope<τ_0_0, τ_0_1, τ_0_2, τ_0_3>(_:state:action:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/Store.swift:650
#15 0x00000001038cf49c in partial apply for closure #3 in ScopedReducer.rescope<τ_0_0, τ_0_1, τ_0_2, τ_0_3>(_:state:action:) ()
#16 0x000000019b33d8e4 in Subscribers.Sink.receive(_:) ()
#17 0x000000019b33df04 in protocol witness for Subscriber.receive(_:) in conformance Subscribers.Sink<τ_0_0, τ_0_1> ()
#18 0x000000019b3f4b4c in Publishers.Drop.Inner.receive(_:) ()
#19 0x000000019b3f4e74 in protocol witness for Subscriber.receive(_:) in conformance Publishers.Drop<τ_0_0>.Inner<τ_1_0> ()
#20 0x000000019b3ab418 in CurrentValueSubject.Conduit.offer(_:) ()
#21 0x000000019b3ac250 in partial apply for closure #1 in CurrentValueSubject.send(_:) ()
#22 0x000000019b38070c in partial apply for thunk for @callee_guaranteed (@guaranteed ConduitBase<τ_0_0, τ_0_1>) -> (@error @owned Error) ()
#23 0x000000018bd68588 in Sequence.forEach(_:) ()
#24 0x000000019b380124 in ConduitList.forEach(_:) ()
#25 0x000000019b3aa65c in CurrentValueSubject.send(_:) ()
#26 0x000000019b3ac214 in specialized CurrentValueSubject.value.setter ()
#27 0x000000019b3aa550 in CurrentValueSubject.value.setter ()
#28 0x00000001038c8ca8 in $defer #1 <τ_0_0, τ_0_1>() in Store.send(_:originatingFrom:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/Store.swift:336
#29 0x00000001038c8a0c in Store.send(_:originatingFrom:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/Store.swift:332
#30 0x000000010399e358 in closure #1 in ViewStore.init(_:removeDuplicates:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/ViewStore.swift:203
#31 0x000000010399ea78 in ViewStore.send(_:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/ViewStore.swift:281
#32 0x0000000103bd3e00 in MainTabBarView.didSelectItem(at:) at /Users/olcayertas/popcorn-ios/Packages/MainTabBar/Sources/MainTabBar/MainTabBarView.swift:104
#33 0x0000000103befb28 in MainTabBarViewController.tabBar(_:didSelect:) at /Users/olcayertas/popcorn-ios/Packages/MainTabBar/Sources/MainTabBar/MainTabBarViewController.swift:168
#34 0x0000000103befd40 in @objc MainTabBarViewController.tabBar(_:didSelect:) ()
#35 0x000000011f3ff51c in -[UITabBar _sendAction:withEvent:] ()
#36 0x000000011fccd920 in -[UIApplication sendAction:to:from:forEvent:] ()
#37 0x000000011f6879cc in -[UIControl sendAction:to:forEvent:] ()
#38 0x000000011f687d10 in -[UIControl _sendActionsForEvents:withEvent:] ()
#39 0x000000011f401d1c in -[UITabBar _buttonUp:] ()
#40 0x000000011fccd920 in -[UIApplication sendAction:to:from:forEvent:] ()
#41 0x000000011f6879cc in -[UIControl sendAction:to:forEvent:] ()
#42 0x000000011f687d10 in -[UIControl _sendActionsForEvents:withEvent:] ()
#43 0x000000011f686a30 in -[UIControl touchesEnded:withEvent:] ()
#44 0x000000011fd02ae8 in -[UIWindow _sendTouchesForEvent:] ()
#45 0x000000011fd03f90 in -[UIWindow sendEvent:] ()
#46 0x000000011fce2224 in -[UIApplication sendEvent:] ()
#47 0x000000011fd5eb10 in __dispatchPreprocessedEventFromEventQueue ()
#48 0x000000011fd60774 in __processEventQueue ()
#49 0x000000011fd59650 in __eventFetcherSourceCallback ()
#50 0x00000001803731dc in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ ()
#51 0x0000000180373124 in __CFRunLoopDoSource0 ()
#52 0x0000000180372894 in __CFRunLoopDoSources0 ()
#53 0x000000018036cf00 in __CFRunLoopRun ()
#54 0x000000018036c7f4 in CFRunLoopRunSpecific ()
#55 0x0000000188faec98 in GSEventRunModal ()
#56 0x000000011fcc85d4 in -[UIApplication _run] ()
#57 0x000000011fccc5cc in UIApplicationMain ()
#58 0x000000010b0f4fc0 in UIApplicationMain(_:_:_:_:) ()
#59 0x0000000102d95dac in static UIApplicationDelegate.main() ()
#60 0x0000000102d95d34 in static AppDelegate.$main() at /Users/olcayertas/popcorn-ios/Pop/AppDelegate.swift:21
#61 0x0000000102d9624c in main ()
#62 0x00000001089a1fa0 in start_sim ()
#63 0x0000000108ac5e50 in start ()

And this is the stack trace for TCA debug tread:

Thread 14 Queue : co.pointfree.ComposableArchitecture.DebugEnvironment (serial)

#0  0x000000010324a404 in static SharedUIModel.Address.__derived_struct_equals(_:_:) ()
#1  0x000000010448a9a4 in static Home.State.__derived_struct_equals(_:_:) ()
#2  0x000000010448de64 in protocol witness for static Equatable.== infix(_:_:) in conformance Home.State ()
#3  0x0000000103086108 in static CoW<τ_0_0>.== infix(_:_:) at /Users/olcayertas/popcorn-ios/Packages/Common/Sources/Common/Helpers/CoW.swift:39
#4  0x0000000103ba06a4 in static MainTabBar.State.__derived_struct_equals(_:_:) ()
#5  0x0000000103bac51c in protocol witness for static Equatable.== infix(_:_:) in conformance MainTabBar.State ()
#6  0x0000000103a454d8 in static Box<τ_0_0>.isEqual(_:_:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-custom-dump/Sources/CustomDump/Internal/Box.swift:11
#7  0x0000000103a457b0 in protocol witness for static AnyEquatable.isEqual(_:_:) in conformance <τ_0_0> Box<τ_0_0> ()
#8  0x0000000103a45f74 in open #1 <τ_0_0>(_:) in isMirrorEqual(_:_:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-custom-dump/Sources/CustomDump/Internal/Box.swift:17
#9  0x0000000103a458d4 in isMirrorEqual(_:_:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-custom-dump/Sources/CustomDump/Internal/Box.swift:19
#10 0x0000000103a30d30 in diff<τ_0_0>(_:_:format:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-custom-dump/Sources/CustomDump/Diff.swift:546
#11 0x00000001038a77f0 in closure #1 in print #1 <τ_0_0, τ_0_1, τ_0_2><τ_1_0, τ_1_1>@Sendable () in closure #1 in AnyReducer.debug<τ_0_0, τ_0_1>(_:state:action:actionFormat:environment:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/Reducer/AnyReducer/AnyReducerDebug.swift:206
#12 0x00000001038aa6c0 in partial apply for closure #1 in print #1 <τ_0_0, τ_0_1, τ_0_2><τ_1_0, τ_1_1>@Sendable () in closure #1 in AnyReducer.debug<τ_0_0, τ_0_1>(_:state:action:actionFormat:environment:) ()
#13 0x00000001038a7e98 in thunk for @escaping @callee_guaranteed @Sendable () -> () ()
#14 0x000000010c424594 in _dispatch_call_block_and_release ()
#15 0x000000010c425d5c in _dispatch_client_callout ()
#16 0x000000010c42e040 in _dispatch_lane_serial_drain ()
#17 0x000000010c42ed80 in _dispatch_lane_invoke ()
#18 0x000000010c43cb40 in _dispatch_workloop_worker_thread ()
#19 0x00000001af2568fc in _pthread_wqthread ()
Enqueued from com.apple.main-thread (Thread 1) Queue : com.apple.main-thread (serial)
#0  0x000000010c42b0b8 in dispatch_async ()
#1  0x00000001a10ec0ac in _swift_dispatch_async ()
#2  0x00000001a10eaf48 in OS_dispatch_queue.async(group:qos:flags:execute:) ()
#3  0x00000001038a74c8 in print #1 <τ_0_0, τ_0_1, τ_0_2><τ_1_0, τ_1_1>@Sendable () in closure #1 in AnyReducer.debug<τ_0_0, τ_0_1>(_:state:action:actionFormat:environment:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/Reducer/AnyReducer/AnyReducerDebug.swift:196
#4  0x00000001038a80f8 in closure #2 in closure #1 in AnyReducer.debug<τ_0_0, τ_0_1>(_:state:action:actionFormat:environment:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/Reducer/AnyReducer/AnyReducerDebug.swift:221
#5  0x00000001038aa1e4 in partial apply for closure #2 in closure #1 in AnyReducer.debug<τ_0_0, τ_0_1>(_:state:action:actionFormat:environment:) ()
#6  0x000000010387b708 in closure #1 in closure #1 in static EffectPublisher.fireAndForget(_:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/Effects/Publisher.swift:273
#7  0x000000010387d648 in partial apply for closure #1 in closure #1 in static EffectPublisher.fireAndForget(_:) ()
#8  0x00000001af05d090 in TaskLocal.withValue<τ_0_0>(_:operation:file:line:) ()
#9  0x000000010387b4d0 in closure #1 in static EffectPublisher.fireAndForget(_:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/Effects/Publisher.swift:272
#10 0x000000019b33c0d4 in Deferred.receive<τ_0_0>(subscriber:) ()
#11 0x000000019b3d00a8 in PublisherBox.receive<τ_0_0>(subscriber:) ()
#12 0x000000019b3d02f8 in AnyPublisher.receive<τ_0_0>(subscriber:) ()
#13 0x0000000103878688 in EffectPublisher.receive<τ_0_0>(subscriber:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/Effects/Publisher.swift:13
#14 0x000000010387963c in protocol witness for Publisher.receive<τ_0_0>(subscriber:) in conformance EffectPublisher<τ_0_0, τ_0_1> ()
#15 0x000000019b38ba48 in Publishers.Merge.receive<τ_0_0>(subscriber:) ()
#16 0x000000019b3d00a8 in PublisherBox.receive<τ_0_0>(subscriber:) ()
#17 0x000000019b3d02f8 in AnyPublisher.receive<τ_0_0>(subscriber:) ()
#18 0x000000019b3d07f4 in Publishers.Map.receive<τ_0_0>(subscriber:) ()
#19 0x000000019b3d00a8 in PublisherBox.receive<τ_0_0>(subscriber:) ()
#20 0x000000019b3d02f8 in AnyPublisher.receive<τ_0_0>(subscriber:) ()
#21 0x000000019b3c56ec in Publishers.HandleEvents.receive<τ_0_0>(subscriber:) ()
#22 0x000000019b33d1f8 in Publisher.sink(receiveCompletion:receiveValue:) ()
#23 0x00000001038c840c in Store.send(_:originatingFrom:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/Store.swift:372
#24 0x00000001038cde94 in ScopedReducer.reduce(into:action:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/Store.swift:612
#25 0x00000001038ce404 in protocol witness for ReducerProtocol.reduce(into:action:) in conformance ScopedReducer<τ_0_0, τ_0_1, τ_0_2, τ_0_3> ()
#26 0x00000001038c7ee4 in Store.send(_:originatingFrom:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/Store.swift:352
#27 0x000000010399e358 in closure #1 in ViewStore.init(_:removeDuplicates:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/ViewStore.swift:203
#28 0x000000010399ea78 in ViewStore.send(_:) at /Users/olcayertas/Library/Developer/Xcode/DerivedData/Pop-cpngppyhjpuxaufspkgplncwputt/SourcePackages/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/ViewStore.swift:281
#29 0x0000000103bd3e00 in MainTabBarView.didSelectItem(at:) at /Users/olcayertas/popcorn-ios/Packages/MainTabBar/Sources/MainTabBar/MainTabBarView.swift:104
#30 0x0000000103befb28 in MainTabBarViewController.tabBar(_:didSelect:) at /Users/olcayertas/popcorn-ios/Packages/MainTabBar/Sources/MainTabBar/MainTabBarViewController.swift:168
#31 0x0000000103befd40 in @objc MainTabBarViewController.tabBar(_:didSelect:) ()
#32 0x000000011f3ff51c in -[UITabBar _sendAction:withEvent:] ()
#33 0x000000011fccd920 in -[UIApplication sendAction:to:from:forEvent:] ()
#34 0x000000011f6879cc in -[UIControl sendAction:to:forEvent:] ()
#35 0x000000011f687d10 in -[UIControl _sendActionsForEvents:withEvent:] ()
#36 0x000000011f401d1c in -[UITabBar _buttonUp:] ()
#37 0x000000011fccd920 in -[UIApplication sendAction:to:from:forEvent:] ()
#38 0x000000011f6879cc in -[UIControl sendAction:to:forEvent:] ()
#39 0x000000011f687d10 in -[UIControl _sendActionsForEvents:withEvent:] ()
#40 0x000000011f686a30 in -[UIControl touchesEnded:withEvent:] ()
#41 0x000000011fd02ae8 in -[UIWindow _sendTouchesForEvent:] ()
#42 0x000000011fd03f90 in -[UIWindow sendEvent:] ()
#43 0x000000011fce2224 in -[UIApplication sendEvent:] ()
#44 0x000000011fd5eb10 in __dispatchPreprocessedEventFromEventQueue ()
#45 0x000000011fd60774 in __processEventQueue ()
#46 0x000000011fd59650 in __eventFetcherSourceCallback ()
#47 0x00000001803731dc in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ ()
#48 0x0000000180373124 in __CFRunLoopDoSource0 ()
#49 0x0000000180372894 in __CFRunLoopDoSources0 ()
#50 0x000000018036cf00 in __CFRunLoopRun ()
#51 0x000000018036c7f4 in CFRunLoopRunSpecific ()
#52 0x0000000188faec98 in GSEventRunModal ()
#53 0x000000011fcc85d4 in -[UIApplication _run] ()
#54 0x000000011fccc5cc in UIApplicationMain ()
#55 0x000000010b0f4fc0 in UIApplicationMain(_:_:_:_:) ()
#56 0x0000000102d95dac in static UIApplicationDelegate.main() ()
#57 0x0000000102d95d34 in static AppDelegate.$main() at /Users/olcayertas/popcorn-ios/Pop/AppDelegate.swift:21
#58 0x0000000102d9624c in main ()
#59 0x00000001089a1fa0 in start_sim ()
#60 0x0000000108ac5e50 in start ()
#61 0x542c800000000000 in 0x542c800000000000 ()
stephencelis commented 1 year ago

@olcayertas Is it possible to share a building project that reproduces the problem?

Without that, I think the problem is here:

internal func handleNewUserStore(_ id: String) {
    guard let nc = getNavController() else { return }
    guard let index = viewStore.userProfileStates.firstIndex (where: { $0.id == id }) else { return }
    showUserProfile(
        nc,
        store: store.scope(
            state: \.userProfileStates[index],
            action: { .userProfileAction(id: id, action: $0) }
        )
    )
}

Grabbing an element via the index is an unsafe operation over time. Is it possible to refactor the above using the safer, [id:]-based subscript instead?

olcayertas commented 1 year ago

Hi @stephencelis. Thank you for your response.

Normally I wouldn't use the index but I have seen this approach in your UIKitCaseStudies/ListsOfState.swift example. I was trying to find the correct way to scope the state and make my child screens and main tab bar communicate correctly.

This is even happening if there is only one child state and there is no other side effects that is running that changes the array.

I have tried your suggestion and updated my code like this:

internal func handleNewUserStore(_ id: String) {
    guard let nc = getNavController() else { return }
    guard viewStore.userProfileStates[id: id] != nil else { return }
    showUserProfile(
        nc,
        store: store.scope(
            state: { $0.userProfileStates[id: id]! },
            action: { .userProfileAction(id: id, action: $0) }
        )
    )
}

And this is also crashing:

Screenshot 2022-12-21 at 20 26 34
olcayertas commented 1 year ago

I have also tried to get the state before calling showUserProfile and pass it in the (scope -> state) closure like this:

internal func handleNewUserStore(_ id: String) {
    guard let nc = getNavController() else { return }
    guard let state = viewStore.userProfileStates[id: id] else { return }
    showUserProfile(
        nc,
        store: store.scope(
            state: { [state] _ in state },
            action: { .userProfileAction(id: id, action: $0) }
        )
    )
}

But this time my action to fetch content didn't update the UI.

Can you please show me what is the correct way to have an array of child states and present child view controllers when a new state is added to array. Then remove the added state when the child view controller is dismissed?

olcayertas commented 1 year ago

With using Index or ID, I am able to show and communicate with my child view controller and clean it's state when user taps to back button using delayed clean action. The last thing remains is the clean all child states when user taps to one of the tab bar items and dismiss all the presented child view controllers.

olcayertas commented 1 year ago

OK, I think I have find the non-crashing way to do it:

internal func handleNewUserStore(_ id: String) {
    guard let nc = getNavController() else { return }
    guard viewStore.userProfileStates[id: id] != nil else { return }
    showUserProfile(
        nc,
        store: store.scope(
            state: { $0.userProfileStates[id: id] ?? .init(userId: id) },
            action: { .userProfileAction(id: id, action: $0) }
        )
    )
}

internal func showUserProfile(
    _ navController: UINavigationController?,
    store: UserStore?
) {
    guard let parent = viewStore.parentViewController else { return }
    guard let store else { return }
    let view = UserProfileView(
        store: store,
        parent: parent,
        delegate: self
    )
    let controller = UIHostingController(rootView: view)
    controller.hidesBottomBarWhenPushed = false
    navController?.pushViewController(controller, animated: true)
    navController?.setNavigationBarHidden(true, animated: true)
}

But I am not sure if this is the best approach.

olcayertas commented 1 year ago

Thank you very much for directing me to a solution that works for me! Love you guys!