A modern framework for deeply integrated Swift apps

Apps have to carefully request system permissions, restrict access to features that require in-app purchases, track usage analytics, and support deep-linking from URLs, notifications or Siri Shortcuts. By adding a small amount of code to describe the features and actions of your app, Flint takes care of those things for you


Thinking differently.

Flint is an application framework written in Swift that helps you build apps out of Features and Actions using ideas from Feature Driven Development. It provides the plumbing behind the scenes to integrate with platform APIs. You continue to use your preferred UI libraries and patterns — Flint does not force you into MVVM or a particular UI model. The framework operates at a lower level in your code to handle action dispatch and the various entry points to modern apps. You call into your actions which call back your presenter. This could be anything: a View Controller, a flow controller or coordinator for example.

By adding information about actual features to apps you gain the ability to control access to features in a clean and safe way. 

Features in Flint conform to the Feature protocol and follow some simple conventions:

class DocumentManagementFeature: Feature {
    static let description = "Create, Open and Save documents"

    static let openDocument = action(DocumentOpenAction.self)

    static func prepare(actions: FeatureActionsBuilder) {

Actions are high level tasks the user can perform with your app such as “Open a document”, “Close a document” or “Share a document”. Through the combination of features and actions, you gain context about what is happening at a given point in time.

In Flint these actions are types conforming to Action that are bound to features as in the above example. When performed, an Action receives a context (also containing the input) and a presenter. The type of the input and presenter are determined by you, using type aliases:

final class DocumentOpenAction: UIAction {
    typealias InputType = DocumentRef
    typealias PresenterType = DocumentPresenter

    static var description = "Open a document"

    static func perform(context: ActionContext<DocumentRef>,
                        presenter: DocumentPresenter,
                        completion: Completion) -> Completion.Status {
        return completion.completedSync(.success)

// Performing the action...
DocumentManagementFeature.openDocument.perform(input: myDocument, 
                                               presenter: self)

Once you define actions, Flint can observe when your code performs any of these high level tasks. This unlocks many behaviours including automatic NSUserActivity support for Handoff, Spotlight and Siri Suggestions, analytics tracking and improved debug logging.

In addition, because Flint also knows how to invoke your actions for a given input, it can handle all the different app entry points for you too, including app or deep-linking URLs and continued activities including Handoff, Spotlight and Siri Suggestions. Read more in the Features & Actions guide.

What about features that require in-app purchases or certain system permissions? Conditional Features support such constraints, which can include specific platforms, OS versions, system permissions, in-app purchases and more. Thanks to Swift your code can’t perform actions of conditional features unless you also handle the case where the feature is not currently available.

let premiumSubscription = AutoRenewingSubscriptionProduct(name: "💎 Premium Subscription",
                                                          description: "Unlock the Selfietron!",
                                                          productID: "SUB0001")

/// The Selfie feature requires an in-app purchase and camera/photos and location permissions.
class SelfieFeature: ConditionalFeature {
    static let description: String = "Selfie Posting"

    static func constraints(requirements: FeatureConstraintsBuilder) {
      requirements.userToggled(defaultValue: true)


                               .location(usage: .whenInUse))

    static let showSelfieCapture = action(ShowSelfieCaptureAction.self)


// In your view controller somewhere, check we can actually use Selfies
if let request = SelfieFeature.showSelfieCapture.request() {
    request.perform(presenter: self)
} else {
    // Look at why it is not available, e.g. missing permissions

Features that require multiple permissions or one of many purchase options are easily accommodated, and Flint will help you build a first class permissions onboarding UI to maximise the number of users that can use your feature without you having to worry about how to manage this yourself.

Subscribe for Flint articles & news

Find out more

Getting Started

Learn how to add the Flint framework to your apps

Features and Actions

Defining your features and performing actions on them

Conditional Features

Prevent use of features unless constraints are met — system permissions, OS versions, and in-app purchases

URL Routes

Map incoming app or deep-linking URLs to your actions


Let Flint automaticaly publish NSUserActivity instances for the actions you want to expose


Use the framework to tell your analytics backend about the actions your users perform

Popular Articles

Didn't find an answer to your question?

Get help from the contributors and community in our Slack

Go to the Flint Slack