Flint

final public class Flint

This is the Flint class, with entry points for application-level convenience functions and metadata.

Your application must call Flint.quickSetup or Flint.setup at startup to bootstrap the Feature & Action declarations, to set up all the URL mappings and other conventions.

Failure to do so will usually result in a precondition failure in your app.

  • Call for the default setup of loggers, link creation, automatic logging of action start/end.

    • param group: The main group of your application features
    • param domains: The list of universal link domains your app supports. The first one will be used to create new universal links. (Domains vannot be extracted automatically by Flint)
    • param initialDebugLogLevel: The default log level for debug logging. Default if not specified is .debug
    • param initialProductionLogLevel: The default log level for production logging. Default if not specified is .info
    • param briefLogging: Set to true for logging with less verbosity (primarily dates)

    Declaration

    Swift

    public static func quickSetup(_ group: FeatureGroup.Type, domains: [String] = [], initialDebugLogLevel: LoggerLevel = .debug,
                                  initialProductionLogLevel: LoggerLevel = .off, briefLogging: Bool = true, configuration: ((_ dependencies: DependenciesConfig) -> Void)? = nil)
  • Call to set up your application features and Flint’s internal features.

    Use this only if you have manually configured your logging and action sessions.

    Declaration

    Swift

    public static func setup(_ group: FeatureGroup.Type, configuration: ((_ dependencies: DependenciesConfig) -> Void)? = nil)
  • Register the feature with Flint. Call this to register specific features if they are not already registered by way of being subfeatures of a group. Only call this if you have not passed this feature to setup or quickSetup.

    Registration of features at runtime is required because Swift does not provide runtime discovery of types conforming to a protocol, without using the Objective-C runtime upon which we do not want to depend, to be future proof. We need to know which types are features because:

    • We want to be able to show the info about all the features in debug UIs
    • Some apps will want to be able to show info about features in their user-facing UIs
    • We need to process the conventions on the types to know what actions they support
    • We need to process the conventions on the types to know what URL mappings they support, if any
    • We need to know if a feature is enabled currently, and to test for permissions and preconditions

    We can switch to lazy registration (processing of conventions etc.) at a later point to reduce startup overheads. However we will still need to know the types required to e.g. invoke an action on a feature via a URL, or continue an activity, or perform a Siri shortcut.

    If users only register some of their feature types, they would have to always remember to register all feature types that require URL mappings and/or have actions that support activity continuation. This is very error prone, and should be discouraged. It is better to minimize the overheads at the point of calling register and defer any processing where possible. Even in this case it is unlikely to be very profitable because you need to evaluate the conventions in order to know whether or not an Action or Feature is going to be required for URL or activity handling.

    Note

    Even with the Objective-C runtime, iterating (and hence forcing +load) on all Obj-C compatible classes is a slow process as there are thousands of them.

    Declaration

    Swift

    public static func register(_ feature: FeatureDefinition.Type)
  • Register a feature group with Flint. This will recursively register all the subfeatures. Only call this if you have not passed this group to setup or quickSetup.

    Registration of features at runtime is required because Swift does not provide runtime discovery of types conforming to a protocol, without using the Objective-C runtime upon which we do not want to depend, to be future proof. We need to know which types are features because:

    • We want to be able to show the info about all the features in debug UIs
    • Some apps will want to be able to show info about features in their user-facing UIs
    • We need to process the conventions on the types to know what actions they support
    • We need to process the conventions on the types to know what URL mappings they support, if any
    • We need to know if a feature is enabled currently, and to test for permissions and preconditions

    We can switch to lazy registration (processing of conventions etc.) at a later point to reduce startup overheads. However we will still need to know the types required to e.g. invoke an action on a feature via a URL, or continue an activity, or perform a Siri shortcut.

    If users only register some of their feature types, they would have to always remember to register all feature types that require URL mappings and/or have actions that support activity continuation. This is very error prone, and should be discouraged. It is better to minimize the overheads at the point of calling register and defer any processing where possible. Even in this case it is unlikely to be very profitable because you need to evaluate the conventions in order to know whether or not an Action or Feature is going to be required for URL or activity handling.

    Note

    Even with the Objective-C runtime, iterating (and hence forcing +load) on all Obj-C compatible classes is a slow process as there are thousands of them.

    Declaration

    Swift

    public static func register(group: FeatureGroup.Type)
  • Open the specified URL, dispatching the appropriately mapped action if it has been set up via a URLMapped Feature.

    Add this to your AppDelegate:

    func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
        let result: URLRoutingResult = Flint.open(url: url, with: presentationRouter)
        return result == .success
    }
    
    • param url: The URL that may point to an action in the app.
    • param presentationRouter: The object that will return the correct presenter for the router
    • return: The routing result indicating whether or not an action was found and performed

    Declaration

    Swift

    public static func open(url: URL, with presentationRouter: PresentationRouter) -> MappedActionResult
  • Call this to continue an NSUserActivity that may map to an Action in your application.

    Add this to your AppDelegate:

    Perform the action required to continue a user activity.
    

    func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool { return Flint.continueActivity(activity: userActivity, with: presentationRouter) == .success }

    - param activity: The activity pass to the application
    - param presentationRouter: The object that will return the correct presenter for the router
    - return: The routing result indicating whether or not an action was found and performed
    

    Declaration

    Swift

    public static func continueActivity(activity: NSUserActivity, with presentationRouter: PresentationRouter) -> MappedActionResult
  • Gather all logs, timelines and stacks into a single ZIP suitable for sharing.

    This will use DebugReporting to enumerate over all the DebugReportable objects in the app, asking each to generate their reports, and then it will zip all the contents into a single file.

    • return: A URL pointing to a Zip file containing the reports. You should delete this after generating it.

    Declaration

    Swift

    public static func gatherReportZip(options: Set<DebugReportOption>) -> URL