Skip to content
4 changes: 3 additions & 1 deletion Examples/CaseStudies/SwiftUICaseStudies/00-RootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ struct RootView: View {
AlertAndConfirmationDialog()
}
) { store in
AlertAndConfirmationDialogView(store: store)
UIViewControllerRepresenting {
AlertAndConfirmationDialogViewController(store: store)
}
}
}
NavigationLink("Focus State") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,67 @@ struct AlertAndConfirmationDialog {
}
}

import UIKit
final class AlertAndConfirmationDialogViewController: UIViewController {
@UIBindable var store: StoreOf<AlertAndConfirmationDialog>

init(store: StoreOf<AlertAndConfirmationDialog>) {
self.store = store
super.init(nibName: nil, bundle: nil)
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
super.viewDidLoad()

let countLabel = UILabel()

let alertButton = UIButton(type: .system, primaryAction: UIAction { [weak self] _ in
self?.store.send(.alertButtonTapped)
})
alertButton.setTitle("Alert", for: .normal)

let confirmationDialogButton = UIButton(type: .system, primaryAction: UIAction { [weak self] _ in
self?.store.send(.confirmationDialogButtonTapped)
})
confirmationDialogButton.setTitle("Confirmation Dialog", for: .normal)

let stack = UIStackView(arrangedSubviews: [
countLabel,
alertButton,
confirmationDialogButton,
])
stack.axis = .vertical
stack.translatesAutoresizingMaskIntoConstraints = false

view.addSubview(stack)

NSLayoutConstraint.activate([
stack.topAnchor.constraint(equalTo: view.topAnchor),
stack.bottomAnchor.constraint(equalTo: view.bottomAnchor),
stack.leadingAnchor.constraint(equalTo: view.leadingAnchor),
stack.trailingAnchor.constraint(equalTo: view.trailingAnchor),
])

observe { [weak self] in
guard let self else { return }
countLabel.text = "Count: \(store.count)"
}

present(item: $store.scope(state: \.alert, action: \.alert)) { store in
UIAlertController(store: store)
}
present(
item: $store.scope(state: \.confirmationDialog, action: \.confirmationDialog)
) { store in
UIAlertController(store: store)
}
}
}

struct AlertAndConfirmationDialogView: View {
@Bindable var store: StoreOf<AlertAndConfirmationDialog>

Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ let package = Package(
.package(url: "https://github.com/apple/swift-collections", from: "1.1.0"),
.package(url: "https://github.com/pointfreeco/combine-schedulers", from: "1.0.2"),
.package(url: "https://github.com/pointfreeco/swift-case-paths", from: "1.5.4"),
.package(url: "https://github.com/pointfreeco/swift-concurrency-extras", from: "1.2.0"),
.package(url: "https://github.com/pointfreeco/swift-concurrency-extras", from: "1.3.0"),
.package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.2"),
.package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.4.0"),
.package(url: "https://github.com/pointfreeco/swift-identified-collections", from: "1.1.0"),
Expand Down
2 changes: 1 addition & 1 deletion [email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ let package = Package(
.package(url: "https://github.com/apple/swift-collections", from: "1.1.0"),
.package(url: "https://github.com/pointfreeco/combine-schedulers", from: "1.0.2"),
.package(url: "https://github.com/pointfreeco/swift-case-paths", from: "1.5.4"),
.package(url: "https://github.com/pointfreeco/swift-concurrency-extras", from: "1.2.0"),
.package(url: "https://github.com/pointfreeco/swift-concurrency-extras", from: "1.3.0"),
.package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.2"),
.package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.4.0"),
.package(url: "https://github.com/pointfreeco/swift-identified-collections", from: "1.1.0"),
Expand Down
21 changes: 19 additions & 2 deletions Sources/ComposableArchitecture/Observation/Store+Observation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,24 @@ extension Store: Hashable {
}
}

extension Store: Identifiable {}
extension Store: Identifiable {
public struct ID: Hashable {
fileprivate let objectIdentifier: ObjectIdentifier
fileprivate let stateIdentifier: AnyHashableSendable?
}

public nonisolated var id: ID {
ID(
objectIdentifier: ObjectIdentifier(self),
stateIdentifier: Thread.isMainThread
? MainActor._assumeIsolated {
((currentState as? any Identifiable)?.id as? any Hashable)
.map(AnyHashableSendable.init)
}
: nil
)
}
}

extension Store where State: ObservableState {
/// Scopes the store to optional child state and actions.
Expand Down Expand Up @@ -454,7 +471,7 @@ extension Store where State: ObservableState {
set {
if newValue == nil,
let childState = self.state[keyPath: state],
id == _identifiableID(childState),
id == nil || id == _identifiableID(childState),
!self.core.isInvalid
{
self.send(action(.dismiss))
Expand Down
Loading