Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ jobs:
- uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: Run swift-format
branch: 'master'
branch: 'main'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1140"
LastUpgradeVersion = "1200"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1140"
LastUpgradeVersion = "1200"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1150"
LastUpgradeVersion = "1200"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
18 changes: 13 additions & 5 deletions Examples/CaseStudies/CaseStudies.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
objects = {

/* Begin PBXBuildFile section */
CA0C51FB245389CC00A04EAB /* 04-HigherOrderReducers-ResuableOfflineDownloadsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA0C51FA245389CC00A04EAB /* 04-HigherOrderReducers-ResuableOfflineDownloadsTests.swift */; };
CA0C51FB245389CC00A04EAB /* 04-HigherOrderReducers-ReusableOfflineDownloadsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA0C51FA245389CC00A04EAB /* 04-HigherOrderReducers-ReusableOfflineDownloadsTests.swift */; };
CA25E5D224463AD700DA666A /* 01-GettingStarted-Bindings-Basics.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA25E5D124463AD700DA666A /* 01-GettingStarted-Bindings-Basics.swift */; };
CA27C0B7245780CE00CB1E59 /* 03-Effects-SystemEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA27C0B6245780CE00CB1E59 /* 03-Effects-SystemEnvironment.swift */; };
CA34170824A4E89500FAF950 /* 01-GettingStarted-AnimationsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA34170724A4E89500FAF950 /* 01-GettingStarted-AnimationsTests.swift */; };
CA410EE0247A15FE00E41798 /* 02-Effects-WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA410EDF247A15FE00E41798 /* 02-Effects-WebSocket.swift */; };
CA410EE2247C73B400E41798 /* 02-Effects-WebSocketTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA410EE1247C73B400E41798 /* 02-Effects-WebSocketTests.swift */; };
CA50BE6024A8F46500FE7DBA /* 01-GettingStarted-AlertsAndActionSheetsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA50BE5F24A8F46500FE7DBA /* 01-GettingStarted-AlertsAndActionSheetsTests.swift */; };
CA6AC2642451135C00C71CB3 /* ReusableComponents-Download.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA6AC2602451135C00C71CB3 /* ReusableComponents-Download.swift */; };
CA6AC2652451135C00C71CB3 /* CircularProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA6AC2612451135C00C71CB3 /* CircularProgressView.swift */; };
CA6AC2662451135C00C71CB3 /* DownloadComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA6AC2622451135C00C71CB3 /* DownloadComponent.swift */; };
Expand All @@ -24,6 +25,7 @@
CAA9ADC824465D950003A984 /* 02-Effects-CancellationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAA9ADC724465D950003A984 /* 02-Effects-CancellationTests.swift */; };
CAA9ADCA2446605B0003A984 /* 02-Effects-LongLiving.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAA9ADC92446605B0003A984 /* 02-Effects-LongLiving.swift */; };
CAA9ADCC2446615B0003A984 /* 02-Effects-LongLivingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAA9ADCB2446615B0003A984 /* 02-Effects-LongLivingTests.swift */; };
CAE962FD24A7F7BE00EFC025 /* 01-GettingStarted-AlertsAndActionSheets.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAE962FC24A7F7BE00EFC025 /* 01-GettingStarted-AlertsAndActionSheets.swift */; };
DC07231724465D1E003A8B65 /* 02-Effects-TimersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC07231624465D1E003A8B65 /* 02-Effects-TimersTests.swift */; };
DC072322244663B1003A8B65 /* 03-Navigation-Sheet-LoadThenPresent.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC072321244663B1003A8B65 /* 03-Navigation-Sheet-LoadThenPresent.swift */; };
DC13940E2469E25C00EE1157 /* ComposableArchitecture in Frameworks */ = {isa = PBXBuildFile; productRef = DC13940D2469E25C00EE1157 /* ComposableArchitecture */; };
Expand Down Expand Up @@ -125,12 +127,13 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
CA0C51FA245389CC00A04EAB /* 04-HigherOrderReducers-ResuableOfflineDownloadsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "04-HigherOrderReducers-ResuableOfflineDownloadsTests.swift"; sourceTree = "<group>"; };
CA0C51FA245389CC00A04EAB /* 04-HigherOrderReducers-ReusableOfflineDownloadsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "04-HigherOrderReducers-ReusableOfflineDownloadsTests.swift"; sourceTree = "<group>"; };
CA25E5D124463AD700DA666A /* 01-GettingStarted-Bindings-Basics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "01-GettingStarted-Bindings-Basics.swift"; sourceTree = "<group>"; };
CA27C0B6245780CE00CB1E59 /* 03-Effects-SystemEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "03-Effects-SystemEnvironment.swift"; sourceTree = "<group>"; };
CA34170724A4E89500FAF950 /* 01-GettingStarted-AnimationsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "01-GettingStarted-AnimationsTests.swift"; sourceTree = "<group>"; };
CA410EDF247A15FE00E41798 /* 02-Effects-WebSocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "02-Effects-WebSocket.swift"; sourceTree = "<group>"; };
CA410EE1247C73B400E41798 /* 02-Effects-WebSocketTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "02-Effects-WebSocketTests.swift"; sourceTree = "<group>"; };
CA50BE5F24A8F46500FE7DBA /* 01-GettingStarted-AlertsAndActionSheetsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "01-GettingStarted-AlertsAndActionSheetsTests.swift"; sourceTree = "<group>"; };
CA6AC2602451135C00C71CB3 /* ReusableComponents-Download.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ReusableComponents-Download.swift"; sourceTree = "<group>"; };
CA6AC2612451135C00C71CB3 /* CircularProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircularProgressView.swift; sourceTree = "<group>"; };
CA6AC2622451135C00C71CB3 /* DownloadComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadComponent.swift; sourceTree = "<group>"; };
Expand All @@ -142,6 +145,7 @@
CAA9ADC724465D950003A984 /* 02-Effects-CancellationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "02-Effects-CancellationTests.swift"; sourceTree = "<group>"; };
CAA9ADC92446605B0003A984 /* 02-Effects-LongLiving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "02-Effects-LongLiving.swift"; sourceTree = "<group>"; };
CAA9ADCB2446615B0003A984 /* 02-Effects-LongLivingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "02-Effects-LongLivingTests.swift"; sourceTree = "<group>"; };
CAE962FC24A7F7BE00EFC025 /* 01-GettingStarted-AlertsAndActionSheets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "01-GettingStarted-AlertsAndActionSheets.swift"; sourceTree = "<group>"; };
DC07231624465D1E003A8B65 /* 02-Effects-TimersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "02-Effects-TimersTests.swift"; sourceTree = "<group>"; };
DC072321244663B1003A8B65 /* 03-Navigation-Sheet-LoadThenPresent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "03-Navigation-Sheet-LoadThenPresent.swift"; sourceTree = "<group>"; };
DC25DC5E2450F13200082E81 /* IfLetStoreController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IfLetStoreController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -308,6 +312,7 @@
children = (
DC89C42424460F96006900B9 /* Info.plist */,
DC89C41A24460F95006900B9 /* 00-RootView.swift */,
CAE962FC24A7F7BE00EFC025 /* 01-GettingStarted-AlertsAndActionSheets.swift */,
DC88D8A5245341EC0077F427 /* 01-GettingStarted-Animations.swift */,
CA25E5D124463AD700DA666A /* 01-GettingStarted-Bindings-Basics.swift */,
DCC68EE02447C4630037F998 /* 01-GettingStarted-Composition-TwoCounters.swift */,
Expand Down Expand Up @@ -341,14 +346,15 @@
DC89C42C24460F96006900B9 /* SwiftUICaseStudiesTests */ = {
isa = PBXGroup;
children = (
CA50BE5F24A8F46500FE7DBA /* 01-GettingStarted-AlertsAndActionSheetsTests.swift */,
CA34170724A4E89500FAF950 /* 01-GettingStarted-AnimationsTests.swift */,
CAA9ADC324465AB00003A984 /* 02-Effects-BasicsTests.swift */,
CAA9ADC724465D950003A984 /* 02-Effects-CancellationTests.swift */,
CAA9ADCB2446615B0003A984 /* 02-Effects-LongLivingTests.swift */,
DC07231624465D1E003A8B65 /* 02-Effects-TimersTests.swift */,
CA410EE1247C73B400E41798 /* 02-Effects-WebSocketTests.swift */,
CA0C51FA245389CC00A04EAB /* 04-HigherOrderReducers-ResuableOfflineDownloadsTests.swift */,
DC634B242448D15B00DAA016 /* 04-HigherOrderReducers-ReusableFavoritingTests.swift */,
CA0C51FA245389CC00A04EAB /* 04-HigherOrderReducers-ReusableOfflineDownloadsTests.swift */,
);
path = SwiftUICaseStudiesTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -465,7 +471,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1140;
LastUpgradeCheck = 1140;
LastUpgradeCheck = 1200;
ORGANIZATIONNAME = "Point-Free";
TargetAttributes = {
DC4C6EA62450DD380066A05D = {
Expand Down Expand Up @@ -589,6 +595,7 @@
DC89C41B24460F95006900B9 /* 00-RootView.swift in Sources */,
DCC68EDD2447A5B00037F998 /* 01-GettingStarted-OptionalState.swift in Sources */,
DCC68EAB244666AF0037F998 /* 03-Navigation-Sheet-PresentAndLoad.swift in Sources */,
CAE962FD24A7F7BE00EFC025 /* 01-GettingStarted-AlertsAndActionSheets.swift in Sources */,
CA25E5D224463AD700DA666A /* 01-GettingStarted-Bindings-Basics.swift in Sources */,
DC88D8A6245341EC0077F427 /* 01-GettingStarted-Animations.swift in Sources */,
DC89C44D244621A5006900B9 /* 03-Navigation-NavigateAndLoad.swift in Sources */,
Expand All @@ -607,12 +614,13 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CA0C51FB245389CC00A04EAB /* 04-HigherOrderReducers-ResuableOfflineDownloadsTests.swift in Sources */,
CA0C51FB245389CC00A04EAB /* 04-HigherOrderReducers-ReusableOfflineDownloadsTests.swift in Sources */,
DC634B252448D15B00DAA016 /* 04-HigherOrderReducers-ReusableFavoritingTests.swift in Sources */,
CAA9ADC824465D950003A984 /* 02-Effects-CancellationTests.swift in Sources */,
CA410EE2247C73B400E41798 /* 02-Effects-WebSocketTests.swift in Sources */,
CA34170824A4E89500FAF950 /* 01-GettingStarted-AnimationsTests.swift in Sources */,
DC07231724465D1E003A8B65 /* 02-Effects-TimersTests.swift in Sources */,
CA50BE6024A8F46500FE7DBA /* 01-GettingStarted-AlertsAndActionSheetsTests.swift in Sources */,
CAA9ADC424465AB00003A984 /* 02-Effects-BasicsTests.swift in Sources */,
CAA9ADCC2446615B0003A984 /* 02-Effects-LongLivingTests.swift in Sources */,
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1140"
LastUpgradeVersion = "1200"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1140"
LastUpgradeVersion = "1200"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
11 changes: 11 additions & 0 deletions Examples/CaseStudies/SwiftUICaseStudies/00-RootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@ struct RootView: View {
)
)

NavigationLink(
"Alerts and Action Sheets",
destination: AlertAndSheetView(
store: .init(
initialState: .init(),
reducer: alertAndSheetReducer,
environment: .init()
)
)
)

NavigationLink(
"Animations",
destination: AnimationsView(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import ComposableArchitecture
import SwiftUI

private let readMe = """
This demonstrates how to best handle alerts and action sheets in the Composable Architecture.

Because the library demands that all data flow through the application in a single direction, we \
cannot leverage SwiftUI's two-way bindings because they can make changes to state without going \
through a reducer. This means we can't directly use the standard API to display alerts and sheets.

However, the library comes with two types, `AlertState` and `ActionSheetState`, which can be \
constructed from reducers and control whether or not an alert or action sheet is displayed. \
Further, it automatically handles sending actions when you tap their buttons, which allows you \
to properly handle their functionality in the reducer rather than in two-way bindings and action \
closures.

The benefit of doing this is that you can get full test coverage on how a user interacts with \
with alerts and action sheets in your application
"""

struct AlertAndSheetState: Equatable {
var actionSheet: ActionSheetState<AlertAndSheetAction>?
var alert: AlertState<AlertAndSheetAction>?
var count = 0
}

enum AlertAndSheetAction: Equatable {
case actionSheetButtonTapped
case actionSheetCancelTapped
case alertButtonTapped
case alertCancelTapped
case decrementButtonTapped
case incrementButtonTapped
}

struct AlertAndSheetEnvironment {}

let alertAndSheetReducer = Reducer<
AlertAndSheetState, AlertAndSheetAction, AlertAndSheetEnvironment
> { state, action, _ in

switch action {
case .actionSheetButtonTapped:
state.actionSheet = .init(
title: "Action sheet",
message: "This is an action sheet.",
buttons: [
.cancel(),
.default("Increment", send: .incrementButtonTapped),
.default("Decrement", send: .decrementButtonTapped),
]
)
return .none

case .actionSheetCancelTapped:
state.actionSheet = nil
return .none

case .alertButtonTapped:
state.alert = .init(
title: "Alert!",
message: "This is an alert",
primaryButton: .cancel(),
secondaryButton: .default("Increment", send: .incrementButtonTapped)
)
return .none

case .alertCancelTapped:
state.alert = nil
return .none

case .decrementButtonTapped:
state.actionSheet = nil
state.count -= 1
return .none

case .incrementButtonTapped:
state.actionSheet = nil
state.alert = nil
state.count += 1
return .none
}
}

struct AlertAndSheetView: View {
let store: Store<AlertAndSheetState, AlertAndSheetAction>

var body: some View {
WithViewStore(self.store) { viewStore in
Form {
Section(header: Text(template: readMe, .caption)) {
Text("Count: \(viewStore.count)")

Button("Alert") { viewStore.send(.alertButtonTapped) }
.alert(
self.store.scope(state: { $0.alert }),
dismiss: .alertCancelTapped
)

Button("Action sheet") { viewStore.send(.actionSheetButtonTapped) }
.actionSheet(
self.store.scope(state: { $0.actionSheet }),
dismiss: .actionSheetCancelTapped
)
}
}
}
.navigationBarTitle("Alerts & Action Sheets")
}
}

struct AlertAndSheet_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
AlertAndSheetView(
store: .init(
initialState: .init(),
reducer: alertAndSheetReducer,
environment: .init()
)
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct SharedState: Equatable {
enum Tab { case counter, profile }

struct CounterState: Equatable {
var alert: String?
var alert: AlertState<SharedStateAction.CounterAction>?
var count = 0
var maxCount = 0
var minCount = 0
Expand Down Expand Up @@ -105,10 +105,11 @@ let sharedStateCounterReducer = Reducer<
return .none

case .isPrimeButtonTapped:
state.alert =
isPrime(state.count)
? "👍 The number \(state.count) is prime!"
: "👎 The number \(state.count) is not prime :("
state.alert = .init(
title: isPrime(state.count)
? "👍 The number \(state.count) is prime!"
: "👎 The number \(state.count) is not prime :("
)
return .none
}
}
Expand Down Expand Up @@ -204,14 +205,7 @@ struct SharedStateCounterView: View {
.padding(16)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .top)
.navigationBarTitle("Shared State Demo")
.alert(
item: viewStore.binding(
get: { $0.alert.map(PrimeAlert.init(title:)) },
send: .alertDismissed
)
) { alert in
SwiftUI.Alert(title: Text(alert.title))
}
.alert(self.store.scope(state: { $0.alert }), dismiss: .alertDismissed)
}
}
}
Expand Down Expand Up @@ -249,11 +243,6 @@ struct SharedStateProfileView: View {
}
}

private struct PrimeAlert: Equatable, Identifiable {
let title: String
var id: String { self.title }
}

// MARK: - SwiftUI previews

struct SharedState_Previews: PreviewProvider {
Expand Down
Loading