A modern framework for deeply integrated Swift apps

Delighting users means interacting with lots of APIs these days. We have to carefully request permissions, handle in-app purchases, track usage analytics, and deep-link into our apps through URLs, notifications or Siri. With Flint you add a small amount of code to describe the features and actions of your app so it can take care of those things for you. You spend the rest of your time working on what makes your app unique.

(It also makes debugging and testing easier)

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:

classDocumentManagementFeature:Feature{staticletdescription="Create, Open and Save documents"staticletopenDocument=action(DocumentOpenAction.self)staticfuncprepare(actions:FeatureActionsBuilder){actions.declare(openDocument)}}

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 combinattion of featuers 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:

finalclassDocumentOpenAction:Action{typealiasInputType=DocumentReftypealiasPresenterType=DocumentPresenterstaticvardescription="Open a document"staticfuncperform(withcontext:ActionContext<DocumentRef>,usingpresenter:DocumentPresenter,completion:Completion)->Completion.Status{presenter.openDocument(context.input)returncompletion.completedSync(.success)}}

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.

letpremiumSubscription=Product(name:"💎 Premium Subscription",description:"Unlock the Selfietron!",productID:"SUB0001")/// The Selfie feature requires an in-app purchase and camera/photos and location permissions.classSelfieFeature:ConditionalFeature{staticvardescription:String="Selfie Posting"staticfuncconstraints(requirements:FeatureConstraintsBuilder){requirements.userToggled(defaultValue:true)requirements.runtimeEnabled()requirements.purchase(PurchaseRequirement(premiumSubscription))requirements.permissions(.camera,.photos,.location(usage:.whenInUse))}staticletshowSelfieCapture=action(ShowSelfieCaptureAction.self)...}// In your view controller somewhere, check we can actually use Selfiesifletrequest=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

Activities

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

Analytics

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