This is a rather long-awaited next major version of ProcedureKit.
- Networking procedures no longer use an associated type for the
URLSession
. InsteadSession
is a free-floating protocol. This makes general usage, subclassing and composing much simpler. - There is now a Core Data module
BlockProcedure
API has changed.Procedure
only supports a singleError
value, instead of[Error]
- this has had some fairly wide reaching changes to APIs.- New built-in logger, which uses
os_log
by default. - Changes to
UIProcedure
in ProcedureKitMobile module.
-
[823]: Removes associated types from Network
Originally raised as an issue by @ericyanush in which I totally missed the point initially. But, after thinking about it more, made so much sense. Instead of having a generic
URLSessionTaskFactory
protocol, where the various types of tasks were associated types, we now just have a non-genericNetworkSession
protocol, to whichURLSession
conforms. The impact of this subtle change, is that what was once:NetworkDataProcedure<Session: URLSessionTaskFactory>
is nowNetworkDataProcedure
. In otherwords, no longer generic, and now super easy to use as that genericSession
doesn't leak all over the place. -
[#875]: Refactored
BlockProcedure
There has been a long-standing wish for
BlockProcedure
instances to "receive themselves" in their block to allow for access to its logger etc. In v5, the following is all possible, see this comment:-
Simple synchronous block (existing functionality):
let block = BlockProcedure { print("Hello World") }
-
Synchonous block, accessing the procedure inside the block:
let block = BlockProcedure { this in this.log.debug.message("Hello World") this.finish() }
Note that here, the block is responsible for finishing itself - i.e. call
.finish()
or.finish(with:)
to finish the Procedure. Using this initializer, by default,BlockProcedure
will add aTimeoutObserver
to itself, usingdefaultTimeoutInterval
which is set to 3 seconds. This can be modified if needed.BlockProcedure.defaultTimeoutInterval = 5
-
Asynchronous block with cancellation check,
AsyncBlockProcedure
andCancellableBlockProcedure
get deprecated warnings.let block = BlockProcedure { this in guard !this.isCancelled else { this.finish() } DispatchQueue.default.async { print("Hello world") this.finish() } }
-
ResultProcedure
as been re-written as a subclass ofBlockProcedure
(previously, it was the superclass). Existing functionality has been maintained:let hello = ResultProcedure { "Hello World" }
-
-
[#851]: Errors
At WWDC18 I spent some time with some Swift engineers from Apple talking about framework design and error handling. The key take-away from these discussions was to increase clarity which reduces confusion, and makes intent clear.
This theme drove some significant changes. To increase clarity, each Procedure can only have a single
Error
, because ultimately, how can a framework consumer "handle" an array ofError
values over just a single one? I realised that the only reasonProcedure
has an[Error]
property at all was fromGroupProcedure
collecting all of the errors from its children, yet the impact of this is felt throughout the codebase.This means, to finish a procedure with an error, use:
finish(with: .downloadFailedError) // this is a made up error type
Observers only receive a single error now:
procedure.addDidFinishBlockObserver { (this, error) in guard let error = error else { // there is an error, the block argument is Error? type return } // etc }
Plus more API changes in
Procedure
andGroupProcedure
which will result in deprecation warnings for framework consumers.For
GroupProcedure
itself, it will now only set its own error to the first error received. However, to access the errors from child procedures, use the.children
property. Something like:let errors = group.children.operationsAndProcedures.1.compactMap { $0.error }
-
ProcedureKit has its own logging system, which has received an overhawl in v5. The changes are:
1. Now uses `os_log` instead of `print()` where available.
- Dedicated severity levels for caveman debugging & user event. See this comment.
- Slight API change:
previously, it was:
procedure.log.info.message("This is my debug message")
For module-wide settings:procedure.log.info("This is my debug message")
Log.enabled = true Log.severity = .debug // default is .warning Log.writer = CustomLogWriter() // See LogWriter protocol Log.formatter = CustomLogFormatter() // See LogFormatter protocol
-
[#860]: Swift 3/4 API naming & conventions
@lukeredpath initially raised the issue in #796, that some APIs such as
add(condition: aCondition)
did not Swift 3/4 API guidelines, and contributed to inconsistency within the framework. These have now been tidied up.
-
[#830, #837]: Swift 4.1 & Xcode 9.3 support, (Xcode 10 is ready to go).
These changes take advantage of Swift 4.1 capabilities, such as synthesized
Equatable
and conditional conformance. -
[#828, #833]: Result Injection & Binding
Result Injection conformance is added to
RepeatProcedure
(and subclasses such asRetryProcedure
&NetworkProcedure
). This means the input can be set on the outRepeatProcedure
, and this value will be set on every instance of the target procedure (assuming it also conforms toInputProcedure
). This avoids having to jump through hoops like this.Additionally, a new binding API can be used, particularly with
GroupProcedure
subclasses, so that the input of a child procedure is "bound" to that of the group itself, likewise, the output of the group is bound to a child. This makes it very easy to encapsulate a chain of procedures which use result injection into aGroupProcedure
subclass. See the docs. -
[#834]: Adds
BatchProcedure
BatchProcedure
is aGroupProcedure
subclass which can be used to batch process a homogeneous array of objects, so that we get[T] -> [V]
via a procedure which doesT -> V
. We already haveMapProcedure
which does this via a closure, and so is synchronous, and useful for simple data transforms.BatchProcedure
allows asynchronous processing via a custom procedure. This is actually a pretty common situation in production apps. For example, consider an API response for a gallery of images, we can useBatchProcedure
to get all the images in the gallery. -
[#838]: Adds
IgnoreErrorsProcedure
IgnoreErrorsProcedure
will safely wrap another procedure to execute it and suppress any errors. This can be useful for fire, forget and ignore type behavior. -
[#843, #844, #847, #849]: Adds ProcedureKitCoreData.
-
LoadCoreDataProcedure
- intended to be subclassed by framework consumers for their project, see the docs. -
MakeFetchedResultControllerProcedure
-
SaveManagedObjectContext
-
InsertManagedObjectsProcedure
-
MakesBackgroundManagedObjectContext
- a protocol to allow mixed usage ofNSPersistentContainer
,NSManagedObjectContext
andNSPersistentStoreCoordinator
.
-
-
[#840, #858, #868]: Adds
UIBlockProcedure
UIBlockProcedure
replacesUIProcedure
, and it essentially is a block which will always run on the main queue. It is the basis for other UI procedures. -
[#841, #873, #874]: Adds
UIViewController
containment procedures-
AddChildViewControllerProcedure
-
RemoveChildViewControllerProcedure
-
SetChildViewControllerProcedure
All of these procedures provide configurable auto-layout options. By default the child view controller's view is "pinned" to the bounds of the parent view. However, it is possible to use custom auto-layout behaviour.
-
Thanks to everyone who has contributed to ProcedureKit - v5 has been quite a while in development. There is still quite a bit left to do on the documentation effort - but that will be ongoing for evermore.
- #816 Updates for Xcode 9.2 and Swift 4.0. Thanks to @jshier for making the updates. Included here are updates to the Travis config too.
- #781 Some significant performance and reliability improvements for
BackgroundObserver
by @swiftlyfalling.
- #818
UserIntent
property onProcedure
has been deprecated. Suggestion is to set the underlying queue priority.
- #787 Updates to a minimum version of watchOS 3. Technically, this is a breaking change, but, realistically, anyone building for Watch will be at least on watchOS 3 now.
- #802 Updates iOS Simulators to iOS 11 in Fastlane
- #801 Removes SwiftLint
- #795 Fixes an issue with Conditions and suspended ProcedureQueues
-
#790,#791 Fixes a mistake which hid the initialiser of
ReverseGeocodeUserLocation
which renders it un-usable 🙄. Thanks to Anatoliy for raising the issue. There really aught to be some way of having autogenerated tests for this type of bug. -
#793 Migrates ProcedureKit's CI to a complementary account on BuildKite. You will still need an account to view this, however, it means that open source contributors can be added to the BK account without cost. Please get in touch if you want an invite. Thanks to @keithpitt and @ticky for migrating our pipeline & history between orgs.
In addition to this, I have setup a Mac in MacStadium, in addition to my own build server. This means that we should have effectively got constant uptime of agents to build CI.
In light of these changes, I've disabled the Travis service, which has proved to be slow and un-reliable. The
travis.yml
will stay and remain working for anyone who maintains their own fork.
- #785 To get round an error with Xcode 9 betas archiving applications.
- #750, #762, #763, #751, #766, #767, #768, #771, #772, #773, #775, #779 Numerous improvements to project documentation.
- Docs are a combination of source code documentation and a programming guide. It is built as the code changes as part of the CI system, and published on procedure.kit.run with a path matching the branch. Therefore, the most up-to-date documentation is: procedure.kit.run/development.
- The programming guide is written in Markdown, and stored in the repo under
Documentation/Guides
- Documentation is generated using jazzy and organised via
.jazzy.json
file. It can be generated locally by runningjazzy --config .jazzy.json
from the project root. - Because documentation is built as part of CI, it should evolve with the code, and the documentation for WIP branches can be built, published and viewed.
- Eventually the documentation site will allow framework consumers to browse versions of the programming guide.
- Current documentation coverage is 53%. This is reported in a shield on the project page.
- #757 Improves the
QualityOfService
tests. - #756 Fixes a rare race condition involving
Condition
. - #752, #754 Resolves
ProcedureObserver
errors in Xcode 9 Beta 3 onwards. - #769 Fixes a race condition in
TestProcedure
. - #770, #774 Fixes unstable tests related to producing operations.
- #777 Simplifies
Procedure.ConditionEvaluator
state management.
- I (@danthorpe) changed the code of conduct to comply with GitHub's notion of what a code of conduct is. Quite frankly, this is annoying, please feel free to contact me if you find the changes disagreeable.
- #717 Termination status & termination reason provided to handler (full details in PR).
- #737 Simplifies
GroupProcedure
child error handling, so that it is now centralised in a single, better named method:child(_:willFinishWithErrors:)
- @swiftlyfalling has gone through the entire project and fixes Swift 4 released issues so that ProcedureKit will work in Xcode 9 beta without issue. Critically, we have not yet changed the build settings for Swift 4 - but this is the last release for Swift 3.
- #739 Fixes complication of
ProcedureEventQueue
in Xcode 9, when in Swift 3 mode.
- #715 Improves the
.then { }
API so that all operations in the receiver are added as dependents of the argument. - #717 Improves
ProcessProcedure
so that can be used with result injection and dispatches using its internal queue. - #738 Adds
transformChildErrorsBlock
toGroupProcedure
. This will enable customisation of the errors withGroupProcedure
without subclassing.
- #710, #711, #712 Lots of robustness fixes for tests.
- #714 Fixes a (rare) data race in DelayProcedure.
- #721 Adds missing
tearDown
overrides toCloudKitProcedure
tests. - #722 Fixes a memory cycle in
makeFinishingProcedure
in the testing framework helpers. - #724 Reduces dependencies on BuildKite agents by adding Travis CI.
- Swift 3.1 fixes for Xcode 8.3
Same as Beta 7 😀
- #668 Adds Procedure event queue. Procedure now utilises an internal serial FIFO queue which dispatched user "events". Procedure events include anything that calls user code, like overridden methods, observer callbacks, injecting results from a dependency. See the PR for more details, there are some breaking changes here, which is very well documented in the PR description.
- #681 @swiftlyfalling Refactors how Condition is implemented. There are breaking changes here which are well documented in PR description.
- #662, #673 Improves the TimeoutObserver implementation by utilising a registrar to handle the lifetime of timers. By @swiftlyfalling.
- #658, #659 Adds Repeatable type.
- #663 Fixes building when using Swift Package Manager.
- #664 Improves Swift 3 URLError handling in Network procedures.
- #690 Adds
UserConfirmationCondition
as in Operations. - #676 Enhances
AnyProcedure
to allow forAnyOutputProcedure
. Thanks to @sviatoslav.
- #660, #661 Fixes a string conversion memory leak, which is actually a bug in Swift itself.
- #666 Fixes code signing issues that prevents compiling release configuration builds.
- #669 Fixes a type to Dan's GitHub profile.
- #677 Restricts
RetryProcedure
to only allowProcedure
subclasses. - #679
AuthorizedFor
condition now ensures that the producedAuthorizedCapabilityProcedure
is mutually exclusive, rather than the procedure it gets attached to. - #689 Updates SwiftLint ruleset.
- #687 Uses
dependencyCancelledWithErrors
error context.
ProcedureKit is nearing a final v4 release. Beta 6 sees all functionality that will be added for v4 in place. Some breaking changes around cancellation are currently being discussed, and will come in the next (and hopefully last) beta.
In this release, @swiftlyfalling has been doing amazing work finding, fixing and adding tests for race-conditions, memory leaks, general thread-safety and cancellation. It really has been fantastic. Currently, over 83% for all components on average.
-
#631, #632 Result injection is now supported for
NetworkDataProcedure
et. al. This API is calledinjectPayload(fromNetwork:)
and will support functionality like this:// Procedure to get a network request let getRequest = GetRequest() // Procedure to get the Data payload let network = NetworkDataProcedure() // Inject the URLRequest .injectResult(from: getRequest) // Procedure to decode the data payload let decode = DecodeNetworkPayload() // Inject the network payload .injectPayload(fromNetwork: network)
Thanks to @robfeldmann for raising the initial issue.
-
#592 Adds
UIProcedure
andAlertProcedure
as part of ProcedureKitMobile framework. Usage is like this:let alert = AlertProcedure(presentAlertFrom: self) alert.add(actionWithTitle: "Sweet") { alert, action in alert.log.info(message: "Running the handler!") } alert.title = "Hello World" alert.message = "This is a message in an alert" queue.add(operation: alert)
-
#623 Adds
ProcedureKit/All
CocoaPod sub-spec which corresponds to all the cross platform components. -
#625 Tweaks for TestingProcedureKit imports.
-
#626, #627,#640, #646 Tweaks Network procedures so that cancellation is thread safe, avoids a potential race condition, and testing enhancements.
-
#624 Some minor fixes after a through investigation with the visual memory debugger - which can produce erroneous leak indicators.
-
#630 Adds a build step to CI to perform integration testing using CocoaPods works with the current changes on a feature branch. Currently this does not work for 3rd party contributions.
-
#634 Fixes some copy/paste typos from a merge conflict.
-
#635 Removes the fatal override of
waitUntilFinished()
. -
#639 Thread safety improvements to
ProcedureProcedure
in ProcedureKitMac. -
#643 Further testing of
DidExecute
observers. AddscheckAfterDidExecute
API toProcedureKitTestCase
. -
#649 Removes all code signing settings.
-
#644 Fixes issues for ProcedureKitCloud in Xcode 8.2 - as they've changed some APIs here.
-
#647 Marks non-open properties/methods as
final
. -
#650 Adds more tests for cancelling
Condition
subclasses. -
#655 Removes the beta tag from the internal framework versioning.
Beta 5 is primarily about refinements and bug fixes.
-
#574, #583 Removal of
GroupObserverProtocol
This protocol was to allow observer to be attached to a group, and be informed when children are added to the group. Instead, this functionality has been rolled intoProcedureObserver
. -
#601, #605 Refactor of
ResultInjection
. TheResultInjection
protocol has been overhauled, again. The major changes here, are:- Change to a pair of protocols,
InputProcedure
andOutputProcedure
, with associated typeInput
andOutput
respectively. This change is to avoid overloading the "result" concept. - Renames
PendingValue<T>
to justPending
. Both protocols have properties which arePending
, which in turn maintains the.pending
and.ready
cases. ProcedureResult<T>
which is an either enum type, which is either.success(value)
or.failure(error)
. The error is not an associated type - so anyError
will do.OutputProcedure
'soutput
property isPending<ProcedureResult<Output>>
which means that it can now capture the procedure finishing with an error instead of just a value.
In addition,
Procedure
subclasses which conform toOutputProcedure
can use the following API:/// Finish the procedure with a successful result. finish(withResult: .success(outputValue)) /// Finish the procedure with an error. finish(withResult: .failure(anError))
To support
OutputProcedure
with aVoid
output value, there is also a public constant calledsuccess
which represents.success(())
.All other APIs have been changed to reflect this change, e.g.
injectResult(from: dependency)
works as before if your receiver is updated to conform toOutputProcedure
. - Change to a pair of protocols,
-
#561 Rename & refactor of
ResilientNetworkProcedure
NetworkProcedure
now performs the functionality of network resiliency, in addition to automatic handling of client reachability errors.
-
#565
NetworkDownloadProcedure
Thanks to @yageek for adding support for network file downloads. -
#567
NetworkUploadProcedure
Thanks to @yageek for adding support for network file uploads. -
#570
ProcessProcedure
Thanks to @yageek for adding support for wrappingProcess
(previouslyNSTask
) to ProcedureKitMac. -
#542, #599
CloudKitProcedure
This is a wrapper class for running Apple'sCKOperation
subclasses. -
#587 Mutual Exclusion categories Mutually exclusive conditions now support arbitrary category names, which means that a condition can be used to add mutual exclusion to any number of disparate procedures.
-
#563
NetworkProcedure
(calledNetworkReachableProcedure
here)NetworkProcedure
is a wrapper procedure for executing network procedures. It has full support for handling client reachability issues, and resilient handling of client and server errors. -
#569
Profiler
Thanks to @yageek for implementing theProfiler
which is aProcedureObserver
and can be used to report timing profiles of procedures. -
#593 Supports the merging of collections of
Procedure
subclasses which all conform toResultInjection
. These APIsflatMap
,reduce
andgathered()
each return another procedure which will depend on all other procedures in the collection, and then perform synchronous processing of the results. For example, either just gather the results into a single array, or flat map the resultant array into an array of different types, or reduce the resultant array into a single type. -
#606, #607
AsyncResultProcedure
etc.AsyncResultProcedure
,AsyncBlockProcedure
andAsyncTransformProcedure
support asynchronous blocks. Each procedure's initialiser receives a finishWithResult closure, which must be called to finish the procedure. For example:let procedure = AsyncBlockProcedure { finishWithResult in asyncTask { finishWithResult(success) } }
- #562 Fixes a typo in
LocationServicesRegistrarProtocol
- #566 Fixes
Condition
so that it can support result injection. - #575 Improves the performance of
add(observer: )
. - #568 Opens up
add(operation: Operation)
for overriding by subclasses. Thanks to @bizz84. - #579 Adds more test coverage to
GroupProcedure
. - #586 Fixes
HTTPRequirement
initializers. Thanks to @yageek for this one. - #588 Fixes bug where using the
produce(operation:)
from aGroupProcedure
subclass was failing. This was actually introduced by other changes since Beta 4. - #591 Adds some missing equality checks in
ProcedureKitError.Context
. - #600 Minor changes to remove @testable imports.
- #602 Adds stress tests for cancelling
RepeatProcedure
. - #603 Adds more
GroupProcedure
tests. - #608 Uses the internal queue for the
DispatchAfter
delayed functionality inNetworkActivityController
instead of the main queue. - #611 Restores
import Foundation
etc where needed in all classes, which makes Xcode 8 a little happier - although not strictly necessary. - #615 Fixes issues where
BackgroundObserver
was not removing notification observers. - #619 Fixes some issues with location related procedures.
Recently, @swiftlyfalling has been fixing a number of thread safety issues highlighted either from our own stress tests, or from the Thread Sanitizer.
NetworkObserver
- #577StressTestCase
- #596RepeatProcedure
- #597Procedure
- #598NetworkDataProcedure
etc - #609BackgroundObserver
- #614DelayProcedure
- #616
Beta 4 is a significant maturation over Beta 3. There are a couple of breaking changes here which I will call out explicitly. Overall however, the APIs have been refined, adjusted and extended, bugs have been fixed, and tests have been stabilised.
Additionally, Beta 4 now supports integration via Carthage and CocoaPods including full support for TestingProcedureKit and CocoaPod subspecs.
-
#519 Renames
AuthorizationStatusProtocol
toAuthorizationStatus
. Thanks to @martnst.
-
#520 Renames:
GetAuthorizationStatus
toGetAuthorizationStatusProcedure
,Authorize
toAuthorizeCapabilityProcedure
Thanks to @martnst again.
-
#527, #528, #541, #546 ResultInjection
ResultInjection, which is what we call the methodology of automatically injecting the result from one procedure as the requirement of a dependency, has been revamped in Beta 4.
- It is now an extension on
ProcedureProctocol
. - The API now support injection via a transform block. For example, lets assume that we are using
NetworkDataProcedure
which requires aURLRequest
, and we have a procedure which results in aURL
, we might do this:download.injectResult(from: getURL) { url in return URLRequest(url: $0) }
- Refactors
ResultInjection
protocol to usePendingValue<T>
. Now therequirement
andresult
properties arePendingValue<Requrement>
andPendingValue<Result>
respectively. This avoids the need to use explicitly unwrapped optionals for theRequirement
. For example:class MyProcedure: Procedure, ResultInjection { var requirement: PendingValue<Foo> = .pending var result: PendingValue<Bar> = .pending }
- Extension APIs automatically unwrap optionals. This means that where a
Result? == Requirement
the result will be automatically unwrapped.
- It is now an extension on
-
This is a new procedure which supports composition of any
Procedure
subclass conforming toResultInjection
APIs with complete type erasure. This makes the following usage possible:- Inject / store generic
Procedure
subclasses into other types. - Store many different types of
Procedure
subclasses in a homogenous storage container, so long as they have the same sub-typeRequirement
andResult
.
An example of where this is useful would be with a strategy design pattern, where each strategies likely has a different
Procedure
subclass, but theRequirement
(i.e. input) andResult
(i.e. output) of each is the same. Given this, any strategy can be injected into aGroupProcedure
or other structure type-erased usingAnyProcedure<Requirement,Result>
. - Inject / store generic
-
#523 ProcedureKitCloud
Added a ProcedureKitCloud framework, which currently just includes
Capability.CloudKit
. Unfortunately the fullCloudKitProcedure
class did not get finished in time for this beta. However, it is very close to being finished, see PR: #542. -
#524, #525, #526, #538,#547 ProcedureKitNetwork
ProcedureKitNetwork is a framework which offers a very simple wrapper around
URLSession
completion based APIs. Currently only forNetworkDataProcedure
which uses theURLSessionDataTask
based APIs. If you need to use the delegate based APIs you cannot use thisProcedure
subclass.Additionally, there is full support in TestingProcedureKit for using
TestableURLSession
which allows framework consumers to check that the session receives the correct request etc. -
#536
.then { }
APIAdded an alternative way of adding dependencies on
Operation
in a chain. For example:let operations = foo.then(do: bar).then { Baz() }
Thanks to @jshier for initial idea and suggestion - sorry it took so long to get done!
-
Note here that the standard pod is just the core framework. This is Extension API compatible with support for all 4 platforms. To get iOS related classes, such as
BackgroundObserver
which are not Extension API compatible useProcedureKit/Mobile
, likewise forProcedureKit/Location
,ProcedureKit/Network
,ProcedureKit/Cloud
etc. TestingProcedureKit, has its own podspec. -
#537
ResilientNetworkProcedure
BetaThis is a
RetryProcedure
subclass which is designed to add network resiliency around network request basedProcedure
subclasses. This procedure works by providing a value which corresponds toResilientNetworkBehavior
protocol, and a closure which returns a new network request procedure. The protocol allows the framework consumer to decide how to interpret status/error codes, and trigger retries. -
#550
ConcurrencyTestCase
This is a new
ProcedureKitTestCase
subclass in TestingProcedureKit which has methods to help test concurrency issues in ProcedureKit itself, but also in your applications. Thanks to @swiftlyfalling for adding it. -
#552
wait(forAll: [Procedure])
APIThis is added to
ProcedureKitTestCase
. Thanks to @swiftlyfalling for adding this. -
#554 Adds
did(execute: Procedure)
observer callback.Use this with great caution, as it may not always do what you expect depending on the behavior of the
execute
method of the procedure. From the discussion:all that's currently guaranteed is that didExecuteObservers will be called after execute() returns. The rest is up to the specifics of the Procedure subclass implementation. This will likely be improved before 4.0.0 is final.
- #518 Fixes failing release build regression.
- #531 Adds default empty implementation to some of the Queue Delegate methods. Feedback welcome here!
- #533 Adds an area in the repo for talks and presentations which have been given about Operations or ProcedureKit. Watch out for @jshier who will be speaking at Swift Summit ProcedureKit and you on Nov 7th. 😀😀😀
- #532 Fixes a bug where
GroupProcedure
would collect errors from its children after it had been cancelled. This is a bit annoying, if a group is cancelled, it will cancel all of its children with an error (ProcedureKitError.parentDidCancel(error)
), but it would then receive in its delegate all of those errors from the children. - #539 Tweaks to cancel
BlockProcedure
stress tests. - #540 Moves
import ProcedureKit
into umbrella headers. - #544 Fixes
BlockProcedure
stress tests - thanks to @swiftlyfalling. - #545 Fixes a bug where
ExclusivityManager
was not thread safe. Thanks to @myexec for reporting the bug, and @swiftlyfalling for fixing it. - #549 Fixes random crashed in
QueueTestDelegate
- thanks to @swiftlyfalling for fixing this, and generally being awesome at identifying where code paths are not thread safe 💚. - #557 Fixes some CI errors in Fastfile.
- #558 #559 Fixes issue with Xcode 8.1 release builds, thanks so much to @pomozoff for figuring out the issue here!
- #556 Adds group concurrency tests using the new
ConcurrencyTestCase
. Thanks to @swiftlyfalling for their awesome contributions!
Beta 3 adds ProcedureKitMobile, ProcedureKitLocation and TestingProcedureKit frameworks. The mobile framework is suitable for use in iOS applications, although it does not yet have AlertProcedure
which will come in a future beta.
To integrate these frameworks, use:
import ProcedureKit
which can be done anywhere, such as internal frameworks and extensions, and on any platform.
import ProcedureKitMobile
which can only be done in an iOS application target, as it’s not extension compatible.
import ProcedureKitLocation
which can be used on any platform.
TestingProcedureKit is a framework is for adding to test bundle targets. It links with XCTest, so cannot be added to an application target. While documentation is sorely lacking here, this is very useful for writing unit tests for Procedure
subclasses. It has APIs to support waiting for procedures to run, and asserting their end state.
- #476 Adds
BackgroundObserver
- #496 Adds
FilterProcedure
- #497 Adds
ReduceProcedure
- #498 Adds
NetworkObserver
- #499 Adds
Capability.Location
- #500 Adds
UserLocationProcedure
- #502 Adds
ReverseGeocodeUserLocationProcedure
- #503 Adds
ReverseGeocodeUserLocationProcedure
- #503 Fixes an issue where the minimum deployment target was incorrect for iOS.
- #510 Makes procedures which were
public
and therefore not override-able by a framework consumeropen
. Got to watch out for these. - #511 Refactors
BlockProcedure
to no longer be a subclass ofTransformProcedure
. I did like the simplicity of this, however, I want to be able to automatically throw an error if the requirement ofTransformProcedure
is not set.
Beta 2 is all about rounding out the majority of the missing functionality from ProcedureKit, and additionally fixing integration issues.
- #471 NegatedCondition
- #472 SilentCondition
- #474 Fixes for how Procedure finishes - thanks @swiftlyfalling
- #473 BlockCondition
- #470 NoFailedDependenciesCondition
- #475 TimeoutObserver
- #478 Procedure name and identity
- #480 BlockObserver - thanks to @jshier for his input on this.
- #487 Adds ComposedProcedure & GatedProcedure
- #488 RepeatProcedure
- #491 RetryProcedure
- #492 Capabilities
In addition to the above additions, fixes have been made to fix Release builds correctly compile, despite some Swift 3 compiler bugs in Xcode 8 and 8.1. See the release notes for more instructions.
Well, it’s time to say goodbye to Operations and hello to ProcedureKit. ProcedureKit is a complete re-write of Operations in Swift 3.0, and has the following key changes.
-
ProcedureKit Operations has been lucky to have many contributors, and for ProcedureKit I wanted to be able to recognise the fantastic contributions of this little community properly. So the repository has been transferred into an organization. At the moment, the only additional member is @swiftlyfalling however I hope that more will join soon. In addition to moving to an org, there are now contribution guidelines and code of conduct documents.
-
Naming changes Because Swift 3.0 has dropped the
NS
prefix from many classes, includingNSOperation
,NSOperationQueue
andNSBlockOperation
, Operations had some pretty significant issues in Swift 3.0. At WWDC this year, I was able to discuss Operations with Dave DeLong and Philippe Hausler. We brainstormed some alternatives, and came up with “Procedure”, which I’ve sort of grown accustomed to now. The name changes are widespread, and essentially, what was onceOperation
is nowProcedure
. -
Project structure For a long time, we’ve had an issue where some classes are not extension API compatible. This has resulted in having two projects in the repository, which in turn leads to problems with Carthage not being able to build desired frameworks. With ProcedureKit, this problem is entirely resolved. The core framework, which is the focus of this beta, is entirely extension API compatible. It should be imported like this:
import ProcedureKit
Functionality which depends on UIKit, such as
AlertProcedure
will be exposed in a framework calledProcedureKitMobile
, and imported like this:import ProcedureKit import ProcedureKitMobile
Similarly for other non-core functionality like CloudKit wrappers etc.
In addition to types which should be used in applications, I wanted to expose types to aid writing unit tests. This is called
TestingProcedureKit
, which itself links againstXCTest
. It can only be used inside test bundle targets. This framework includesProcedureKitTestCase
andStressTestCase
which are suitable for subclassing. The former then exposes simple APIs to wait for procedures to run usingXCTestExpectation
. Additionally, there areXCTAssertProcedure*
style macros which can assert that aProcedure
ran as expected. To use it in your own application’s unit test target:import ProcedureKit import TestingProcedureKit @testable import MyApplication
-
Beta 1 Functionality This beta is focused on the minimum. It has
Procedure
,ProcedureQueue
,GroupProcedure
,MapProcedure
,BlockProcedure
andDelayProcedure
. In addition, there is support for the following features:- Attaching conditions which may support mutual exclusion
- Adding observers - see notes below.
- Result injection has been simplified to a single protocol.
- Full logging support
- Errors are consolidated into a single
ProcedureKitError
type.
-
Observers An annoying element of the observers in Operations is that the received
Operation
does not retain full type fidelity. It’s justOperation
, notMyOperationSubclass
. WithProcedureKit
this has been fixed as now the underlying protocolProcedureObserver
is generic over theProcedure
which is possible as there is now aProcedureProtocol
. This means, that the block observers are now generic too. Additionally, an extension onProcedureProtocol
provides convenience methods for adding block observers. This means that adding block observers should now be done like this:let foo = FooProcedure() foo.addDidFinishBlockObserver { foo, errors in // No need to cast argument to FooProcedure foo.doFooMethod() }
-
BlockProcedure
The API forBlockProcedure
has changed somewhat. The block type is now() throws -> Void
. In some regards this is a reduction in capability overBlockOperation
from Operations which received a finishing block. The finishing block meant that the block could have an asynchronous callback to it.While this functionality might return, I think that it is far better to have a simple abstraction around synchronous work which will be enqueued and can throw errors. For asynchronous work, it would be best to make a proper
Procedure
subclass.Having said that, potentially we will add
AsyncBlockProcedure
to support this use case. Please raise an issue if this is something you care about!
Anyway, I think that is about it - thanks to all the contributors who have supported Operations and ProcedureKit while this has been written. Stay tuned for Beta 2 in a week or so.
This is a release suitable for submission for iOS 10, but built using Swift 2.3 & Xcode 8.
This is a release suitable for submission for iOS 10, but built using Swift 2.2 & Xcode 7.
- OPR-452 Resolves the warning related to CFErrorRef.
- OPR-453, OPR-454 Fixes an issue where the Mac OS X deployment target was incorrect.
- OPR-456 Modifies the podspec to remove Calendar, Passbook, Photos, CloudKit, Location, AddressBook etc from the standard spec. This is to prevent linking/importing OS frameworks which consumers might not have explanations in their info.plist. This is following reports that are being more restrictive for iOS 10 submissions.
This is a pretty special release! All the important changes have been provided by contributors! 🚀😀💚
Additionally, @swiftlyfalling has become a ProcedureKit core contributor 😀
- OPR-416, OPR-417 Thanks to @pomozoff for reporting and fixing a bug which could cause a crash in an edge case where operation with conditions is previously cancelled. Thanks to @swiftlyfalling.
- OPR-420 Thanks to @swiftlyfalling for replacing an assertion failure, and adding some more stress tests in
Condition
’sexecute
method. - OPR-344, OPR-344 Thanks to @ryanjm for reporting (a while ago, sorry!) a bug in
NetworkObserver
, and thanks to @swiftlyfalling for fixing the bug! - OPR-422 Thanks to @swiftlyfalling for adding some robustness to
NetworkObserver
and its tests. - OPR-419 Thanks to @swiftlyfalling for fixing a bug which improves the performance of a whole number of tests. Mostly the changes here are to ensure that
XCTestExpectation
s get theirfulfill()
methods called on the main queue. - OPR-423 Thanks to @swiftlyfalling for fixing a bug where
cancelWithError()
could result in an assertion due to an illegal state transition. They even added some stress tests around this 💚 - OPR-425 Thanks to @swiftlyfalling for refactoring some unit tests to use a dispatch group instead of multiple
XCTestExpectation
instances. - OPR-427 I made some changes to the CI pipeline for Swift 2.2 branch so that @swiftlyfalling didn’t have to wait too long to merge their pull requests.
- OPR-434 Thanks to @pomozoff for raising a configuration issue where a file was added to the Xcode project twice, causing a warning when running Carthage.
- OPR-435 Thanks to @swiftlyfalling for making some improvements to avoid a Swift 2.2 bug which causes a memory leak when using string interpolation.
- OPR-410 Thanks to @paulpires for fixing a bug in the
ReachabilityManager
. - OPR-412 Makes the
condition
property ofOperation
publicly accessible.
I’ve made some changes to make working with result injection easier.
-
OPR-362, OPR-363, OPR-378 These changes simplify the core implementation of how result injection works, no longer using any closure capture. Additionally, if the
Requirement
is equal toResult?
, the framework provides a new API, for requiring that the result is available. For example:class Foo: Operation, ResultOperationType { var result: String? // etc } class Bar: Operation, AutomaticInjectionOperationType { var requirement: String = “default value” // etc } let foo = Foo() let bar = Bar() bar.requireResultFromDependency(foo)
Now, if
foo
finishes with a nilresult
value,bar
will be automatically cancelled with anAutomaticInjectionError.RequirementNotSatisfied
error. And it’s no longer necessary toguard let
unwrap the requirement in theexecute()
method.This works well in situations where the
requirement
property is not an optional, but can be set with a default value.
I’ve made some changes to improve working with Condition
s. The focus here has been to support more subtle/complex dependency graphs, and suppressing errors resulting from failed conditions.
-
OPR-379, OPR-386 Fixes some unexpected behaviour where indirect dependencies (i.e. dependencies of a condition) which are also direct dependencies got added to the queue more than once. This was fixed more generally to avoid adding operations which are already enqueued.
-
OPR-385, OPR-390, OPR-397 Adds support for ignoring condition failures
In some situations, it can be beneficial for an operation to not collect an error if an attached condition fails. To support this,
ConditionResult
now has an.Ignored
case, which can be used to just cancel the attachedOperation
but without an error.To make this easier, a new condition,
IgnoredCondition
is provided which composes another condition. It will ignore any failures of the composed condition.In addition,
NoFailedDependenciesCondition
now supports an initialiser where it will ignore any dependencies which are also ignored, rather than failing for cancelations. This can be used like this:dependency.addCondition(IgnoredCondition(myCondition)) operation.addDependency(dependency) operation.addCondition(NoFailedDependenciesCondition(ignoreCancellations: true))
Note that the
ignoreCancellations
argument defaults to false to maintain previous behaviour. Thanks to @aurelcobb for raising this issue.
-
OPR-361
GroupOperation
’s queue is now private.Given the way that
GroupOperation
works, critically that it acts as its queue’s delegate, this change restricts the ability for that contract to be broken. Specifically, the queue is now private, but its properties are exposed via properties of the group.Additionally, the default initialiser of
GroupOperation
now takes an optionaldispatch_queue_t
argument. This dispatch queue is set as theNSOperationQueue
’sunderlyingQueue
property, and effectively allows the class user to set a target dispatch queue. By default this isnil
.This change will require changes to subclasses which override the default initialiser.
Thanks to @swiftlyfalling for these changes.
- OPR-377 GatedOperation will now cancel its composed operation if the gate is closed.
- OPR-365 Fixes a log message error. Thanks to @DeFrenZ for this one.
- OPR-382 Fixes an issue where
TaskOperation
was included in non-macOS platforms via CocoaPods.
🚀🙌 After a significant period of testing, Version 3.0.0 is finally here! Checkout the details below:
The protocol OperationCondition
is not deprecated. Instead, conditions should be refactored as subclasses of Condition
or ComposedCondition
. Condition itself is an Operation
subclass, and there is now support in Operation
for adding conditions like this. Internally Operation
manages a group operation which is added as a dependency. This group evaluates all of the conditions.
- [OPR-286]: Conditions are now subclasses of
Condition
andOperation
subclass. - [OPR-309]: Fixes a bug with
ComposedCondition
.
- [OPR-293]: Adds
WillCancelObserver
- use will/did cancel observer to handle cancellation. - [OPR-319]: Improvements to invoking observers in
Operation
. - [OPR-353]: Fixes a bug with Swift 2.* where weak properties are not thread safe when reading. @swiftlyfalling for fixing this one!
- [OPR-330]: Ensures that
NSOperationQueue.mainQueue()
returns anOperationQueue
instance. Thanks to @gtchance for this - great spot! - [OPR-359]:
Operation.cancel()
is now final, which means that it cannot be overridden. To support effective cancelling inOperation
subclasses, attachWillCancelObserver
andDidCancelObserver
observers to the operation before it is added to a queue. Thanks to @swiftlyfalling for adding this. - [OPR-358]: @swiftlyfalling has done a fantastic job fixing an assortment of thread safety issues in
Operation
andGroupOperation
. Now cancellation, finishing, logs, and adding operations to groups is a lot safer.
- [OPR-305, OPR-306]: Fixes a bug where
CLLocationManager
would respond with a status of not determined prematurely in some cases. Thanks to @J-Swift for the fix! - [OPR-321]: Adds support for checking if the current queue is the main queue, without using
NSThread.isMainThread()
API. This technique is used to ensure thatCLLocationManager
is always created on the main queue, regardless of the calling queue. This allows for location operations to be run insideGroupOperation
s for example. Thanks again to @J-Swift for reporting this one! - [OPR-304]: Vastly improved support for CloudKit errors. Each
CKOperation
defines its own CloudKit error type which provides direct support for managing its subtypes. For example,CKMarkNotificationsReadOperation
uses anErrorType
ofMarkNotificationsReadError<NotificationID>
which stores the marked notification IDs. These error types allow framework consumers to provide effective error handling forCKPartialError
for example. - [OPR-327]: Removes reachability from
CLoudKitOperation
, now, network reachability will be handled as recommended by Apple, which is to retry using the error information provided. This is in contrast to waiting for the network to be reachable. - [OPR-312]: Supports the
enterReaderIfAvailable
configuration ofSFSafariViewController
withWebpageOperation
. This defaults to false. Thanks to @blg-andreasbraun for adding this! - [OPR-315]: Refactors
WebpageOperation
to subclassComposedOperation
. Thanks to @blg-andreasbraun for tidying this up! - [OPR-317]: Adds an
OpenInSafariOperation
. Thanks to @blg-andreasbraun for adding this! - [OPR-334, OPR-351]: Updates
AlertController
to support action sheets withUIAletController
. Thanks, again, to @blg-andreasbraun, for fixing this! - [OPR-329]: Added support for
GroupOperation
subclasses to recover from errors. Thanks to @gsimmons for reporting this issue! - [OPR-348]: Added the ability for
RepeatedOperation
to reset its configuration block. - [OPR-294]: Adds very simplistic support to
CloudKitOperation
to handleCKLimitExceeded
. Framework consumers should bear in mind however, that this is quite simplistic, and if your object graph uses many (or any)CKReferences
be careful here. It is generally advised to updateCKReferences
first.
- [OPR-302]: Fixes a incorrect Fix-It hint
- [OPR-303]: Fixes
.Notice
severity logs. - [OPR-310]: Removes an unnecessary
let
statement. Thanks to @pomozoff for this one! - [OPR-324]: Exposes the
LogSeverity
value to Objective-C. Thanks to @J-Swift for this one! - [OPR-341]: Makes
UserIntent
accessible from Objective-C. Thanks @ryanjm for this one! - [OPR-338]: Thanks to @ryanjm for fixing an issue which caused the
NetworkObserver
to flicker. - [OPR-350]: Turns on Whole Module Optimization for Release configuration builds. Whoops! Sorry!
This is a pretty big release. Thanks so much to all the contributors. I promise that 3.1 will not be too far behind.
- [OPR-305]: Resolves an issue where
Capability.Location
can finish early. This can happen on subsequent permission challenges if the app is closed while the permission alert is on screen. It appears to be some slightly unexpected behavior ofCLLocationManager
informing its delegate immediately that the status is.NotDetermined
. Lots of thanks to J-Swift for finding, explaining to me, and providing a fix for this issue!
- [OPR-256]: When a
GroupOperation
is cancelled with errors, the child operations in the group are also cancelled with those errors wrapped inside anOperationError.ParentOperationCancelledWithErrors
error. Thanks to @felix-dumit and @jshier for contributing. - [OPR-257, OPR-259]: Improves the README to give much clearer documentation regarding the need for an
OperationQueue
instance, instead of just a regularNSOperationQueue
. Thanks to @DavidNix for raising the initial issue. - [OPR-265]: Defines
Operation.UserIntent
. This is a simple type which can be used express the intent of the operation. It allows for explicit user action (.Initiated
), a side effect of user actions (.SideEffect
), and.None
for anything else, which is the default.Operation
will use this value to set the quality of service (QoS) of the operation. The reason for separatingUserIntent
from the QoS, is that it is not possible to accurately determine the intent from the QoS because anNSOperation
's QoS can be modified when it is added to a queue which has a different QoS, or even if it is already on a queue, which has anotherNSOperation
with a different QoS added to the same queue. See the documentation on Quality of Service classes. - [OPR-266]: Thanks for @estromlund for fixing this bug - now network errors are passed into the errors of the network operation.
- [OPR-273]:
AlertOperation
can now be customized to display action sheet style alerts. Thanks to @felix-dumit for writing this one! - [OPR-281]:
BlockOperation
now supports blocks which throw errors. The errors are caught and processed byOperation
correctly. Thanks to @ryanjm for reporting and contributing! - [OPR-292]: Fixes a bug accessing
PHPhotoLibrary
. Thanks to @ffittschen for reporting this bug! - [OPR-285]: Fixes the watchOS target which had CloudKit references in it. Thanks to @vibrazy and the ASOS team for this one!
- [OPR-290]: Fixes a typo - thanks @waywalker!
- [OPR-296]: Updates the
.gitignore
for Swift Package Manager. Thanks to @abizern for contributing. - [OPR-269]: Fixes a bug with
NSURLSessionTaskOperation
where it could crash if it is not safely finished. Please report any bugs with this class, as at the moment it is not very well tested.
This is an interim release before some significant breaking changes get merged, for version 3.0.
Thanks a lot for everyone who has contributed!
- [OPR-282]: Fixes a bug accessing
PHPhotoLibrary
throughCapability.Photos
. - [OPR-285]: Removes CloudKit files from the watchOS target.
This is a patch release to get these fixes released.
- [OPR-241]: Makes change for Xcode 7.3 and Swift 2.2.
- [OPR-251]: Refactors how Capabilities work with their generic registrars.
Got there in the end! Thanks everyone for helping out during the change to Swift 2.2 - by the time Swift 3.0 comes around, I’ll hopefully have a fully automated CI system in place for switching up toolchains/build.
- [OPR-250]: Thanks to @felix-dumit for making the cancellation of
GroupOperation
more sensible and consistent. Essentially now the group will cancel after all of its children have cancelled.
This should be the last bug release before v2.9.0 and Swift 2.2 is released.
Also, before v2.10 is released, if you happen to use Operations framework in your app or team, and would agree to having a logo displayed in the README - please get in touch!
- [OPR-245]: Thanks to @difujia for spotting and fixing a really clear retain cycle in
ComposedOperation
. Good tip - is to remember that an operation will retain its observers, meaning that if an operation owns another operation, and acts as its observer, then it will create a retain cycle. The easy fix is to use block based observers, with a capture list of[unowned self]
. - [OPR-246]: Another bug fix from @difujia for a race condition when adding operation which have mutual exclusive dependencies. My bad! Thanks Frank!
Just a quick note - these bug fixes are both being released now as 2.8.1 for Swift 2.1 & Xcode 7.2. The same fixes will be pulled into the development
branch which is shortly going to become Swift 2.2, although it isn't yet. Bear with me - as that should happen over the weekend.
🚀 This will be the last minor release for Swift 2.1.1. From here on development of new features will be in Swift 2.2 😀.
Yet again, this release features more contributors - thanks a lot to @estromlund, @itsthejb, @MrAlek and @felix-dumit for finding bugs and fixing them!
Also, I’m pretty happy to report that adoption and usage of this framework has been seeing somewhat of an uptick! According to the stats on CocoaPods, we’re seeing almost 2,500 downloads/week and over used by over 120 applications 🎉! The support from the Swift community on this has been pretty amazing so far - thanks everyone 😀🙌!
-
[OPR-233]: Thanks to @estromlund & @itsthejb for fixing a bug which would have caused retain cycles when using result injection.
-
[OPR-225]: Adds a unit test to check that
Operation
callsfinished()
. This was a bit of a followup to the fixes in 2.7.1. -
[OPR-208,OPR-209]: Thanks to @itsthejb who remove the
HostReachabilityType
from the arguments ofReachabilityCondition
which allows it to be more easily consumed. It’s now access via a property in unit tests. -
[OPR-210]: Thanks to @itsthejb (again!) for improving the logic for ReachabilityCondition.
-
[OPR-226]: Some improvements to the unit tests to fix some failures on development.
-
[OPR-224]: Use
.Warning
log severity when logging errors inOperation
. Thanks again to @itsthejb for this one. -
[OPR-227]: Sets the log severity to
.Fatal
for the unit tests. -
[OPR-229]: Thanks to @estromlund for fixing a bug from 2.7.0 where the automatic result injection was done using a
DidFinishObserver
instead ofWillFinishObserver
which was causing some race conditions. -
[OPR-231]: Removes
self
from the default operation name - which due to the@autoclosure
nature of the log message could cause locking issues. -
[OPR-234]: Thanks to @MrAlek for fixing a bug (causing a race condition) when cancelling a
GroupOperation
. -
[OPR-236]: Thanks to @felix-dumit for fixing a bug where an
AlertOperation
would finish before its handler is called. -
[OPR-239]: Adds
GroupOperationWillAddChildObserver
observer protocol. This is only used byGroupOperation
and can be use to observer when child operations are about to be added to the group’s queue. -
[OPR-235]: New Observer:
OperationProfiler
.An
OperationProfiler
can be added as an observer to anOperation
instance. It will report a profile result which contains the timings for the lifecycle events of the operation, from created through attached, started to cancelled or finished.By default, a logging reporter is added, which will print the profile information to the
LogManager
’s logger. This is done like this:let operation = MyBigNumberCrunchingOperation() operation.addObserver(OperationProfiler()) queue.addOperation(operation)
However, for customized reporting and analysis of profile results, create the profiler with an array of reporters, which are types conforming to the
OperationProfilerReporter
protocol.In most cases doing any kind of profiling of applications in production is unnecessary and should be avoided.
However, in some circumstances, especially with applications which have very high active global users, it is necessary to gain a holistic view of an applications performance. Typically these measurements should be tied to networking operations and profiling in back end systems. The
OperationProfiler
has deliberately designed with a view of using custom reporters. The built in logging reporter should only really be used as debugging tool during development.In addition to profiling regular “basic”
Operation
instances. The profiler will also measure spawned operations, and keep track of them from the parent operation’s profiler. Operations can be spawned by callingproduceOperation()
or by using aGroupOperation
. Regardless, the profiler’s results will reference both as “children” in the same way.WARNING: Use this feature carefully. If you have not written a custom reporter class, there is no need to add profilers to operations in production.
- [OPR-219]: Fixes an issue after refactoring Operation which would prevent subclasses from overriding
finished(errors: [ErrorType])
.
🚀 This release continues the refinement of the framework. Thanks again to everyone who has contributed!
-
[OPR-152, OPR-193, OPR-195, OPR-201]: This is a breaking change which significantly improves Operation observers.
- Observers can be safely added to an Operation at any point in its lifecycle.
- Observers can implement a callback which is executed when there are attached to the operation.
- All the block based observers have labels on their arguments.
Thanks to @jshier for reporting this one.
-
[OPR-193]: Thanks to @seancatkinson who made improvements to
UIOperation
making it possible to specify whether the controller should be wrapped in aUINavigationController
. -
[OPR-199]: Refactored the initializers of
RepeatedOperation
to make it far easier for framework consumers to subclass it - thanks @jshier for reporting this one. -
[OPR-197]: Fixes a bug where errors from nested
GroupOperation
s were not propagating correctly - thanks to @bastianschilbe for reporting this one. -
[OPR-204]: Fixes a typo in the README - thanks to @Augustyniak.
-
[OPR-214]: Moves code coverage reporting from Codecov.io to Coveralls.
-
[OPR-164]: Adds initial support for Swift Package Manager - no idea if this actually works yet though.
-
[OPR-212]: Removes the example projects from the repo. They are now in the @danthorpe/Examples repo. This was done as a safer/better fix for the issue which was resolved in v2.6.1. Essentially because Carthage now builds all Xcode projects that it can finds, it will attempt to build any example projects in the repo, and because Carthage does not have the concept of “local dependencies” these example projects are setup using CocoaPods. And I really don’t like to include the
Pods
folder of dependencies in repositories as it just take longer to checkout. So, this was causing Carthage to exit because it couldn’t build these exampled. So, I’ve moved them to a new repo. -
[OPR-216]: Adds SwiftLint to the project & CI, including fixes for all the issues which were warnings or errors.
-
[OPR-192]: Updates the
.podspec
to have more granular dependencies. For users ofCloudKitOperation
this is a breaking change, and you will need to update yourPodfile
:pod ‘Operations/+CloudKit’
Thanks to @itsthejb for this one.
-
[OPR-205, OPR-206]: Fixes a mistake where the Cloud Capability was not available on tvOS platform.
-
Temporary work around for an issue with Carthage versions 0.12 and later. In this version, Carthage now builds all Xcode projects it can find, which in this case is 4 projects because there are two examples. Those example projects use CocoaPods to setup their dependency on the Operations framework, using the "development pod" technique. I would prefer to not include their
Pods/
folder in the repo, however, without it, it becomes necessary to runpod update
before building - which Carthage (reasonably) does not do. Therefore they fail to build and Carthage exits.
🚀 This release contains quite a few changes, with over 230 commits with input from 11 contributors! Thanks! 😀🎉
A note on quality: test coverage has increased from 64% in v2.5 to 76%. The code which remains untested is either untestable (fatalError
etc) or is due for deletion or deprecation such as AddressBookCondition
etc.
-
[OPR-150]:
MapOperation
,FilterOperation
andReduceOperation
For advanced usage.These operations should be used in conjunction with
ResultOperationType
which was introduced in v2.5.0. Essentially, given an receiving operation, conforming toResultOperationType
, the result of mapping, filtering, or reducing the receiver’sresult
can be returned as theresult
of another operation, which also conforms toResultOperationType
. This means that it can be trivial to map the results of one operation inside another.It is suggested that this is considered for advanced users only as it’s pretty subtle behavior.
-
[OPR-154, OPR-168]:
RepeatedOperation
The
RepeatedOperation
is aGroupOperation
subclass which can be used in conjunction with a generator to scheduleNSOperation
instances. It is useful to remember thatNSOperation
is a “one time only” class, meaning that once an instance finishes, it cannot be re-executed. Therefore, it is necessary to construct repeatable operations using a closure or generator.This is useful directly for periodically running idempotent operations. It also forms the basis for operation types which can be retried.
The operations may optionally be scheduled after a delay has passed, or a date in the future has been reached.
At the lowest level, which offers the most flexibility,
RepeatedOperation
is initialized with a generator. The generator (something conforming toGeneratorType
) element type is(Delay?, T)
, whereT
is aNSOperation
subclass, andDelay
is an enum used in conjunction withDelayOperation
.RepeatedOperation
can also be initialized with a simple() -> T?
closure andWaitStrategy
. The strategy offers standardized delays such as.Random
and.ExpoentialBackoff
, and will automatically create the appropriateDelay
.RepeatedOperation
can be stopped by returningnil
in the generator, or after a maximum count of operations, or by callingcancel()
.Additionally, a
RepeatableOperation
has been included, which composes anOperation
type, and adds convenience methods to support whether or not another instance should be scheduled based on the previous instance. -
[OPR-154, OPR-161, OPR-168]:
RetryOperation
RetryOperation
is a subclass ofRepeatedOperation
, except that instead of repeating irrespective of the finishing state of the previous instance,RetryOperation
only repeats if the previous instance finished with errors.Additionally,
RetryOperation
is initialized with an “error recovery” block. This block receives various info including the errors from the previous instance, the aggregate errors so far, aLoggerType
value, plus the suggested(Delay, T?)
tuple. This tuple is the what theRetryOperation
would execute again without any intervention. The error block allows the consumer to adjust this, either by returning.None
to not retry at all, or by modifying the return value. -
[OPR-160, OPR-165, OPR-167]:
CloudKitOperation
2.0Technically, this work is a refactor of
CloudKitOperation
, however, because it’s a major overhaul it is best viewed as completely new.CloudKitOperation
is a subclass ofRetryOperation
, which composes theCKOperation
subclass inside aReachableOperation
.CloudKitOperation
can be used to scheduleCKOperation
subclasses. It supports configuration of the underlyingCKOperation
instance “through” the outerCloudKitOperation
, where the configuration applied is stored and re-applied on new instances in the event of retrying. For example, below// Modify CloudKit Records let operation = CloudKitOperation { CKModifyRecordsOperation() } // The user must be logged into iCloud operation.addCondition(AuthorizedFor(Capability.Cloud())) // Configure the container & database operation.container = container operation.database = container.privateCloudDatabase // Set the records to save operation.recordsToSave = [ recordOne, recordTwo ] // Set the policy operation.savePolicy = .ChangedKeys // Set the completion operation.setModifyRecordsCompletionBlock { saved, deleted in // Only need to manage the happy path here }
In the above example, all the properties set on
operation
are saved into an internal configuration block. This is so that it in the case of retrying after an error, the same configuration is applied to the newCKOperation
instance returned from the generator. The same could also be achieved by setting these properties inside the initial block, however the completion block above must be called to setup theCloudKitOperation
correctly.Thanks to
RetryOperation
,CloudKitOperation
supports some standardized error handling for common errors. For example, if Apple’s CloudKit service is unavailable, your operation will be automatically re-tried with the correct delay. Error handling can be set for individualCKErrorCode
values, which can replace the default handlers if desired.CKOperation
subclasses also all have completion blocks which receives the result and an optional error. As discussed briefly above,CloudKitOperation
provides this completion block automatically when the consumer sets the “happy path” completion block. The format of this function is alwaysset<Name of the CKOperation completion block>()
This means, that it is only necessary to set a block which is executed in the case of no error being received.BatchedCloudKitOperation
is aRepeatedOperation
subclass which composedCloutKitOperation
instances. It can only be used withCKOperation
subclasses which have the notion of batched results.See the class header, example projects, blog posts and (updated) guide for more documentation. This is significant change to the existing class, and should really be viewed as entirely new. Please get in touch if you were previously using
CloudKitOperation
prior to this version, and are now unsure how to proceed. I’m still working on improving the documentation & examples for this class.
-
[OPR-169]: Last Opened example project
Last Opened, is the start of an iOS application which will demonstrate how to use the new
CloudKitOperation
. At the moment, it is not exactly complete, but it does show some example. However, the application does not compile until the correct development team & bundle id is set. -
[OPR-171]:
CloudKitOperation
documentationThere is now quite a bit of public interface documentation. Still working on updating the programming guide right now.
-
[OPR-152]: Adding Conditions & Observers
When adding conditions and observers, we sanity check the state of the operation as appropriate. For adding a Condition, the operation must not have started executing. For adding an Observer, it now depends on the kind, for example, it is possible to add a
OperationDidFinishObserver
right up until the operation enters its.Finishing
state. -
[OPR-147]: Scheduling of Operations from Conditions
When an Operation has dependencies and also has Conditions attached which also have dependencies, the scheduling of these dependencies is now well defined. Dependencies from Conditions are referred to as indirect dependencies versus direct for dependencies added normally.
The indirect dependencies are now scheduled after all the direct dependencies finish. See original issue and the pull request for further explanation including a diagram of the queue.
-
[OPR-129]: Dependencies of mutually exclusive Conditions.
If a Condition is mutually exclusive, the
OperationQueue
essentially adds a lock on the associatedOperation
. However, this previously would lead to unexpected scheduling of that condition had a dependency operation. Now, the “lock” is placed on the dependency of the condition instead of the associated operation, but only if it’s not nil. Otherwise, standard behavior is maintained. -
[OPR-162]: Refactor of
ComposedOperation
andGatedOperation
Previously, the hierarchy of these two classes was all mixed up.
ComposedOperation
has been re-written to support bothOperation
subclasses andNSOperation
subclasses. When aNSOperation
(but notOperation
) subclass is composed, it is scheduled inside its ownGroupOperation
. However, if composing anOperation
subclass, instead we “produce” it and use observers to finish theComposedOperation
correctly.Now,
GatedOperation
is a subclass ofComposedOperation
with the appropriate logic. -
[OPR-163, OPR-171, OPR-179]: Refactor of
ReachableOperation
ReachableOperation
now subclassesComposedOperation
, and usesSCNetworkReachablity
callbacks correctly. -
[OPR-187]: Sanity check
produceOperation()
. Thanks to @bastianschilbe for this fix. Now theOperation
must at least have passed the.Initialized
state beforeproduceOperation()
can be called.
-
[OPR-182]: Extension Compatible
Updates the extension compatible Xcode project. Sorry this got out of sync for anyone who was trying to get it to work!
-
[OPR-180]: Completion Blocks.
Changes in this pull request improved the stability of working with
OperationCondition
s attached toOperation
instances. However, there is still a bug which is potentially an issue with KVO.Currently it is suggested that the
completionBlock
associated withNSOperation
is avoid. Other frameworks expressly forbid its usage, and there is even a Radar from Dave De Long recommending it be deprecated.The original issue, #175 is still being tracked.
-
[OPR-181]: Fixes a bug in
GroupOperation
where many child operations which failed could cause a crash. Now access to theaggregateErrors
property is thread safe, and this issue is tested with a tight loop of 10,000 child operations which all fail. Thanks to @ansf for reporting this one.
I want to say a huge thank you to everyone who has contributed to this project so far. Whether you use the framework in your apps (~ 90 apps, 6k+ downloads via CocoaPods metrics), or you’ve submitted issues, or even sent me pull requests - thanks!
I don’t think I’d be able to find anywhere near the number of edge cases without all the help. The suggestions and questions from everyone keeps me learning new stuff.
Cheers, Dan
I’ve not got anything too major planned right now, except improving the example projects. So the next big thing will probably be Swift 3.0 support, and possibly ditching NSOperation
.
- [OPR-151, OPR-155]: Fixes a bug where
UserLocationOperation
could crash when the LocationManager returns subsequent locations after the operation has finished.
This is a relatively large number of changes with some breaking changes from 2.4.*.
-
[OPR-140]:
OperationObserver
has been refactored to refine four different protocols each with a single function, instead of defining four functions itself.The four protocols are for observing the following events: did start, did cancel, did produce operation and did finish. There are now specialized block observers, one for each event.
This change is to reflect that observers are generally focused on a single event, which is more in keeping with the single responsibility principle. I feel this is better than a single type which typically has either three no-op functions or consists entirely of optional closures.
To observe multiple events using blocks: add multiple observers. Alternatively, create a bespoke type to observe multiple events with the same type.
BlockObserver
itself still exists, however its usage is discouraged and it will be removed at a later time. It may also be necessary to make syntactic changes to existing code, in which case, I recommend replacing its usage entirely with one or more ofStartedObserver
,CancelledObserver
,ProducedOperationObserver
orFinishedObserver
, all of which accept a non-optional block. -
[OPR-139]: Removed
Capabiltiy.Health
. Because this capability imports HealthKit, it is flagged by the app review team, and an application may be rejecting for not providing guidance on its usage of HealthKit. Therefore, as the majority of apps probably do not use this capability, I have removed it from the standard application framework. It is available as a subspec through Cocoapods:pod 'Operations/+Health'
-
[OPR-121,OPR-122, OPR-126, OPR-138]: Improves the built in logger. So that now:
- the message is enclosed in an
@autoclosure
. - there is a default/global severity threshold
- there is a global enabled setting.
Thanks to Jon (@jshier) for raising the initial issue on this one.
- the message is enclosed in an
-
[OPR-128]: Improves how code coverage is generated.
Thanks to Steve (@stevepeak) from Codecov.io for helping with this.
-
[OPR-133, OPR-134]:
DelayOperation
andBlockOperation
have improved response to being cancelled.Thanks to Jon (@jshier) for raising the initial issue on this one.
-
[OPR-132]:
BlockObserver
now supports a cancellation handler. However see the notes regarding changes toOperationObserver
andBlockObserver
above under breaking changes. -
[OPR-135,OPR-137]: Result Injection.
It is now possible to inject the results from one operation into another operation as its requirements before it executes. This can be performed with a provided block, or automatically in the one-to-one, result-to-requirement case. See the programming guide for more information.
Thanks very much to Frank (@difujia) for the inspiration on this, and Jon (@jshier) for contributing to the discussion.
-
[OPR-141]:
Operation
now usesprecondition
to check the expectations of public APIs. These are called out in the function’s documentation. Thanks to the Swift evolution mailing list & Chris Lattner on this one. -
[OPR-144, OPR-145]: Supports adapting the internal logger to use 3rd party logging frameworks. The example project uses SwiftyBeaver as a logger.
Thanks to Steven (@shsteven) for raising the issue on this one!
-
[OPR-148]: Location operations now conform to
ResultOperationType
which means their result (CLLocation
,CLPlacemark
) can be injected automatically into appropriate consuming operations.
-
[OPR-124]: Fixes a bug where notification names conflicted.
Thanks to Frank (@difujia) for this one.
-
[OPR-123,OPR-125, OPR-130]: Fixes a bug where a completion block would be executed twice.
Thanks again to Frank (@difujia) for raising the issue.
-
[OPR-127, OPR-131]: Fixes a bug where an operation could fail to start due to a race condition. Now, if an operation has no conditions, rather than entering a
.EvaluatingConditions
state, it will immediately (i.e. synchronously) become.Ready
.Thanks to Kevin (@kevinbrewster) for raising this issue.
-
[OPR-142]:
Operation
now checks the current state in comparison to.Ready
before adding conditions or operations. This is unlikely to be a breaking change, as it is not a significant difference.Thanks to Frank (@difujia) for this one.
-
[OPR-146]: Fixes a subtle issue where assessing the readiness could trigger state changes.
Thanks to @difujia, @jshier, @kevinbrewster, @shsteven and @stevepeak for contributing to this version. :)
- [OPR-113]: Fixes an issue where building against iOS 9 using Carthage would unleash a tidal wave of warnings related to ABAddressBook. Thanks to @JaimeWhite for bringing this to my attention!
- [OPR-114]: Reorganized the repository into two project files. One create standard frameworks for applications, for iOS, watchOS, tvOS, and OS X. The other creates Extension API compatible frameworks for iOS, tvOS and OS X. At the moment, if you wish to use an API extension compatible framework with Carthage - this is a problem, as Carthage only builds one project, however the is a Pull Request which will fix this. The issue previously was that the
Operations.framework
products would overwrite each other. I’ve tried everything I can think of to make Xcode produce a product which has a different name to its module - but it won’t let me. So.. the best thing to do in this case is use CocoaPods and the Extension subspec. - [OPR-115]: Fixes an issue with code coverage after project reorganization.
- [OPR-116]: Fixes a mistake where
aggregateErrors
was not publicly accessible inGroupOperation
. Thanks to @JaimeWhite for this.
Thanks a lot to @JaimeWhite for helping me find and squash some bugs with this release. Greatly appreciated!
-
[OPR-108]: Adds an internal logging mechanism to
Operation
. Output log information using thelog
property of the operation. This property exposes simple log functions. E.g.let operation = MyOperation() // etc operation.log.info(“This is a info message.”)
To use a custom logger, create a type conforming to LoggerType
and add an instance property to your Operation
subclass. Then override getLogger()
and setLogger(: LoggerType)
to get/set your custom property.
The global severity is set to .Warning
, however individual operation loggers can override to set it lower, e.g.
```swift
operation.log.verbose(“This verbose message will not be logged, as the severity threshold is .Warning”)
operation.log.severity = .Verbose
operation.log.verbose(“Now it will be logged.”)
```
- [OPR-109]: Added documentation to all of the Capability (device permissions etc) functionality. Also now uses Jazzy categories configuration to make the generated documentation more easily navigable. Documentation is hosted on here: docs.danthorpe.me/operations.
- [OPR-89]: Adds support (via subspecs) for watchOS 2 and tvOS apps.
- [OPR-101]: Fixes a bug where
ReachableOperation
may fail to start in some scenarios. - [OPR-102]: Adds more documentation to the finish method of Operation. If it’s possible for an Operation to be cancelled before it’s started, then do not call finish. This is mostly likely a possibility when writing network operations and cancelling groups.
- [OPR-103]: Adds % of code covered by tests to the README. Service performed by CodeCov.
- [OPR-104]: Maintenance work on the CI scripts, which have now moved to using a build pipeline which is uploaded to BuildKite and executed all on the same physical box. See post on danthorpe.me.
- [OPR-105]: Improves the testability and test coverage of the Reachability object.
- [OPR-106]: Adds more tests to the AddressBook swift wrapper, increases coverage of
Operation
,NegatedCondition
&UIOperation
.
- [OPR-100]: Adds documentation to all “Core” elements of the framework. Increases documentation coverage from 8% to 22%. Still pretty bad, but will get there eventually.
- [OPR-91, OPR-92]: Fixes a bug in AddressBook when building against iOS 9, where
Unmanaged<T>
could be unwrapped incorrectly. - [OPR-93, OPR-95]: Adds support for Contacts.framework including
ContactsCondition
plus operations forGetContacts
,GetContactsGroup
,RemoveContactsGroup
,AddContactsToGroup
andRemoveContactsFromGroup
in addition to a base contacts operation class. Also included are UI operationsDisplayContactViewController
andDisplayCreateContactViewController
. - [OPR-97, OPR-98]: Refactors how device authorization permissions are checked and requested. Introduces the concept of a
CapabilityType
to govern authorization status and requests. This works in tandem with new operationsGetAuthorizationStatus<Capability>
andAuthorize<Capability>
with an operation conditionAuthorizedFor<Capability>
. The following conditions are now deprecated:CalendarCondition
,CloudContainerCondition
,HealthCondition
,LocationCondition
,PassbookCondition
,PhotosCondition
in favor ofCapability.Calendar
,Capability.Cloud
,Capability.Heath
,Capability.Location
,Capability.Passbook
,Capability.Photos
respectively. Replace your condition code like this example:
operation.addCondition(AuthorizedFor(Capability.Location(.WhenInUse)))
- [OPR-90]: Multi-platform support. Adds new framework targets to the project for iOS Extension only API framework. This doesn’t have support for BackgroundObserver, or NetworkObserver for example. Use
pod ‘Operations/Extension’
to use it in a Podfile for your iOS Extension target. Also, we have Mac OS X support (no special pod required). And watchOS support - usepod ‘Operations/watchOS’
.
- [OPR-87]: Improves the reliability of the reverse geocoder unit tests.
- [OPR-62, OPR-86]: Fixes a bug in Swift 2.0 where two identical conditions would cause a deadlock. Thanks to @mblsha.
- [OPR-85]: Fixes up the Permissions example project for Swift 2.0. Note that YapDatabase currently has a problem because it has some weak links, which doesn’t work with Bitcode enabled, which is default in Xcode 7. This PR just turned off Bitcode, but if you re-run the Pods, then that change will be over-ridden. What you can do instead, if YapDatabase is still not fixed is to use my fork which has a fix on the
YAP-180
branch.
This is the Swift 2.0 compatible version.
- [OPR-79]: Adds more documentation to the types.
- [OPR-83]: Adds some convenience functions to
NSOperation
andGroupOperation
for adding multiple dependencies at once, and multiple operations to a group before it is added to a queue.
This is a release for Swift 1.2 compatible codebases.
- [OPR-74]: Work in progress on AddressBook external change request. Warning so not use this, as I cannot actually get this working yet.
- [OPR-75]: Fixes a serious bug where attempting to create an ABAddressBook after previously denying access executed a fatalError.
- [OPR-63]: Speeds up the test suite by 40 seconds.
- [OPR-65]: Adds a generic
UIOperation
class. Can be used to show view controllers, either present modally, show or show detail presentations. It is used as the basis forAlertOperation
, and theAddressBookDisplayPersonController
,AddressBookDisplayNewPersonController
operations. - [OPR-67]: Adds reverse geocode operations. Supply a
CLLocation
toReverseGeocodeOperation
directly. Or useReverseGeocodeUserLocationOperation
to reverse geocode the user’s current location. Additionally,LocationOperation
has been renamed toUserLocationOperation
. - [OPR-68]: General improvements to the
AddressBook
APIs including acreatePerson
function, plus addition of missing person properties & labels. Additionally, fixes a bug in setting multi-value string properties. - [OPR-71]: Updates the unit test scripts to use Fastlane, same as Swift 2.0 branch.
Refactor of AddressBook.framework related functionality. The AddressBookOperation
is no longer block based, but instead keeps a reference to the address book as a property. This allows for superior composition. Additionally there is now an AddressBookGetResource
operation, which will access the address book, and then exposes methods to read people, and if set, an individual person record and group record.
Additionally, there is now operations for adding/removing a person to a group. Add/Remove groups. And map all the people records into your own type.
Internally, these operations are supported by a Swift wrapper of the AddressBook types, e.g. AddressBookPerson
etc. This wrapper is heavily inspired by the Gulliver. If you want more powerful AddressBook features, I suggest you checkout that project, and then either subclass the operations to expose Gulliver types, or write a simple protocol extension to get Gulliver types from AddressBookPersonType
etc etc.
- [OPR-57]: The CloudKitOperation is no longer a GroupOperation, just a standard Operation, which enqueues the
CKDatabaseOperation
onto the database’s queue directly. - [OPR-58]: Added
ComposedOperation
which is a specializedGatedOperation
which always succeeds. This is handy if you want to add conditions or observers to anNSOperation
. - [OPR-60]: Renamed
NoCancellationsCondition
toNoFailedDependenciesCondition
which encompasses the same logic, but will also fail if any of the operation’s dependencies areOperation
subclasses which have failed. In addition,Operation
now exposes all it’s errors via theerrors
public property.
- [OPR-14]: Supports Photos library permission condition.
- [OPR-16]: Supports Health Kit permission condition.
- [OPR-11]: Supports Passbook condition.
- [OPR-13]: Supports a EventKit permission condition.
- [OPR-17]: Supports remote notification permission condition.
- [OPR-18]: Supports user notification settings condition.
- [OPR-38]: Adds a
LocationOperation
demo to Permissions.app - [OPR-39]: Adds a user confirmation alert condition.
- [OPR-37]: Creates an example app called Permissions. This is a simple catalogue style application which will be used to demonstrate functionality of the Operations framework.
At the moment, it only shows Address Book related functionality. Including using combinations of SilentCondition
, NegatedCondition
and AddressBookCondition
to determine if the app has already got authorization, requesting authorization and performing a simple ABAddressBook related operation.
Additionally, after discussions with Dave DeLong, I’ve introduced changes to the underlying Operation’s state machine.
Lastly, the structure of BlockOperation
has been modified slightly to allow the task execution block to pass an error (ErrorType
) into the continuation block. Because closures cannot have default arguments, this currently means that it is required, e.g. continueWithError(error: nil)
upon success.
- [OPR-7]: Supports a condition which requires all of an operation’s dependencies to succeed.
- [OPR-12]: Adds
LocationOperation
andLocationCondition
. This allows for accessing the user’s location, requesting “WhenInUse” authorization. - [OPR-36]: Adds
AddressBookOperation
which allows for access to the user’s address book inside of a handler block (similar to aBlockOperation
). As part of this,AddressBookCondition
is also available, which allows us to condition other operation types.
- [OPR-5]: Supports silent conditions. This means that if a condition would normally produce an operation (say, to request access to a resource) as a dependency, composing it inside a
SilentCondition
will suppress that dependent operation. - [OPR-6]: Supports negating condition.
- [OPR-30]: Adds a
LoggingObserver
to log operation lifecycle events. - [OPR-33]: Adds
GatedOperation
which will only execute the composed operation if the supplied block evaluates true - i.e. opens the gate. - [OPR-34] & [OPR-35]: Adds a
ReachableOperation
. Composing an operation inside aReachableOperation
will ensure that it runs after the device regains network reachability. If the network is reachable, the operation will execute immediately, if not, it will register a Reachability observer to execute the operation when the network is available. Unlike theReachabilityCondition
which will fail if a host is not available, useReachableOperation
to perform network related tasks which must be executed regardless.
- [OPR-22]: Supports displaying a
UIAlertController
as aAlertOperation
. - [OPR-26]: Adds a Block Condition. This allows an operation to only execute if a block evaluates true.
- [OPR-27]: Fixes a bug where the
produceOperation
function was not publicly accessible. Thanks - @MattKiazyk - [OPR-28]: Supports a generic
Operation
subclass which wraps aCKDatabaseOperation
setting the providedCKDatabase
. - [OPR-29]: Improves the
CloudCondition.Error
to include.NotAuthenticated
for when the user is not signed into iCloud.
Base Operation
and OperationQueue
classes, with the following features.
The project has been developed using Xcode 7 and Swift 2.0, with unit testing (~ 75% test coverage). It has now been back-ported to Swift 1.2 for version 1.0 of the framework. Version 2.0 will support Swift 2.0 features, including iOS 9 technologies such as Contacts framework etc.
-
Operation types: 1.1.
BlockOperation
: run a block inside an operation, taking advantage of Operation features. 1.2.GroupOperation
: compose one more operations into a group. 1.3.DelayOperation
: delay execution of operations on the queue. -
Conditions Conditions can be attached to
Operation
s, and optionally introduce newNSOperation
instances to overcome the condition requirements. E.g. presenting a permission dialog. The following conditions are currently supported: 2.1.MutuallyExclusive
: for exclusivity of a given kind, e.g. to prevent system alerts presenting at the same time. 2.2.ReachabilityCondition
: only execute tasks when the device is online. 2.3.CloudCondition
: authorised access to a CloudKit container. -
Observers Observers can be attached to
Operation
s, and respond to events such as the operation starting, finishing etc. Currently observer types are: 3.1.BackgroundObserver
: when the app enters the background, a background task will automatically be started, and ended when the operation ends. 3.2.BlockObserver
: run arbitrary blocks when events occur on the observed operation. 3.3.NetworkObserver
: updates the status of the network indicator. 3.4.TimeoutObserver
: trigger functionality if the operation does not complete within a given time interval.