import FlintCore
final class PhotoAttachmentsFeature: ConditionalFeature {
static var description: String = "Attach a photo to a document"
static func constraints(requirements: FeatureConstraintsBuilder) {
requirements.iOSOnly = 10
requirements.permissions(.photos, .camera)
requirements.purchase(InAppPurchases.unlockAttachments)
}
static let showPhotoSelection = action(ShowPhotoSelectionAction.self)
static let addSelectedPhoto = action(AddSelectedPhotoAction.self)
static func prepare(actions: FeatureActionsBuilder) {
actions.declare(showPhotoSelection)
actions.declare(addSelectedPhoto)
}
}
What does the framework do?
Once your code is using Features and Actions, Flint makes a bunch of platform integration and app infrastructure really easy; URL Mappings, Analytics, NSUserActivity for Handoff, Search and Siri Prediction, requesting system permissions, requiring in-app purchases and Siri Shortcuts are just some of the wonderful things you get for little effort. Often all you have to do is define a property on your Action
type.
If that wasn’t enough, the coding patterns make your code cleaner, reduce coupling, and shift your thinking to a product-centric approach.
Define your Features
Features in Flint conform to the Feature
protocol and follow some simple conventions:
class DocumentManagementFeature: Feature {
static let description = "Create, Open and Save documents"
// Bind an action to this feature
static let openDocument = action(DocumentOpenAction.self)
// Declare the action bindings
static func prepare(actions: FeatureActionsBuilder) {
actions.declare(openDocument)
}
}
Wire-up the Actions
Actions are the things that users can do with your features such as “Open a document”, “Close a document” or “Share a document”:
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 {
// Do the work
presenter.openDocument(context.input)
// Tell Flint we're done
return completion.completedSync(.success)
}
}
// Performing the action in your app looks like this...
DocumentManagementFeature.openDocument.perform(withInput: myDocument,
presenter: self)
Flint observes when your code performs any of these high level tasks, and will automatically trigger logic based on the value of the convention properties, such as logging an analytics event or registering an NSUserActivity
for the action so the user’s device can suggest the same action again in future.
Constraining when Features are available
Conditional Features support constraints, which can include specific platforms, OS versions, system permissions, in-app purchases and more:
let premiumSubscription = AutoRenewingSubscriptionProduct(name: "💎 Premium Subscription",
description: "Unlock the Selfietron!",
productID: "SUB0001")
class SelfieFeature: ConditionalFeature {
static let description: String = "Selfie Posting"
static func constraints(requirements: FeatureConstraintsBuilder) {
// Require a purchase
requirements.purchase(premiumSubscription)
// All these permissions are required
requirements.permissions(.camera,
.photos,
.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(withPresenter: self)
} else {
// Look at why it is not available, e.g. missing permissions (see docs)
}
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.
Next steps
Flint has a wealth of other capabilities, including a range of debug tools that allow you to browse all the features in your app, the current status of their constraints and even fake the status of in-app purchases, and even add bug report export functionality to your own apps that captures all the rich information the framework an gather to help you root cause problems quickly.
Head over to the Getting Started guide to learn how to install Flint into your app project and start adding features to your existing projects, or experiment with the FlintDemo iOS sample project.