We’ve tagged a new 1.0.3 early access release that you can build apps against. This is an important release that slightly changes to way action completion is handled as well as action outcome handling. These are source-breaking changes. However the changes are well worth it and tick off some of the long-standing tasks from the roadmap to 1.0 GM convergence.
So what’s changed? Mostly it’s a bunch of stuff cleaning up the Action
APIs for performing actions and handling completion.
Clearer Action
outcome values
The cases of ActionPerformOutcome
have changed to be easier to understand and use. Gone is the closeActionStack
argument that was a bit confusing.
Now your actions must be updated to return new values:
Before | After |
---|---|
.success(closeActionStack: false) |
.success |
.success(closeActionStack: true) |
.successWithFeatureTermination |
.failure(error: error, closeActionStack: false) |
.failure(error: error) |
.failure(error: error, closeActionStack: true) |
.failureWithFeatureTermination(error: error) |
You will need to search and replace in you code to update those. Also note that for failure
outcomes, the error
argument is no longer optional. You will also need to change how you return these outcomse — see the next section.
When you return these values, you are telling Flint whether your action signals that the feature to which it belongs is no longer being used by the user. Normally this is not the case, but certain actions such as “Close document” will often need to signify that their feature is finished with, and so the curren Action Stack for that feature can be disposed of. The Action Stack is used to view or capture a snapshot of what the user is currently “doing” right now in your app.
See #141
A new, safer completion mechanism for Action
In order to reduce bugs where actions forget to call completion
, the completion handler closure passed to your Action
implementations has been replaced by a new type Action.Completion
and the perform
function must now return a status from this instance obtained by calling completedSync
or willCompleteAsync
to indicate how your Action
will behave. This is implemented using the new CompletionRequirement
type which forces code to indicate whether or not it completed synchronously or will complete later asynchronously.
Calling completion is critical for Flint to perform its housekeeping around Timeline and ActionStack, and action dispatch observers such as the ActivitiesFeature
. For your existing actions, this means changing code from this style:
final class DocumentOpenAction: Action {
...
static func perform(with context: ActionContext<DocumentRef>,
using presenter: DocumentPresenter,
completion: @escaping (ActionPerformOutcome) -> ()) {
presenter.openDocument(documentRef)
completion(.success(closeActionStack:false))
}
}
To this newer style that forces you to return a value from the completion
instance:
final class DocumentOpenAction: Action {
...
static func perform(with context: ActionContext<DocumentRef>,
using presenter: DocumentPresenter,
completion: Completion) -> Completion.Status {
presenter.openDocument(documentRef)
return completion.completedSync(.success)
}
}
(note the nifty use of a typealias in Action
to alias Completion
to CompletionRequirement<ActionPerformOutcome>
. Nice Swifty stuff!)
This mechanism forces the developer to think about how their Action behaves and allows the caller (typically Flint itself) to adapt its behaviour based on whether or not the action has already completed synchronously. We use the compiler to force you to return a status that can only be obtained from the Completion
instance, so your code must always show the sync or async completion intention.
After this change we found a bug where FlintDemo was not calling completion, so we’re already improving things with this!
See #6
Easy access to context-specific loggers for Features from non-Action code
A new simple API has been added to get a context-specific logger from a given feature, for use with non-action related code e.g. services or other logic. Use developmentLogger(for:)
or productionLogger(for:)
on your feature types to get a logger.
This makes it easy for code inside your app or frameworks to participate in Feature-scoped logging.
See #107
Other small things
We changed the argument name of URLMapped.urlMappings()
to have a single routes
argument. This works better and looks nicer with Swift fix-it(s) in Xcode. See #155
The action detail screen in FlintUI’s feature browser was not correctly showing the URL mappings of the action. See #182
We fixed some crashes around permissions checking. See #174 and #172
We also fixed a crafty concurrent access race in Action Stacks. Yay for Xcode’s Thread Sanitizer!
Hopefully you will enjoy these improvements — get in touch if you want to ask any questions or discuss anything.