Protocols

The following protocols are available globally.

  • Actions that can be performed conform to this protocol to define their inputs, presenter and logic.

    Actions are statically defined to avoid the mistake of storing state with them. Any state belongs in the input passed when performing the action.

    Many of these static properties have default implementations provided by a protocol extension.

    Note

    Action implementations must be final due to Swift extension requirements.

    The same action can be reused in different features, so they receive all context they need when executing.

    Note that actions have their own analytics ID defined statically.

    See more

    Declaration

    Swift

    public protocol Action
  • The protocol to which Inputs must conform to be logged usefully by Flint’s logging, Timeline, Action Stacks etc.

    Flint requires a human readable description of all inputs to use in logs and debug UI, as well as a structured data representation for use in machine-readable outputs.

    We cannot rely on CustomStringConvertible and CustomDebugStringConvertible for this as the developer may not control the contents of these, and the semantics are not rigid enough for our use.

    See more

    Declaration

    Swift

    public protocol FlintLoggable
  • An action that performs no work except successfully completing with feature termination.

    Actions adopting this protocol will not need to provide a perform implementation.

    Conforming to this protocol is useful for done type Actions that you want to participate in standard Action patterns, but do not actually perform any code.

    See more

    Declaration

    Swift

    public protocol TerminatingAction : Action
  • Actions that are performed in the main session and on the main dispatch queue should conform to this This is typealiased for Flint 1.0 source compatibility, as UIKit in iOS 13 shadows this.

    See more

    Declaration

    Swift

    public protocol UIAction : Action
  • Action input types can conform to this protocol to automatically supply the userInfo for NSUserActivity with the Activities feature.

    See more

    Declaration

    Swift

    public protocol ActivityCodable
  • Conform to this protocol to wire up your chosen Analytics service to receive events when Action(s) that have an analyticsID set are performed.

    Your implementation will receive the feature and action information, and the analytics properties returned by your Action implementations’ analyticsAttributes(for:) function.

    Read the analyticsID for the event from the action passeds to the functions.

    See

    ConsoleAnalyticsProvider for a trivial example implementation.
    See more

    Declaration

    Swift

    public protocol AnalyticsProvider
  • You can customise the checking of purchased and user toggled conditional features by implementing this protocol.

    Implementations must be safe to call from any thread or queue, so that callers testing isAvailable on a feature do not need to be concerned about this even if running on a background queue or an ActionSession that is not on the main queue.

    Implementations must also take care to examine the ancestors of features to ensure the correct result is returned from isAvailable.

    See more

    Declaration

    Swift

    public protocol AvailabilityChecker
  • Features that are not guaranteed to be available all the time must conform to this protocol.

    You implement a conditional feature like so:

    public class TimelineFeature: ConditionalFeature {
        /// Set the availability to .purchasRequired, .runtimeEvaluated or .userToggled as appropriate
        public static var availability: FeatureAvailability = .runtimeEvaluated
    
        public static var description: String = "Maintains an in-memory timeline of actions for debugging and reporting"
    
        /// If availability is `runtimeEvaluated`, you must make `isAvailable` return whether or not it is available.
        /// Otherwise do not define a property for it and the `DefaultAvailabilityChecker` will be used to work out
        /// the correct value of this by calling into the `UserDefaultsFeatureToggles` or `PurchaseValidator`.
        public static var isAvailable: Bool? = true
    
        /// If using `runtimeEvaluated` you can use this function to set `isAvailable` at startup based on
        /// some other condition. Beware of dependency on other features and non-determinate initialising sequence.
        public static func prepare(actions: FeatureActionsBuilder) {
            if isAvailable == true {
                // Tracks the user's history of actions performed
                Flint.dispatcher.add(observer: TimelineDispatchObserver.instance)
            }
        }
    }
    

    Apps must call request to test if the action is available, and then call perform with the resulting request instance.

    See more

    Declaration

    Swift

    public protocol ConditionalFeature : ConditionalFeatureDefinition
  • A feature that is not guaranteed to always be available must conform to ConditionalFeatureDefinition, so that we can ensure the caller always verifies their availability before performing them.

    This type exists separately from ConditionalFeature so that other types of conditional feature can exist (e.g. a future ConditionalFeatureGroup).

    We also have this type to enable us to reference conditional features without generic constraint issues that arise from the Self requirement of ConditionalFeature. This allows us to define helper functions that operate on conditional features without having to deal with those problems.

    Note

    Accesses to any properties that may change at runtime, e.g. isAvailable must only occur on the main thread.
    See more

    Declaration

    Swift

    public protocol ConditionalFeatureDefinition : FeatureDefinition
  • The interface to the constraints evaluator component.

    Implementations are responsible for evaluating all the constraints and returning information about those that are satisfied or not.

    See more

    Declaration

    Swift

    public protocol ConstraintsEvaluator : AnyObject
  • All feature constraint types must conform to this protocol.

    This protocol lets us get at basic information about any kind of constraint enum.

    See more

    Declaration

    Swift

    public protocol FeatureConstraint : Hashable
  • An authorisation controller is used to request a set of system permissions.

    Using ConditionalFeature.permissionAuthorisationController(using:) you can get an instance of a controller in your app for any conditional feature you have. You then call begin to start the flow.

    The flow can be multi-step if your feature has multiple permissions that are not yet authorised, and the coordinator object you pass to ConditionalFeature.permissionAuthorisationController gives you the opportunity to update your UI at each step, giving the user the ability to skip or cancel the process.

    See

    ConditionalFeature.permissionAuthorisationController
    See more

    Declaration

    Swift

    public protocol AuthorisationController
  • The interface to the coordinator that apps must implement if they want to hook in to the permission authorisation controller flow.

    Apps can use this interface to present custom UI before the authorisation flow starts, and update this before and after each permission is requested, with control over what happens next.

    For example you may have non-modal UI that shows the user what the camera will be used for, and it contains a SKIP button. If they tap this, you call the completion handler passing .skip and the controller will move on to the next permission, or finish the flow if there are no more permissions required.

    See

    see ConditionalFeature.permissionAuthorisationController(using:) and AuthorisationController
    See more

    Declaration

    Swift

    public protocol PermissionAuthorisationCoordinator
  • The protocol for observers of the ActionDispatcher.

    Dispatch observers are called asynchronously on an arbitrary queue.

    Note

    Because of the user of generics, this cannot be @objc which is required if we want to use ObserverSet because… https://bugs.swift.org/browse/SR-55
    See more

    Declaration

    Swift

    public protocol ActionDispatchObserver
  • An action dispatcher is used to perform actions and perform housekeeping to enable tracking of which features are active at a given time, hooking into logging and analytics etc.

    Dispatchers are expected to perform actions synchronously.

    If you wish to use your own implementation you must assign it to Flint.dispatcher at startup.

    See more

    Declaration

    Swift

    public protocol ActionDispatcher
  • Classes conforming to this protocol can provide debug reports when Flint.gatherReportZip is called.

    Use in apps to expose app-specific debug information that may be useful for troubleshooting.

    See

    DebugReporting.add(:) for registering your own reportable objet to be included.
    See more

    Declaration

    Swift

    public protocol DebugReportable : AnyObject
  • Classes conforming to Feature represent an always-available feature that can perform actions.

    To define such a feature, conform to this protocol and declare static properties for the actions it supports, using the action(Action.Type) helper function. You then override prepare and use the actions builder to declare or publish those actions:

    class DocumentManagementFeature: Feature, URLMapped {
        static let description = "Create, Open and Save documents"
    
        static let createNew = action(DocumentCreateAction.self)
        static let openDocument = action(DocumentOpenAction.self)
        static let closeDocument = action(DocumentCloseAction.self)
        static let saveDocument = action(DocumentSaveAction.self)
    
        static func prepare(actions: FeatureActionsBuilder) {
            actions.declare(createNew)
            actions.declare(openDocument)
            actions.declare(closeDocument)
            actions.declare(saveDocument)
        }
    
        static func urlMappings(routes: URLMappingsBuilder) {
            routes.send("create", to: createNew)
            routes.send("open", to: openDocument)
        }
    }
    

    You can optionally override the default implementations of name and description of you want to change how the feature is presented in logging and debug UI.

    -note: This type exists simply to allow protocol extenions on this type that are not to be inherited by ConditionalFeatureDefinition, e.g. the differing action() binding functions.

    See

    ConditionalFeature for features that can be enabled or disabled based on some condition.
    See more

    Declaration

    Swift

    public protocol Feature : FeatureDefinition
  • The actions builder protocol defines the domain-specific-language used to declare the actions available on a feature.

    See more

    Declaration

    Swift

    public protocol FeatureActionsBuilder
  • A grouping (nesting) of features.

    Used to apply some hierarchical struture to feature definitions internally, for logging and debugging user activities.

    final class AppFeatures: FeatureGroup {
        static var description = "Demo app features"
    
        static var subfeatures: [FeatureDefinition.Type] = [
            DocumentManagementFeature.self,
            DocumentSharingFeature.self
        ]
    }
    
    See more

    Declaration

    Swift

    public protocol FeatureGroup : FeatureDefinition
  • An implementation of FocusSelection is used to control what logging and debug info is currently being produced. When the focus selection is empty (reset), all the normal logging levels apply and there is no filtering.

    Focusing on one or more features results in only logging related to the focused items being produced, automatically setting debug log level for those features and topic paths.

    Topic Paths are used to allow control of non-Feature subsystems that have opted in to Contextual Logging. Feature identifiers are converted to Topic Paths so we have one common concept for logging hierarchy.

    See

    TopicPath
    See more

    Declaration

    Swift

    public protocol FocusSelection
  • The is the high level logger interface that uses the same context for all events, for passing to actions and other subsystems where the user’s feature context is known.

    You obtain one of these from a ContextualLoggerFactory, although typically Flint will automatically provide these to your Action(s) in the ActionContext.

    See more

    Declaration

    Swift

    public protocol ContextSpecificLogger : AnyObject
  • The low level interface for getting a logger.

    This is the application-facing interface for logging, which will filter and if necessary prepare log entries for the underlying logging implementation.

    Note

    Flint supports log levels per topic path (e.g. by Feature) even without setting one or more focused features.

    See more

    Declaration

    Swift

    public protocol ContextualLoggerFactory : AnyObject
  • An implementation of logging output. This is the protocol to implement to output logs to your chosen logging system.

    Note

    Log events for excluded levels will never be passed in to the implementation. Interactions with your logging system’s own log level may need close attention. The expectation at the app level is that if something passes the log filtering of Flint that it will appear in the logs. This is particularly important for Focus where Flint will flip the effective log level to DEBUG to allow all loggic for the focused topics through. If your logging subsystem is set to log level INFO then these log events will not be logged.
    See more

    Declaration

    Swift

    public protocol LoggerOutput
  • Implement this protocol to verify whether a specific purchase has been paid for.

    You may implement this against whatever receipt system you use, but typically this is StoreKit.

    Flint will call this multiple times for each productID that is required in a PurchaseRequirement, so implementations only need to respond to single product requests.

    See more

    Declaration

    Swift

    public protocol PurchaseTracker
  • The protocol for observers of changes to product purchase status

    Note

    @objc only because of SR-55.

    See

    PurchaseValidator
    See more

    Declaration

    Swift

    @objc
    public protocol PurchaseTrackerObserver
  • Applications must implement this protocol to provide UI for actions that are invoked for URLs or deep linking.

    The implementation is responsible for providing an instance of the right kind of presenter for a given action.

    How this works is up to your UI platform and your application. On UIKit for example you may choose to return .appCancelled if the user has a modal view controller presented currently, or unsaved data in an incomplete workflow. For the case where the current UI state can present the UI for the specified action, the view controllers required must be created in the correct configuration and the final presenter instance returned with a value of .appReady

    See more

    Declaration

    Swift

    public protocol PresentationRouter
  • The protocol for decoding an input value from URL route parameters. Conform your input types to this to to enable execution of actions with your input type when incoming URLs are parsed.

    See more

    Declaration

    Swift

    public protocol RouteParametersDecodable
  • The protocol for encoding an input value from URL route parameters. Conform your input types to this to to enable creation of links to actions with your input type.

    See more

    Declaration

    Swift

    public protocol RouteParametersEncodable
  • Features must adopt this protocol if they support URL mappings, and define the routes that map from URLs to actions.

    There is support for multiple custom app URL schemes and multiple associated domains, URL wildcards and named variables in the path components.

    See more

    Declaration

    Swift

    public protocol URLMapped
  • The interface to a URLPattern that can be matched to an incoming path and generate reverse paths

    See more

    Declaration

    Swift

    public protocol URLPattern
  • Actions that implement a Siri Intent must conform to this protocol.

    It will ensure that they use a non-main queue (because Intent extensions are called on a background thread) and use an Intent-specific session for log and timeline scoping.

    See more

    Declaration

    Swift

    @available(iOS 12, *)
    public protocol IntentBackgroundAction : Action
  • Adopt this protocol when implementing an action that fulfills a Siri Intent via an Intent Extension

    Note

    See https://bugs.swift.org/browse/SR-10831 for why this is so ugly re: defaulting the associated types. The type inference should see the constraints fully satisfy the associated types but it doesn’t, so we have to also specify the associatedtype so that conforming types can compile
    See more

    Declaration

    Swift

    @available(iOS 12, *)
    public protocol IntentAction : IntentBackgroundAction