Classes
The following classes are available globally.
-
The context in which an action executes. Contains the initial state and contextual logger.
The context can be passed forward, or a new instance derived with new state, so that e.g. a subsystem can be passed the logger and state information as a single opaque value, without passing forward the entire action request
See moreDeclaration
Swift
public class ActionContext<InputType> where InputType : FlintLoggable
-
A type that tracks metadata about a single action, including its URL Mappings.
This is used for debug UIs.
See
FlintUI.FeatureBrowserFeature
Declaration
Swift
public class ActionMetadata
-
An action request encapsulates the information required to perform a single action and perform various action auditing.
This needs class semantics for identity.
See moreDeclaration
Swift
public class ActionRequest<FeatureType, ActionType> : CustomDebugStringConvertible where FeatureType : FeatureDefinition, ActionType : Action
-
The is the internal Flint feature for automatic
NSUserActivity
publishing and handling.This provides actions used internally to publish and handle
See moreNSUserActivity
for actions that opt-in to this by setting theiractivityEligibility
property.Declaration
Swift
public final class ActivitiesFeature : ConditionalFeature
-
An
See moreActionDispatchObserver
implementation that will use thepublishCurrentActionActivity
action ofActivitiesFeature
to automatically publish anNSUserActivity
for what the user is currently doing.Declaration
Swift
public class ActivityActionDispatchObserver : ActionDispatchObserver
-
This is a builder for creating Metadata instances without requiring a mutable type or ugly initializer permutation.
See
ActivityMetadata.build
for the function that creates this builder.Declaration
Swift
public class ActivityMetadataBuilder
-
The Analytics Feature sends analytics events for qualifying actions to your analytics provider.
See moreDeclaration
Swift
final public class AnalyticsFeature : ConditionalFeature
-
This class observes action execution and passes the analytics data to your analytics subsystem for any Actions that support analytics.
This allows internal or third party frameworks that use Flint to expose functionality that you also track with analytics, without them directly linking to the analytics package.
To use Analytics reporting, you need to make sure AnalyticsFeature.isEnabled is set to true, and you need to set an implementation of
AnalyticsProvider
to theAnalyticsFeature.provider
property before Flintsetup
is called. By default this includes just a default console analytics provider.AnalyticsFeature.provider = MyGoogleAnalyticsProvider()
See
AnalyticsProvider
for the protocol to conform to, to wire up your actual analytics service such as Mixpanel, Google Analytics or preferably, your own back end.Declaration
Swift
public class AnalyticsReporting : ActionDispatchObserver
-
A trivial
AnalyticsProvider
implementation that simply outputs analytics to the console for debugging.You can use this in developer builds to ensure all your expected analytics analytics are being produced.
See moreDeclaration
Swift
public class ConsoleAnalyticsProvider : AnalyticsProvider
-
The standard feature availability checker that supports the user toggling features (in User Defaults) that permit this, as well as purchase-based toggling. It caches the results in order to avoid walking the feature graph every time there is a check.
To customise behaviour of user toggling, implement
UserFeatureToggles
and pass it in to an instance of this class.To customise behaviour of purchase verification, implement
PurchaseValidator
and pass it in to an instance of this class.This class implements the
See morePurchaseRequirement
logic to test if they are all met for features that require purchases.Declaration
Swift
public class DefaultAvailabilityChecker : AvailabilityChecker
-
A simple convenience class that can be used for managing user-toggled features using UserDefaults, with the
See moreDefaultAvailabilityChecker
Declaration
Swift
public class UserDefaultsFeatureToggles : UserFeatureToggles
-
The default container for feature constraint evaluation results.
See moreDeclaration
Swift
public class DefaultFeatureConstraintEvaluationResults<ConstraintType> : FeatureConstraintEvaluationResults where ConstraintType : FeatureConstraint
-
The standard implementation of the constraints builder, providing the basic DSL functions required.
See
FeatureConstraintsBuilder
for the syntactic sugar applied to all implementations via extensionsDeclaration
Swift
public class DefaultFeatureConstraintsBuilder : FeatureConstraintsBuilder
-
This is the implementation of the constraints evaluator.
It is threadsafe in that you may call this from any thread.
See moreDeclaration
Swift
public class DefaultFeatureConstraintsEvaluator : ConstraintsEvaluator
-
The implementation of the system permission checker.
This registers and verifies the approprite adapters and uses them to check the status of all the permissions required by a feature.
!!! TODO: Add sanity check for missing Info.plist usage descriptions?
See moreDeclaration
Swift
public class DefaultPermissionChecker : SystemPermissionChecker, CustomDebugStringConvertible
-
The precondition evaluator for purchases requirements.
See moreDeclaration
Swift
public class PurchasePreconditionEvaluator : FeaturePreconditionConstraintEvaluator
-
The precondition evaluator that tests if the feature’s
See moreisEnabled
property istrue
Declaration
Swift
public class RuntimePreconditionEvaluator : FeaturePreconditionConstraintEvaluator
-
The precondition evaluator that checks the
See moreUserFeatureToggles
implementation to see if the given feature is currently enabledDeclaration
Swift
public class UserTogglePreconditionEvaluator : FeaturePreconditionConstraintEvaluator
-
The default dispatcher implementation that provides the ability to observe when actions are performed, across sessions.
This will attempt to detect if the caller is on the queue the action expects, and avoid crashing with a
DispatchQueue.sync
call when already on that queue. If the current queue is not the action’s queue, it will use aDispatchQueue.sync
.The goal is that if the app is on the main queue, call
perform
and the dispatcher than finds the action expects themain
queue, that no queue or async thread hops occur. This prevents aslushy
UI that is always updating asynchronously, and means the caller does not have to worry about the queue they are on, and nor does theAction
.However, it is important to note that this mechanism (using setSpecific/getSpecific) will not currently work if the Action’s queue uses a
See moretarget
queue.Declaration
Swift
public class DefaultActionDispatcher : ActionDispatcher
-
This is a simple action dispatch observer that will log the start and end of every action execution.
To add this to aid debugging, just add the following code:
See moreFlint.dispatcher.add(observer: ActionLoggingDispatchObserver.instance)
Declaration
Swift
public class ActionLoggingDispatchObserver : ActionDispatchObserver
-
An ActionSession is used to group a bunch of Action invocations, ensure they are invoked on the expected queue, and track the Action Stacks that result from performing actions.
The session used for an action invocation is recorded in Flint timelines and logs to aid in debugging.
The default
ActionSession.main
session provided is what your UI will use most of the time. However if your application supports multiple concurrent windows or documents you may wish to create more so that you can see timeline and log events broken down per window or document, e.g. with the session name equal to the document name or a symbolic equivalent of it for privacy. This way you can see what the user is doing in a multi-context environment.Furthermore, if your application performs background tasks you should consider creation a session for these.
The lifetime of an ActionStack can be tracked in logging and analytics and tied to a specific activity session, and is demarcated by the first use of an action from a feature, and the action that indicates termination of the current
feature
.Threading
A session can be accessed from any queue or thread. The
ActionSession.perform
method can be called directly or via theStaticActionBinding
/VerifiedActionBinding
convenienceperform
methods without knowing whether the queue is correct for the action.Actions can select which queue they will be called to
perform
on, via theirqueue
property. This is always the queue they will execute on, and may be entirely different from the session’s queue.This mechanism guarantees that code calling into an
ActionSession
does not need to care about the queue an Action expects, and Actions do not need to care about the queue they are called on, thus eliminating excessive thread hops (AKAmmm, just DispatchQueue.async it
). This reducesslushiness
and lag in UIs, and makes it easier to reason about the code.The dispatcher will ensure that the Actions are called synchronously on their desired queue, even if that is the same as the current queue. It will also make sure that they call their completion handler on the completion requirement’s
callerQueue
, without excessive queue hops so that if the caller is already on the correct thread, there is no async dispatch required.!!! TODO: Extract protocol for easier testing
See moreDeclaration
Swift
public class ActionSession : CustomDebugStringConvertible
-
An Action Stack is a trail of actions a user has performed from a specific Feature, with sub-stacks created when the user then uses an action from another feature, so each stack can represent a graph of actions broken down by feature.
Certain actions will
Close
their stack, e.g. aClose
option on a document editing feature. Some stacks may never be closed however, say aDrawingFeature
that allows use of many drawing tools. There is no clear end to that except closing the document, an operation on a different feature.!!! TODO: Work out what this means for sub-stacks. We want to retain information about what was done in other features, in amongst the current stack’s features, but when the stack closes we don’t want to lose that history if there was not a
closing
action of the sub stack. Some sub-stacks should be implicitly discarded however - e.g. drawing functions.We need reference semantics here because we have parent relationships and navigate the graph.
!!! TODO: Use LIFOQueue to limit to the number of past items held to avoid blowing/leaking memory over time
See moreDeclaration
Swift
public class ActionStack : CustomDebugStringConvertible
-
This tracker maintains the list of active Action Stacks across all ActionSession(s).
It is responsible for vending new stacks when required, or existing ones that are not closed.
See moreDeclaration
Swift
public class ActionStackTracker : DebugReportable
-
The Action Stack Feature tracks actions the user performs, as a
threaded
set of stacks.Set
See moreActionStacksFeature.isEnabled = true
to turn this feature onDeclaration
Swift
final public class ActionStacksFeature : ConditionalFeature
-
This is the Flint class, with entry points for application-level convenience functions and metadata.
Your application must call
Flint.quickSetup
orFlint.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.
See moreDeclaration
Swift
final public class Flint
-
This class acts as a registry of information about your App, for Flint to use.
Primarily this is used for access to information about the custom URL schemes and universal link domains your app supports.
Note
Flint cannot currently extract your supported universal link domains as these are only stored in your entitlements file. The custom URL schemes are listed in yourInfo.plist
so for most cases Flint can extract these.Declaration
Swift
final public class FlintAppInfo
-
The set of features provided by Flint itself.
These are Used to scope and filter logging of Flint itself, and to allow you to disable features of Flint that you do not wish to use.
See moreDeclaration
Swift
public final class FlintFeatures : FeatureGroup
-
Internal dependencies for Flint usage.
See moreDeclaration
Swift
public final class FlintInternal
-
A class for managing the debug reporting options of Flint.
With this class you can generate a debug report ZIP containing all the reports from various subsystems in Flint and also your app.
Flint’s internal features are automatically registered with DebugReporting, but if you need to add any other data to debug reports you can do so by creating your own object that conforms to
See moreDebugReportable
and register it here withDebugReporting.add(yourReportableThing)
.Declaration
Swift
public class DebugReporting
-
Metadata describing a single feature.
The
Flint
object makes this metadata available for runtime examination of the Features and actions available.The FlintUI
See moreFeatureBrowserFeature
takes advantage of this to provide a hierarchical UI to look at the graph of features and actions defined in the app.Declaration
Swift
public class FeatureMetadata : Hashable
-
The Focus feature provides realtime filtered logging based on Features and Actions.
Focus is enabled by default. Use the
See morefocus
action to add features to the focus area at runtime.Declaration
Swift
final public class FocusFeature : ConditionalFeature
-
This is a logger output implementation that captures log events in a buffer of limited length, for use in reporting and UI.
This will log whatever is coming out of the logging subsystem, so if Focus is enabled, it will capture only items that are in the focus area.
See
FocusLogDataAccessFeature
which provides realtime access to the data within this logging buffer.Declaration
Swift
public class FocusLogging : LoggerOutput
-
An implementation of
LoggerOutput
that aggregates multipleLoggerOutput
. Use this if you need your logging to go to multiple destinations.Must only be called from a single queue/thread.
See moreDeclaration
Swift
public class AggregatingLoggerOutput : LoggerOutput
-
A trivial
See moreContextualLogger
implementation that retains the context but defers to aContextualLoggerTarget
for the actual filtering and log output.Declaration
Swift
public class DefaultContextSpecificLogger : ContextSpecificLogger
-
The default Focus-aware filtering logger factory.
This creates contextual loggers that support Focus to restrict logging to specific features at runtime.
Note that Flint supports log levels per topic path (e.g. by Feature) even without setting one or more focused features.
This means you can run all your subsystems at
See moreinfo
level but turn your app loggic todebug
for example.Declaration
Swift
public class DefaultLoggerFactory : ContextualLoggerFactory, DebugReportable
-
Logging output to persistent files that can be archived using Flint’s report gathering.
Note
Currently does not support maximum log sizes or log file rotation.Declaration
Swift
public class FileLoggerOutput : LoggerOutput
-
Log file naming strategy that uses a prefix and current date
See moreDeclaration
Swift
public class TimestampLogFileNamingStrategy : LogFileNamingStrategy
-
The contextual logger target implementation used by default, to support the Focus feature of Flint.
This will use the current
FocusSelection
to work out whether or not Focus is in effect, and if it is whether or not events should be logged.When Focus is active, it will also drop the effective log level to
debug
so that everything related to your focused areas is output.Then focus is not active, a standed log level threshold is applied.
See moreDeclaration
Swift
public class FocusContextualLoggerTarget : ContextualLoggerTarget
-
A single log event.
This includes high level context information so that logs can identify more accurately what items relate to.
In the case of
See moreFeature
based apps, this means you can tell all the log activity that relates to a specific feature just by looking at the logs. Even if it comes from different subsystems.Declaration
Swift
public class LogEvent : UniquelyIdentifiable
-
A LoggerOutput implementation that sends log events to the system’s OSLog.
The app bundle ID plus action session name are used as the subsystem, e.g:
co.montanafloss.demo.main
…and the
category
is set to the topic path (AKA feature + action path):
See moreAppFeatures/DocumentEditing/#Save
Declaration
Swift
public class OSLogOutput : LoggerOutput
-
A trivial logger that uses Swift
See moreprint
to stdout. This is not very useful except for debugging without a dependency on another logging framework, as used in Flint demo projects.Declaration
Swift
public class PrintLoggerImplementation : LoggerOutput
-
The default verbose formatter for log events.
Includes an optional prefix for each line, and the time in HH:mm:ss.SSS format.
See moreDeclaration
Swift
public class VerboseLogEventFormatter : LogEventFormattingStrategy
-
A base type for products that represent auto-renewing subscriptions
See moreDeclaration
Swift
open class AutoRenewingSubscriptionProduct : SubscriptionProduct
-
A purchase tracker that allows manually setting of purchase status.
This can be used standalone as a fake in-memory purchase tracker, or to proxy another purchase tracker implementation so that you can provide overrides at runtime for easier testing.
On iOS you can use the FlintUI
PurchaseBrowserFeature
to show a simple UI in your app that will let you view the status of purchases, and if this tracker is used, override the purchase status at runtime for testing.See
seePurchaseBrowserFeature
See
seeStoreKitPurchaseTracker
Declaration
Swift
public class DebugPurchaseTracker : PurchaseTracker, PurchaseTrackerObserver
-
A base class for purchaseable products that do not have a quantity.
Declaration
Swift
open class NoQuantityProduct : Product
-
A base type for products that represent non-consumable products.
See moreDeclaration
Swift
open class NonConsumableProduct : NoQuantityProduct
-
A base type for products that represent non-renewing subscriptions
See moreDeclaration
Swift
open class NonRenewingSubscriptionProduct : SubscriptionProduct
-
This type represents information about a product that can be purchased in your app, for use in constraining features to specific products. This is not intended to implement a store client for displaying products and purchasing, but you may extend the types to do this.
This is used by the
purchase
conditional feature constraint, allowing you to bind Features to one or more Product, so that if the product is purchased, a group of features can become available.Note
The name and description are primarily used for local debugging. You can use them for your purchase UI in your app but you will need to consider loading the strings for display from a strings bundle using the value of
name
anddescription
as keys. For StoreKit usage, you need to retrieve the localized price from the App Store. You could do this with a subclass that lazily loads the prices when required.Note
We use class semantics here so that the app can subclass it to include additional properties as required for the purchasing mechanism they use.
Declaration
Swift
open class Product : Hashable, CustomDebugStringConvertible
-
Use a
PurchaseRequirement
to express the rules about what purchased products enable your Feature(s).You can express complex rules about how your Features are enabled using a graph of requirements. Each Feature can have multiple purchase requirements (combined with AND), but one requirement can match one or all of a list of product IDs, as well as having dependencies on other requirements.
With this you can express the following kinds of rules:
- Feature X is available if Product A is purchased
- Feature X is available if Product A OR Product B OR Product C is purchased
- Feature X is available if Product A AND Product B AND Product C is purchased
- Feature X is available if Product A AND (Product B OR Product C) is purchased
- Feature X is available if (Product A OR Product B) AND ((Product B OR Product C) AND PRODUCT D) is purchased
- Feature X is available if (Product A OR Product B) AND ((Product B OR Product C) AND PRODUCT D AND PRODUCT E) is purchased
…and so on. This allows you to map Feature availability to a range of different product pricing strategies and relationships, such as
See moreBasic
level of subscription plus aFounder
IAP that maybe offered to unlock all features in future for a one-off purchase, provided they still have a basic subscription.Declaration
Swift
public class PurchaseRequirement : Hashable, Equatable, CustomStringConvertible
-
A basic StoreKit In-App Purchase checker that uses only the payment queue and local storage to cache the list of purchase statuses. It does not validate receipts.
The local storage is unprotected if the user unlocks the device, and as such may be subject to relatively easy editing by the determined cheapskate user to unlock features.
Note
⚠️⚠️⚠️ Do not use this implementation if you insist on cryprographically verifying purchases.Note
⚠️⚠️⚠️ It is our view that we should rely on the security of Apple’s platform and not be overly concerned with users performing hacks and workarounds. People that go to the effort of jailbreaking, re-signing apps or applying other patching or data editing mechanisms are unlikely to have paid you any money anyway.If this isn’t good enough for you, you will need to add your own app-specific logic to verify this so there isn’t a single point of verification, and to check receipts. You may not want to use Flint for purchase verification at all if it transpires that the Swift call sites for conditional requests are easily circumvented.
Note
In-App Purchases APIs are not available on watchOS as of watchOS 5. Any Feature that requires a purchase will not be enabled on watchOS.Declaration
Swift
@available(iOS 3, tvOS 9, OSX 10.7, *) @objc open class StoreKitPurchaseTracker : NSObject, PurchaseTracker
-
A base product type for products that represent any kind of subscription
See moreDeclaration
Swift
open class SubscriptionProduct : NoQuantityProduct
-
A class that is used to create URLs that will invoke App actions.
Flint Routes support multiple custom app URL schemes and multiple associated domains for deep linking.
A LinkCreator will only create links for one app scheme or domain - typically apps do not need to generate different kinds of URLs for the same app, but you may need to handle multiple legacy URLs or domains.
As such, Flint will create a default link creator for the first App URL scheme and Associated Domain that you define in your Info.plist (for app URLs) and the domain you pass when calling
Flint.quickSetup
.This will be used for the automatic link creation for Activities and other system integrations. You can change this behaviour by creating a new
LinkCreator
for the scheme and domain you prefer, and assign it toFlink.linkCreator
.You can create your own instances to produce links with specific schemes and domains. Links can only be created for actions that have routes defined in your Feature’s
See moreurlMappings
.Declaration
Swift
public class LinkCreator
-
The action that performs an action associated with a given URL.
Expected input state type: URL Expected presenter type: PresentationRouter
This will attempt to resolve the URL against the known URL routes defined on
See moreURLMapped
features of the app.Declaration
Swift
final public class PerformIncomingURLAction : UIAction
-
A URL Pattern matcher that uses Grails-style matching to extract named parameter values from the path and use them like query parameters. The following syntax is supported, per path component, so that macros are not able to span components (i.e. path components cannot contain or match
/
):$(paramName)
— e.g.something$(param1)
,$(param1)something
,something$(param1)something
. The text whereparam1
is in the path is extracted into the query parameters with the keyparam1
*
— a wildcard that represents 1 or moreany
characters, e.g.something*
,*something
,*
**
— a wildcard that matches everything after it in the URL path. It is not valid to have**
anywhere except the final path component
See more/store/categories/grindcore --> parameters [:] /store/$(category)/grindcore --> parameters ["category":x] /store/$(category)/items/$(sku) --> parameters ["category":x, "sku": y] /store/$(category)/items/** --> parameters ["category":x] (** matches any suffix) /store/$(category)/*/whatever --> parameters ["category":x] (* matches any component, not captured) /store/$(category)/*/whatever?var1=a --> parameters ["category":x, "var1":"a"] /store/*/whatever?var1=a --> parameters ["var1":"a"] /store/*/**?var1=a --> parameters ["var1":"a"] /** --> parameters [:]
Declaration
Swift
public class RegexURLPattern : URLPattern
-
The FlintCore
See moreDeep Linking
feature which is used to take incoming URLs and dispatch the appropriate App action using a Presenter provided by aPresentationRouter
which determines how your app will present the UI required for the action.Declaration
Swift
final public class RoutesFeature : ConditionalFeature
-
A simple representation of the supported
URLMapping
(s) for actions.This is produced by the
URLMappingsBuilder
, and used to collect all the mappings for a single feature, with a sort of type-erasure for the action type, which is required for action metadata binding elsewhere, so we can show developers the URLs mapped to a given action typeDeclaration
Swift
public class URLMappings
-
Builder that creates a URLMappings object, containing all the mappings for a single feature.
This is used to implement the URL mappings convention of a Feature, which binds schemes, domains and paths to actions. An instance is passed to a closure so that Features can use a DSL-like syntax to declare their mappings.
The resulting
See moreURLMappings
object is returned by thebuild
function, using covariant return type inference to select the correctbuild
function provided by the extensions onFeature
.Declaration
Swift
public class URLMappingsBuilder
-
Undocumented
See moreDeclaration
Swift
public class AnyURLPattern : URLPattern
-
The default presenter type for Intent Actions, which provides a single function that will pass the INIntentResponse to the Intent Handler’s completion function.
Note
This will assert that the respone is the expected type. We use generics here to get close to true type safety, but because of the nature of the code generated by Xcode, we cannot have a truly statically typed presenter — so we check the type of the response at the point of submitting.Declaration
Swift
public class IntentResponsePresenter<ResponseType> where ResponseType : FlintIntentResponse
-
The IntentShortcutDonation feature will automatically register shortcuts for actions that support intents. Any action that returns a non-nil intent for a given input will be automatically registered.
See moreDeclaration
Swift
public final class IntentShortcutDonationFeature : ConditionalFeature
-
The is the internal Flint feature for automatic Siri Intent donation and intent dispatch handling from Intent extensions, shortcut registration etc.
See moreDeclaration
Swift
public final class SiriIntentsFeature : ConditionalFeature, FeatureGroup
-
An action dispatch observer that will collect a rolling buffer of N action events, and can notify observers when these entries change. Action requests are converted into audit entries that are immutable, so the values of action state and other information are captured at the point of the action occurring, allow you to see changes in the action state over time through the log.
This is a high level flattened breadcrumb trail of what the user has done in the app, purely in action terms. No other logging is included, so this is suitable for inclusion in crash reports and support requests.
Use this to capture the history of what the user has done. You can use
Flint.quickSetup
or manually add this observer with:Flint.dispatcher.add(observer: TimelineDispatchObserver(maxEntries: 50))
See
Flint.quickSetup
which will add this dispatcher for you automatically.Declaration
Swift
public class Timeline : ActionDispatchObserver, DebugReportable
-
Timeline Entries encapsulate all the lightweight representations of properties related to an action event to be stored in a timeline without any references to the original data. This is to prevent memory usage spiralling out of control while the app is running.
!!! TODO: Remove @objc and change entry to
See morestruct
when Swift bug SR-6039/SR-55 is fixed.Declaration
Swift
@objc public class TimelineEntry : NSObject, UniquelyIdentifiable
-
The Timeline Feature gathers information about the actions the app performs. You can use this to reproduce the steps the user took to arrive at a certain point or crash.
Entries are stored in a LIFO queue restricted to a maximum number of entries to prevent using every-growing amounts of memory.
See
Timeline.snapshot()
for access to the data gathers.Declaration
Swift
final public class TimelineFeature : ConditionalFeature
-
A type that handles completion callbacks with safety checks and semantics that reduce the risks of callers forgetting to call the completion handler.
The type is not concurrency safe (see notes in Threading) and it will always call the
completion
handler synchronously, using the suppliedcompletionQueue
if available, or on whatever the current queue thread is ifcurrentQueue
is nil.To use, define a typealias for this type, with T the type of the completion function’s argument (use a tuple if your completion requires multiple arguments).
Then make your function that requires a completion handler take an instance of this type instead of the closure type, and make the function expect a return value of the nested
Status
type:protocol MyCoordinator { typealias DoSomethingCompletion = CompletionRequirement<Bool> func doSomething(input: Any, completionRequirement: DoSomethingCompletion) -> DoSomethingCompletion.Status }
Now, when calling this function on the protocol, you construct the requirement instance, pass it and verify the result:
let coordinator: MyCoordinator = ... let completion = MyCoordinator.DoSomethingCompletion( { (shouldCancel: Bool, completedAsync: Bool) in print("Cancel? \(shouldCancel)") }) The block takes one argument of type `T`, in this case a boolean, and a second `Bool` argument that indicates if the completion block has been called asynchronously. // Call the function that requires completion let status = coordinator.doSomething(input: x, completionRequirement: completion) // Make sure one of the valid statuses was returned. // This safety test ensures that the completion from the correct completion requirement instance was returned. precondition(completion.verify(status)) // If the result does not return true for `isCompletingAsync`, the completion callback will have already been called by now. if !status.isCompletingAsync { print("Completed synchronously: \(status.value)") } else { print("Completing asynchronously... see you later") }
When implementing such a function requiring a completion handler, you return one of two statuses returned by either the
CompletionRequirement.completed(_ arg: T)
orCompletionRequirement.willCompleteAsync()
. TheCompletionRequirement
will take care of calling the completion block as appropriate.
See morefunc doSomething(input: Any, completionRequirement: DoSomethingCompletion) -> DoSomethingCompletion.Status { return completionRequirement.completedSync(false) } // or for async completion, you retain the result and later call `completed(value)` func doSomething(input: Any, completionRequirement: DoSomethingCompletion) -> DoSomethingCompletion.Status { // Capture the async status let result = completionRequirement.willCompleteAsync() DispatchQueue.main.asyncAfter(deadline: .now() + 5) { // Use the retained status to indicate completion later result.completed(false) } return result } ## Threading A `CompletionRequirement` is not concurrency safe. You must not change any properties or call any methods after calling `completedSync` or `willCompleteAsync`. State of the object will not change asynchronously once you have called either of these.
Declaration
Swift
public class CompletionRequirement<T>
-
A TimeOrderedResultsControllerDataSource that is backed by a last-in, first-out queue which is threadsafe and calls observers when items loaded or added.
Note
You can append items, modify observers and read the queue (usingsnapshot()
) from any queue.Declaration
Swift
public class FIFOArrayDataSource<T> : TimeOrderedResultsControllerDataSource where T : UniquelyIdentifiable
-
Standard formatters used by Flint
See moreDeclaration
Swift
public class Formatters
-
A
ProxyCompletionRequirement
allows you to provide a completion requirement that adds some custom completion logic to an existing completion instance, and then return a possibly modified result value to the original requirement.This mechanism allows your code to
not care
whether the completion you are proxying is called synchronously or not. Normally you need to know if completion you are wrapping would be called async or not, as you would need to capture the async completion status before defining your completion block so it can callcompleted
on the async result.This is a bit nasty in the nuance of the implementation. We may remove this if
See moreaddProxyCompletionHandler
Declaration
Swift
public class ProxyCompletionRequirement<T> : CompletionRequirement<T>
-
A dispatch queue that will call a sync block inline if it can tell we are already on that queue, avoiding the problem of having to know if you are on that queue already before calling sync().
It also supports synchronous execution of the block if on the correct queue already, or flipping that to async if we are not on this queue.
Note
This is not safe to use when using Dispatch Queues that use atarget
queue. The block will execute on the target queue, andgetSpecific
will not return the correct valueDeclaration
Swift
public class SmartDispatchQueue : Equatable
-
A controller for managing results from a data source that inserts new items over time, and can load pages of older items.
This will call into its delegate to indicate when new items are inserted or old items have been loaded.
Note
Does not support random access. We don’t need it.Declaration
Swift
public class TimeOrderedResultsController : TimeOrderedResultsControllerDataSourceObserver
-
A sequence of uncompressed or compressed ZIP entries.
You use an
Archive
to create, read or update ZIP files. To read an existing ZIP file, you have to pass in an existing fileURL
andAccessMode.read
:var archiveURL = URL(fileURLWithPath: "/path/file.zip") var archive = Archive(url: archiveURL, accessMode: .read)
An
Archive
is a sequence of entries. You can iterate over an archive using afor
-in
loop to get access to individualEntry
objects:for entry in archive { print(entry.path) }
Each
Entry
in anArchive
is represented by itspath
. You can usepath
to retrieve the correspondingEntry
from anArchive
via subscripting:let entry = archive['/path/file.txt']
To create a new
Archive
, pass in a non-existing file URL andAccessMode.create
. To modify an existingArchive
useAccessMode.update
:
See morevar archiveURL = URL(fileURLWithPath: "/path/file.zip") var archive = Archive(url: archiveURL, accessMode: .update) try archive?.addEntry("test.txt", relativeTo: baseURL, compressionMethod: .deflate)
Declaration
Swift
public final class Archive : Sequence