Early Access Release 1.0.3

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.


Site now redirects to SSL

We improved security to avoid MITM attacks on downloads