Skip to content

Commit

Permalink
Add UIAlertController example
Browse files Browse the repository at this point in the history
  • Loading branch information
heinzl committed Oct 20, 2021
1 parent 185b1a3 commit 0d28efc
Show file tree
Hide file tree
Showing 7 changed files with 281 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
D8527760266A50DD00BFCC44 /* CountryListAndDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = D852775F266A50DD00BFCC44 /* CountryListAndDetail.swift */; };
D8527762266A886E00BFCC44 /* ContinentFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8527761266A886E00BFCC44 /* ContinentFilter.swift */; };
D8527764266BA39E00BFCC44 /* CountrySort.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8527763266BA39E00BFCC44 /* CountrySort.swift */; };
D8527767266BB22A00BFCC44 /* CountryTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8527766266BB22A00BFCC44 /* CountryTabBar.swift */; };
D852776A266BB4C600BFCC44 /* CountryDeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8527769266BB4C600BFCC44 /* CountryDeepLink.swift */; };
D8E7BACC265C340000936E27 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E7BACB265C340000936E27 /* AppDelegate.swift */; };
D8E7BACE265C340000936E27 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E7BACD265C340000936E27 /* SceneDelegate.swift */; };
Expand All @@ -31,6 +30,8 @@
D8E7BAD8265C340200936E27 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D8E7BAD6265C340200936E27 /* LaunchScreen.storyboard */; };
D8E7BAEE265C340200936E27 /* ComposableNavigation_ExamplesUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E7BAED265C340200936E27 /* ComposableNavigation_ExamplesUITests.swift */; };
D8E7BAFE265CCE0D00936E27 /* ComposableNavigation in Frameworks */ = {isa = PBXBuildFile; productRef = D8E7BAFD265CCE0D00936E27 /* ComposableNavigation */; };
D8ED18322720425300E93021 /* AlertPlayground.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8ED18312720425300E93021 /* AlertPlayground.swift */; };
D8ED183427204EBA00E93021 /* AdvancedTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8ED183327204EBA00E93021 /* AdvancedTabBar.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -59,7 +60,6 @@
D852775F266A50DD00BFCC44 /* CountryListAndDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountryListAndDetail.swift; sourceTree = "<group>"; };
D8527761266A886E00BFCC44 /* ContinentFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContinentFilter.swift; sourceTree = "<group>"; };
D8527763266BA39E00BFCC44 /* CountrySort.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountrySort.swift; sourceTree = "<group>"; };
D8527766266BB22A00BFCC44 /* CountryTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountryTabBar.swift; sourceTree = "<group>"; };
D8527769266BB4C600BFCC44 /* CountryDeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountryDeepLink.swift; sourceTree = "<group>"; };
D8E7BAC8265C340000936E27 /* ComposableNavigation-Examples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ComposableNavigation-Examples.app"; sourceTree = BUILT_PRODUCTS_DIR; };
D8E7BACB265C340000936E27 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
Expand All @@ -72,6 +72,8 @@
D8E7BAED265C340200936E27 /* ComposableNavigation_ExamplesUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposableNavigation_ExamplesUITests.swift; sourceTree = "<group>"; };
D8E7BAEF265C340200936E27 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D8E7BAFB265C345000936E27 /* ComposableNavigation */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ComposableNavigation; path = ../..; sourceTree = "<group>"; };
D8ED18312720425300E93021 /* AlertPlayground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertPlayground.swift; sourceTree = "<group>"; };
D8ED183327204EBA00E93021 /* AdvancedTabBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdvancedTabBar.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -130,6 +132,7 @@
D8527765266BB14F00BFCC44 /* TabBar */,
D8527768266BB45000BFCC44 /* DeepLink */,
D852775E266A509500BFCC44 /* ListAndDetail */,
D8ED18302720422800E93021 /* AlertPlayground */,
);
path = Advanced;
sourceTree = "<group>";
Expand Down Expand Up @@ -159,7 +162,7 @@
D8527765266BB14F00BFCC44 /* TabBar */ = {
isa = PBXGroup;
children = (
D8527766266BB22A00BFCC44 /* CountryTabBar.swift */,
D8ED183327204EBA00E93021 /* AdvancedTabBar.swift */,
);
path = TabBar;
sourceTree = "<group>";
Expand Down Expand Up @@ -231,6 +234,14 @@
name = Frameworks;
sourceTree = "<group>";
};
D8ED18302720422800E93021 /* AlertPlayground */ = {
isa = PBXGroup;
children = (
D8ED18312720425300E93021 /* AlertPlayground.swift */,
);
path = AlertPlayground;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -340,19 +351,20 @@
D8365D7C266421E9005EA884 /* SwipeDownModalSheet.swift in Sources */,
D8527760266A50DD00BFCC44 /* CountryListAndDetail.swift in Sources */,
D8527756266A356500BFCC44 /* CountryProvider.swift in Sources */,
D8527767266BB22A00BFCC44 /* CountryTabBar.swift in Sources */,
D852774A26666DB300BFCC44 /* SwipeBackOnStackNavigation.swift in Sources */,
D8365D6B26625CDD005EA884 /* Counter.swift in Sources */,
D8365D69266244CE005EA884 /* ModalShowcase.swift in Sources */,
D8527764266BA39E00BFCC44 /* CountrySort.swift in Sources */,
D852774C2667D00300BFCC44 /* ChangingTabs.swift in Sources */,
D8365D76266394B4005EA884 /* TabsShowcase.swift in Sources */,
D852776A266BB4C600BFCC44 /* CountryDeepLink.swift in Sources */,
D8ED183427204EBA00E93021 /* AdvancedTabBar.swift in Sources */,
D8527762266A886E00BFCC44 /* ContinentFilter.swift in Sources */,
D852775A266A4BDA00BFCC44 /* CountryList.swift in Sources */,
D8E7BACC265C340000936E27 /* AppDelegate.swift in Sources */,
D8527754266A351E00BFCC44 /* Country.swift in Sources */,
D8E7BACE265C340000936E27 /* SceneDelegate.swift in Sources */,
D8ED18322720425300E93021 /* AlertPlayground.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
environment: .init()
))
case .advanced:
return CountryTabBar.makeView(Store(
initialState: CountryTabBar.State(),
reducer: CountryTabBar.reducer,
return AdvancedTabBar.makeView(Store(
initialState: AdvancedTabBar.State(),
reducer: AdvancedTabBar.reducer,
environment: .init(countryProvider: .init())
))
case .uiTest(.swipeDownModalSheet):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import UIKit
import SwiftUI
import ComposableNavigation
import ComposableArchitecture

struct AlertPlayground {

// MARK: TCA

enum ModalScreen: Hashable {
case resetAlert
case actionSheet
}

struct State: Equatable {
static let initialCount = 5

var counter = Counter.State(id: 0, count: Self.initialCount)
var alertNavigation = ModalNavigation<ModalScreen>.State()

var isResetDisabled: Bool {
counter.count == Self.initialCount
}
}

enum Action: Equatable {
case counter(Counter.Action)
case resetCounter
case alertNavigation(ModalNavigation<ModalScreen>.Action)
}

struct Environment {}

private static let privateReducer = Reducer<State, Action, Environment> { state, action, environment in
switch action {
case .resetCounter:
state.counter.count = State.initialCount
default:
break
}
return .none
}

static let reducer: Reducer<State, Action, Environment> = Reducer.combine([
ModalNavigation<ModalScreen>.reducer()
.pullback(
state: \.alertNavigation,
action: /Action.alertNavigation,
environment: { _ in () }
),
Counter.reducer
.pullback(
state: \.counter,
action: /Action.counter,
environment: { _ in .init() }
),
privateReducer
])

// MARK: View creation

struct ModalViewProvider: ViewProviding {
let store: Store<State, Action>

func makePresentable(for navigationItem: ModalScreen) -> Presentable {
let viewStore = ViewStore(store)
switch navigationItem {
case .resetAlert:
let alert = UIAlertController(
title: "Warning",
message: "Reset counter?",
preferredStyle: .alert
)
alert.addAction(.init(title: "Cancel", style: .cancel, handler: { _ in
viewStore.send(.alertNavigation(.dismiss))
}))
alert.addAction(.init(title: "Reset", style: .destructive, handler: { _ in
viewStore.send(.alertNavigation(.dismiss))
viewStore.send(.resetCounter)
}))
return alert
case .actionSheet:
let alert = UIAlertController(
title: "Choose from following options:",
message: nil,
preferredStyle: .actionSheet
)
alert.addAction(.init(title: "Cancel", style: .cancel, handler: { _ in
viewStore.send(.alertNavigation(.dismiss))
}))
alert.addAction(.init(title: "Up", style: .default, handler: { _ in
viewStore.send(.alertNavigation(.dismiss))
viewStore.send(.counter(.up))
}))
alert.addAction(.init(title: "Down", style: .default, handler: { _ in
viewStore.send(.alertNavigation(.dismiss))
viewStore.send(.counter(.down))
}))
let resetAction = UIAlertAction(title: "Reset", style: .destructive, handler: { _ in
viewStore.send(.alertNavigation(.dismiss))
viewStore.send(.alertNavigation(.presentFullScreen(.resetAlert)))
})
resetAction.isEnabled = !viewStore.isResetDisabled
alert.addAction(resetAction)
return alert
}
}
}
}

struct AlertPlaygroundView: View, Presentable {
let store: Store<AlertPlayground.State, AlertPlayground.Action>

var body: some View {
WithViewStore(store) { viewStore in
VStack {
Text("Count: \(viewStore.counter.count)")
.font(.largeTitle)
.padding()

Button("Options") {
viewStore.send(.alertNavigation(.presentFullScreen(.actionSheet)))
}
.padding()

Button("Reset") {
viewStore.send(.alertNavigation(.presentFullScreen(.resetAlert)))
}
.disabled(viewStore.isResetDisabled)
.padding()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ struct CountryDeepLink {
enum Action: Equatable {
case showCountry(Country.ID)
case showSorting
case showAlertOptions
}

struct Environment {}
Expand All @@ -26,11 +27,20 @@ struct CountryDeepLinkView: View, Presentable {
WithViewStore(store) { viewStore in
NavigationView {
Form {
Button("Show Austria") {
Button {
viewStore.send(.showCountry("Austria"))
} label: {
Label("Show Austria", systemImage: "list.dash")
}
Button("Show sorting options") {
Button {
viewStore.send(.showSorting)
} label: {
Label("Show sorting options", systemImage: "arrow.up.arrow.down.circle")
}
Button {
viewStore.send(.showAlertOptions)
} label: {
Label("Show alert options", systemImage: "exclamationmark.bubble")
}
}
.navigationTitle("Deep link")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,12 +208,10 @@ struct CountryListAndDetail {
)
)
case .sort:
return CountrySortView(
store: store.scope(
state: \.countrySort,
action: Action.countrySort
)
)
return CountrySort.makeView(store.scope(
state: \.countrySort,
action: Action.countrySort
))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,95 @@ struct CountrySort {
case descending
}

enum ModalScreen {
case resetAlert
}

struct State: Equatable {
var sortKey: SortKey = .country
var sortOrder: SortOrder = .ascending
var alertNavigation = ModalNavigation<ModalScreen>.State()

var isResetDisabled: Bool {
sortKey == .country && sortOrder == .ascending
}
}

enum Action: Equatable {
case selectSortKey(SortKey)
case selectSortOrder(SortOrder)
case done
case showFilter
case resetTapped
case resetConfirmed
case alertNavigation(ModalNavigation<ModalScreen>.Action)
}

struct Environment {}

static let reducer = Reducer<State, Action, Environment> { state, action, environment in
private static let privateReducer = Reducer<State, Action, Environment> { state, action, environment in
switch action {
case .selectSortKey(let sortKey):
state.sortKey = sortKey
case .selectSortOrder(let sortOrder):
state.sortOrder = sortOrder
case .resetTapped:
return Effect(value: .alertNavigation(.presentFullScreen(.resetAlert)))
case .resetConfirmed:
state.sortKey = .country
state.sortOrder = .ascending
default:
break
}
return .none
}

static let reducer: Reducer<State, Action, Environment> = Reducer.combine([
ModalNavigation<ModalScreen>.reducer()
.pullback(
state: \.alertNavigation,
action: /Action.alertNavigation,
environment: { _ in () }
),
privateReducer
])

// MARK: View creation

struct ViewProvider: ViewProviding {
let store: Store<State, Action>

func makePresentable(for navigationItem: ModalScreen) -> Presentable {
switch navigationItem {
case .resetAlert:
let viewStore = ViewStore(store)
let alert = UIAlertController(
title: "Confirmation",
message: "Reset sort settings?",
preferredStyle: .alert
)
alert.addAction(.init(title: "Cancel", style: .cancel, handler: { _ in
viewStore.send(.alertNavigation(.dismiss))
}))
alert.addAction(.init(title: "Reset", style: .destructive, handler: { _ in
viewStore.send(.resetConfirmed)
viewStore.send(.alertNavigation(.dismiss))
}))
return alert
}
}
}

static func makeView(_ store: Store<State, Action>) -> UIViewController{
return CountrySortView(store: store)
.viewController.withModal(
store: store.scope(
state: \.alertNavigation,
action: Action.alertNavigation
),
viewProvider: ViewProvider(store: store)
)
}
}

struct CountrySortView: View, Presentable {
Expand Down Expand Up @@ -77,10 +141,15 @@ struct CountrySortView: View, Presentable {
})
)
.toolbar {
ToolbarItem(placement: .bottomBar) {
ToolbarItemGroup(placement: .bottomBar) {
Button("Show filter options") {
viewStore.send(.showFilter)
}
Spacer()
Button("Reset") {
viewStore.send(.resetTapped)
}
.disabled(viewStore.isResetDisabled)
}
}
}
Expand Down
Loading

0 comments on commit 0d28efc

Please sign in to comment.