Skip to content

Commit

Permalink
Case key paths (#132)
Browse files Browse the repository at this point in the history
* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* cleanup

* wip

* wip

* wip

* wip

* wip

* Don't bother running windows tests
  • Loading branch information
stephencelis authored Nov 13, 2023
1 parent 5eb5899 commit a3aa5d4
Show file tree
Hide file tree
Showing 43 changed files with 2,533 additions and 2,814 deletions.
23 changes: 10 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,19 @@ concurrency:

jobs:
library:
runs-on: macos-12
runs-on: macos-13
strategy:
matrix:
xcode: ['13.4.1', '14.1']
xcode:
- '15.0'
- '14.3.1'

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Select Xcode ${{ matrix.xcode }}
run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app
- name: Skip macro validation
run: defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES
- name: Run tests
run: make test

Expand All @@ -38,15 +42,8 @@ jobs:
steps:
- uses: compnerd/gha-setup-swift@main
with:
branch: swift-5.8.1-release
tag: 5.8.1-RELEASE
- uses: actions/checkout@v3
branch: swift-5.9.1-release
tag: 5.9.1-RELEASE
- uses: actions/checkout@v4
- name: Build
run: swift build -c ${{ matrix.config }}
- name: Run tests (debug only)
# There is an issue that exists in the 5.8.1 toolchain
# which fails on release configuration testing, but
# this issue is fixed 5.9 so we can remove the if once
# that is generally available.
if: ${{ matrix.config == 'debug' }}
run: swift test
10 changes: 4 additions & 6 deletions .github/workflows/format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@ on:
jobs:
swift_format:
name: swift-format
runs-on: macOS-11
runs-on: macOS-13
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Xcode Select
run: sudo xcode-select -s /Applications/Xcode_13.0.app
- name: Tap
run: brew tap pointfreeco/formulae
run: sudo xcode-select -s /Applications/Xcode_15.0.app
- name: Install
run: brew install Formulae/swift-format@5.5
run: brew install swift-format
- name: Format
run: make format
- uses: stefanzweifel/git-auto-commit-action@v4
Expand Down
5 changes: 5 additions & 0 deletions .spi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
version: 1
builder:
configs:
- documentation_targets: [SwiftUINavigation, SwiftUINavigationCore]
swift_version: 5.9
12 changes: 5 additions & 7 deletions Examples/CaseStudies/08-Routing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ private let readMe = """
in this library.
"""

@CasePathable
enum Destination {
case alert(AlertState<AlertAction>)
case confirmationDialog(ConfirmationDialogState<DialogAction>)
Expand Down Expand Up @@ -80,7 +81,7 @@ struct Routing: View {
}
}
.navigationTitle("Routing")
.alert(unwrapping: self.$destination, case: /Destination.alert) { action in
.alert(self.$destination.alert) { action in
switch action {
case .randomize?:
self.count = .random(in: 0...1_000)
Expand All @@ -90,10 +91,7 @@ struct Routing: View {
break
}
}
.confirmationDialog(
unwrapping: self.$destination,
case: /Destination.confirmationDialog
) { action in
.confirmationDialog(self.$destination.confirmationDialog) { action in
switch action {
case .decrement?:
self.count -= 1
Expand All @@ -103,13 +101,13 @@ struct Routing: View {
break
}
}
.navigationDestination(unwrapping: self.$destination, case: /Destination.link) { $count in
.navigationDestination(unwrapping: self.$destination.link) { $count in
Form {
Stepper("Count: \(count)", value: $count)
}
.navigationTitle("Routing link")
}
.sheet(unwrapping: self.$destination, case: /Destination.sheet) { $count in
.sheet(unwrapping: self.$destination.sheet) { $count in
NavigationStack {
Form {
Stepper("Count: \(count)", value: $count)
Expand Down
2 changes: 1 addition & 1 deletion Examples/CaseStudies/09-CustomComponents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ extension View {

fileprivate func bottomMenu<Enum, Case, Content>(
unwrapping value: Binding<Enum?>,
case casePath: CasePath<Enum, Case>,
case casePath: AnyCasePath<Enum, Case>,
@ViewBuilder content: @escaping (Binding<Case>) -> Content
) -> some View
where Content: View {
Expand Down
29 changes: 16 additions & 13 deletions Examples/CaseStudies/11-IfLet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import SwiftUI
import SwiftUINavigation

private let readMe = """
This demonstrates to use the IfLet view to unwrap a binding of an optional into a binding of \
an honest value.
This demonstrates how to unwrap a binding of an optional into a binding of an honest value.
Tap the "Edit" button to put the form into edit mode. Then you can make changes to the message \
and either commit the changes by tapping "Save", or discard the changes by tapping "Discard".
Expand All @@ -18,25 +17,29 @@ struct IfLetCaseStudy: View {
Section {
Text(readMe)
}
IfLet(self.$editableString) { $string in
TextField("Edit string", text: $string)
HStack {
Button("Discard") {
self.editableString = nil
}
Button("Save") {
self.string = string
self.editableString = nil
Binding(unwrapping: self.$editableString).map { $string in
VStack {
TextField("Edit string", text: $string)
HStack {
Button("Discard") {
self.editableString = nil
}
Spacer()
Button("Save") {
self.string = string
self.editableString = nil
}
}
}
} else: {
}
if self.editableString == nil {
Text("\(self.string)")
Button("Edit") {
self.editableString = self.string
}
}
.buttonStyle(.borderless)
}
.buttonStyle(.borderless)
}
}

Expand Down
30 changes: 17 additions & 13 deletions Examples/CaseStudies/12-IfCaseLet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import SwiftUI
import SwiftUINavigation

private let readMe = """
This demonstrates to use the IfCaseLet view to destructure a binding of an enum into a binding \
of one of its cases.
This demonstrates how to destructure a binding of an enum into a binding of one of its cases.
Tap the "Edit" button to put the form into edit mode. Then you can make changes to the message \
and either commit the changes by tapping "Save", or discard the changes by tapping "Discard".
Expand All @@ -14,6 +13,7 @@ struct IfCaseLetCaseStudy: View {
@State var string: String = "Hello"
@State var editableString: EditableString = .inactive

@CasePathable
enum EditableString {
case active(String)
case inactive
Expand All @@ -24,25 +24,29 @@ struct IfCaseLetCaseStudy: View {
Section {
Text(readMe)
}
IfCaseLet(self.$editableString, pattern: /EditableString.active) { $string in
TextField("Edit string", text: $string)
HStack {
Button("Discard") {
self.editableString = .inactive
}
Button("Save") {
self.string = string
self.editableString = .inactive
self.$editableString.active.map { $string in
VStack {
TextField("Edit string", text: $string)
HStack {
Button("Discard", role: .cancel) {
self.editableString = .inactive
}
Spacer()
Button("Save") {
self.string = string
self.editableString = .inactive
}
}
}
} else: {
}
if !self.editableString.is(\.active) {
Text("\(self.string)")
Button("Edit") {
self.editableString = .active(self.string)
}
}
.buttonStyle(.borderless)
}
.buttonStyle(.borderless)
}
}

Expand Down
4 changes: 2 additions & 2 deletions Examples/CaseStudies/RootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ struct RootView: View {
NavigationLink("Synchronized bindings") {
SynchronizedBindings()
}
NavigationLink("IfLet view") {
NavigationLink("Optional bindings") {
IfLetCaseStudy()
}
NavigationLink("IfCaseLet view") {
NavigationLink("Enum bindings") {
IfCaseLetCaseStudy()
}
} header: {
Expand Down
11 changes: 3 additions & 8 deletions Examples/Inventory/Inventory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class InventoryModel {
}
var destination: Destination?

@CasePathable
enum Destination: Equatable {
case add(Item)
case edit(Item)
Expand Down Expand Up @@ -84,10 +85,7 @@ struct InventoryView: View {
}
}
.navigationTitle("Inventory")
.navigationDestination(
unwrapping: self.$model.destination,
case: /InventoryModel.Destination.edit
) { $item in
.navigationDestination(unwrapping: self.$model.destination.edit) { $item in
ItemView(item: $item)
.navigationBarTitle("Edit")
.navigationBarBackButtonHidden(true)
Expand All @@ -104,10 +102,7 @@ struct InventoryView: View {
}
}
}
.sheet(
unwrapping: self.$model.destination,
case: /InventoryModel.Destination.add
) { $itemToAdd in
.sheet(unwrapping: self.$model.destination.add) { $itemToAdd in
NavigationStack {
ItemView(item: $itemToAdd)
.navigationTitle("Add")
Expand Down
22 changes: 12 additions & 10 deletions Examples/Inventory/Item.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,10 @@ struct Item: Equatable, Identifiable {
var name: String
var status: Status

@CasePathable
enum Status: Equatable {
case inStock(quantity: Int)
case outOfStock(isOnBackOrder: Bool)

var isInStock: Bool {
guard case .inStock = self else { return false }
return true
}
}

struct Color: Equatable, Hashable {
Expand Down Expand Up @@ -62,26 +58,32 @@ struct ItemView: View {
}
}

Switch(self.$item.status) {
CaseLet(/Item.Status.inStock) { $quantity in
Section(header: Text("In stock")) {
switch self.item.status {
case .inStock:
self.$item.status.inStock.map { $quantity in
Section {
Stepper("Quantity: \(quantity)", value: $quantity)
Button("Mark as sold out") {
withAnimation {
self.item.status = .outOfStock(isOnBackOrder: false)
}
}
} header: {
Text("In stock")
}
.transition(.opacity)
}
CaseLet(/Item.Status.outOfStock) { $isOnBackOrder in
Section(header: Text("Out of stock")) {
case .outOfStock:
self.$item.status.outOfStock.map { $isOnBackOrder in
Section {
Toggle("Is on back order?", isOn: $isOnBackOrder)
Button("Is back in stock!") {
withAnimation {
self.item.status = .inStock(quantity: 1)
}
}
} header: {
Text("Out of stock")
}
.transition(.opacity)
}
Expand Down
13 changes: 4 additions & 9 deletions Examples/Inventory/ItemRow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class ItemRowModel: Identifiable {
var item: Item
var destination: Destination?

@CasePathable
enum Destination: Equatable {
case alert(AlertState<AlertAction>)
case duplicate(Item)
Expand Down Expand Up @@ -113,17 +114,11 @@ struct ItemRowView: View {
.padding(.leading)
}
.buttonStyle(.plain)
.foregroundColor(self.model.item.status.isInStock ? nil : Color.gray)
.alert(
unwrapping: self.$model.destination,
case: /ItemRowModel.Destination.alert
) {
.foregroundColor(self.model.item.status.is(\.inStock) ? nil : Color.gray)
.alert(self.$model.destination.alert) {
self.model.alertButtonTapped($0)
}
.popover(
unwrapping: self.$model.destination,
case: /ItemRowModel.Destination.duplicate
) { $item in
.popover(unwrapping: self.$model.destination.duplicate) { $item in
NavigationStack {
ItemView(item: $item)
.navigationBarTitle("Duplicate")
Expand Down
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"repositoryURL": "https://github.com/pointfreeco/swift-case-paths",
"state": {
"branch": null,
"revision": "5da6989aae464f324eef5c5b52bdb7974725ab81",
"version": "1.0.0"
"revision": "40773cbaf8d71ed5357f297b1ba4073f5b24faaa",
"version": "1.1.0"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
.package(url: "https://github.com/pointfreeco/swift-case-paths", from: "1.0.0"),
.package(url: "https://github.com/pointfreeco/swift-case-paths", from: "1.1.0"),
.package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.0.0"),
.package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.0.0"),
],
Expand Down
Loading

0 comments on commit a3aa5d4

Please sign in to comment.