-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #74 from wwt/swiftui-docs
SwiftUI Documentation
- Loading branch information
Showing
7 changed files
with
233 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
## Overview | ||
|
||
This guide will walk you through getting a [Workflow](https://wwt.github.io/SwiftCurrent/Classes/Workflow.html) up and running in a new iOS project. | ||
|
||
The app in this guide is going to be very simple. It consists of a view that will host the [WorkflowView](https://wwt.github.io/SwiftCurrent/Structs/WorkflowView.html), a view to enter an email address, and an optional view for when the user enters an email with `@wwt.com` in it. Here is a preview of what the app will look like: | ||
|
||
![Preview image of app](https://github.com/wwt/SwiftCurrent/blob/36c45fcc3cc66dba16d5d5c78bcd4bc865175a34/wiki/swiftUI.gif) | ||
|
||
## Adding the dependency | ||
|
||
For instructions on SPM and CocoaPods, [check out our installation page.](https://github.com/wwt/SwiftCurrent/wiki/Installation#swift-package-manager) | ||
|
||
## IMPORTANT NOTE | ||
|
||
SwiftCurrent is so convenient that you may miss the couple lines that are calls to the library. To make it easier, we've marked our code snippets with `// SwiftCurrent` to highlight items that are coming from the library. | ||
|
||
## Create your views | ||
|
||
Create two views that implement [FlowRepresentable](https://wwt.github.io/SwiftCurrent/Protocols/FlowRepresentable.html). | ||
|
||
```swift | ||
import SwiftUI | ||
import SwiftCurrent | ||
|
||
struct FirstView: View, FlowRepresentable { // SwiftCurrent | ||
typealias WorkflowOutput = String // SwiftCurrent | ||
weak var _workflowPointer: AnyFlowRepresentable? // SwiftCurrent | ||
|
||
@State private var email = "" | ||
private let name: String | ||
|
||
init(with name: String) { // SwiftCurrent | ||
self.name = name | ||
} | ||
|
||
var body: some View { | ||
VStack { | ||
Text("Welcome \(name)!") | ||
TextField("Enter email...", text: $email) | ||
.textContentType(.emailAddress) | ||
Button("Save") { proceedInWorkflow(email) } | ||
} | ||
} | ||
} | ||
|
||
struct FirstView_Previews: PreviewProvider { | ||
static var previews: some View { | ||
FirstView(with: "Example Name") | ||
} | ||
} | ||
|
||
struct SecondView: View, FlowRepresentable { // SwiftCurrent | ||
typealias WorkflowOutput = String // SwiftCurrent | ||
weak var _workflowPointer: AnyFlowRepresentable? // SwiftCurrent | ||
|
||
private let email: String | ||
|
||
init(with email: String) { // SwiftCurrent | ||
self.email = email | ||
} | ||
|
||
var body: some View { | ||
VStack { | ||
Button("Finish") { proceedInWorkflow(email) } | ||
} | ||
} | ||
|
||
func shouldLoad() -> Bool { // SwiftCurrent | ||
email.lowercased().contains("@wwt.com") | ||
} | ||
} | ||
|
||
struct SecondView_Previews: PreviewProvider { | ||
static var previews: some View { | ||
SecondView(with: "[email protected]") | ||
} | ||
} | ||
``` | ||
|
||
### Let's talk about what is going on with these views | ||
|
||
#### **Why is `_workflowPointer` weak?** | ||
|
||
<details> | ||
|
||
The [FlowRepresentable](https://wwt.github.io/SwiftCurrent/Protocols/FlowRepresentable.html) protocol requires there to be a `_workflowPointer` on your object, but protocols cannot enforce you to use `weak`. If you do not put `weak var _workflowPointer`, the [FlowRepresentable](https://wwt.github.io/SwiftCurrent/Protocols/FlowRepresentable.html) will end up with a strong circular reference when placed in a [WorkflowView](https://wwt.github.io/SwiftCurrent/Structs/WorkflowView.html). | ||
</details> | ||
|
||
#### **What's this `shouldLoad()`?** | ||
|
||
<details> | ||
|
||
It is part of the [FlowRepresentable](https://wwt.github.io/SwiftCurrent/Protocols/FlowRepresentable.html) protocol. It has default implementations created for your convenience but is still implementable if you want to control when a [FlowRepresentable](https://wwt.github.io/SwiftCurrent/Protocols/FlowRepresentable.html) should load in the workflow. It is called after `init` but before `body` in SwiftUI. | ||
</details> | ||
|
||
#### **Why is there a `WorkflowOutput` but no `WorkflowInput`?** | ||
|
||
<details> | ||
|
||
`WorkflowInput` is inferred from the initializer that you create. If you do not include an initializer, `WorkflowInput` will be `Never`; otherwise `WorkflowInput` will be the type supplied in the initializer. `WorkflowOutput` cannot be inferred to be anything other than `Never`. This means you must manually provide `WorkflowOutput` a type when you want to pass data forward. | ||
</details> | ||
|
||
## Launching the [Workflow](https://wwt.github.io/SwiftCurrent/Classes/Workflow.html) | ||
|
||
Next we add a [WorkflowView](https://wwt.github.io/SwiftCurrent/Structs/WorkflowView.html) to the body of our starting app view, in this case `ContentView`. | ||
|
||
```swift | ||
import SwiftUI | ||
import SwiftCurrent_SwiftUI | ||
|
||
struct ContentView: View { | ||
@State var workflowIsPresented = false | ||
var body: some View { | ||
if !workflowIsPresented { | ||
Button("Present") { $workflowIsPresented.wrappedValue = true } | ||
} | ||
WorkflowView(isLaunched: $workflowIsPresented, startingArgs: "SwiftCurrent") // SwiftCurrent | ||
.thenProceed(with: WorkflowItem(FirstView.self) // SwiftCurrent | ||
.applyModifiers { firstView in firstView.padding().border(.gray) }) | ||
.thenProceed(with: WorkflowItem(SecondView.self) // SwiftCurrent | ||
.applyModifiers { $0.padding().border(.gray) }) | ||
.onFinish { passedArgs in // SwiftCurrent | ||
workflowIsPresented = false | ||
guard case .args(let emailAddress as String) = passedArgs else { | ||
print("No email address supplied") | ||
return | ||
} | ||
print(emailAddress) | ||
} | ||
} | ||
} | ||
|
||
struct Content_Previews: PreviewProvider { | ||
static var previews: some View { | ||
ContentView() | ||
} | ||
} | ||
``` | ||
|
||
### Let's discuss what's going on here | ||
|
||
#### **Wait, where is the [Workflow](https://wwt.github.io/SwiftCurrent/Classes/Workflow.html)?** | ||
|
||
<details> | ||
|
||
In SwiftUI, the [Workflow](https://wwt.github.io/SwiftCurrent/Classes/Workflow.html) type is handled by the [WorkflowView](https://wwt.github.io/SwiftCurrent/Structs/WorkflowView.html). That view contains an underlying [AnyWorkflow](https://wwt.github.io/SwiftCurrent/Classes/AnyWorkflow.html) that it manages and exposes as appropriate. | ||
</details> | ||
|
||
#### **Where is the type safety, I heard about?** | ||
|
||
<details> | ||
|
||
[WorkflowView](https://wwt.github.io/SwiftCurrent/Structs/WorkflowView.html) is specialized with your `startingArgs` type. In [FlowRepresentable](https://wwt.github.io/SwiftCurrent/Protocols/FlowRepresentable.html), these types are supplied by the `WorkflowInput` and `WorkflowOutput` associated types. These all work together to create compile-time type safety when creating your flow. This means that you will get a build error if the output of `FirstView` does not match the input type of `SecondView`. | ||
</details> | ||
|
||
#### **What's going on with this `startingArgs` and `passedArgs`?** | ||
|
||
<details> | ||
|
||
`startingArgs` are the [AnyWorkflow.PassedArgs](https://wwt.github.io/SwiftCurrent/Classes/AnyWorkflow/PassedArgs.html) handed to the first [FlowRepresentable](https://wwt.github.io/SwiftCurrent/Protocols/FlowRepresentable.html) in the workflow. These arguments are used to pass data and determine if the view should load. | ||
|
||
`passedArgs` are the [AnyWorkflow.PassedArgs](https://wwt.github.io/SwiftCurrent/Classes/AnyWorkflow/PassedArgs.html) coming from the last view in the workflow. `onFinish` is only called when the user has gone through all the screens in the `Workflow` by navigation or skipping. For this workflow, `passedArgs` is going to be the output of `FirstView` or `SecondView` depending on the email signature typed in `FirstView`. To extract the value, we unwrap the variable within the case of `.args()` as we expect this workflow to return some argument. | ||
</details> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
- [Why This Library?](https://github.com/wwt/SwiftCurrent/wiki/Why-This-Library%3F) | ||
- [Installation](https://github.com/wwt/SwiftCurrent/wiki/Installation) | ||
- [Getting Started with Storyboards](https://github.com/wwt/SwiftCurrent/wiki/getting-started) | ||
- [Getting Started with Programmatic UIKit](https://github.com/wwt/SwiftCurrent/wiki/getting-started-with-programmatic-uikit) | ||
- [Getting Started with Storyboards](https://github.com/wwt/SwiftCurrent/wiki/Getting-Started-with-Storyboards) | ||
- [Getting Started with Programmatic UIKit Views](https://github.com/wwt/SwiftCurrent/wiki/Getting-Started-with-Programmatic-UIKit-Views) | ||
- [[BETA] Getting Started with SwiftUI](https://github.com/wwt/SwiftCurrent/wiki/Getting-Started-with-SwiftUI) | ||
- [Developer Documentation](https://wwt.github.io/SwiftCurrent/index.html) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.