Releases: ProcedureKit/ProcedureKit
5.2.0
5.1.0
5.1.0
This will be the last update specifically supporting Xcode 10.1 and Swift 4.2.
New Procedures
-
[919]: JSON Coding procedures.
This changes introduced a generic
JSONDecodingProcedure
andJSONEncodingProcedure
which can be used to decode/decode aData
representing a UTF-8 encoded JSON string into a suitableCodable
type. The procedure allows full injection of theJSONDecoder
with a convenience initializer to user or override the default behavior. Additionally, theData
can be inject from a network procedure. For decoding errors - the procedure will fail with the coding error from theJSONDecoder
. This might be quite tricky to recover from and manage in production code, so in some cases it would make sense to decode into a type which can handle JSON error responses, e.g.{"message": "Failed to authorize"}
.
Other Changes
- [908, 909]: Updates the parameter names in method signature named
with: Error?
to includeerror
. This greatly improves code completion. Thanks to @pronebird and @juliensagot for these. - [906]: Updates to the logging mechanisms.
- [918]: The
Identity
property ofProcedure
now usesObjectIdentifier
andHasher
under the hood instead of UUIDs. Thanks to - [912]: Fixes some public accessor attributes for
NetworkRecovery
- thanks to @ericyanush for this one. - [923]: Added an integration point in CI to check that SwiftPM was correctly integrating.
- [924]: Fixes public accessor methods to the
LaunchRequest
type inProcessProcedure
.
5.0.0
5.0.0
This is a rather long-awaited next major version of ProcedureKit.
Headline Changes
- 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.
Breaking Changes
-
[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.
New Features & Improvements
-
[#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](https://github.com/ProcedureKit/ProcedureKit...
5.0.0 Beta 2
5.0.0
This is a rather long-awaited next major version of ProcedureKit. It is ready for Xcode 10 and Swift 4.2.
Headline Changes
- 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.
Breaking Changes
-
[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.
New Features & Improvements
-
[#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.
5.0.0 Beta 1
5.0.0
This is a rather long-awaited next major version of ProcedureKit.
Headline Changes
- 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.
Breaking Changes
-
[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.
New Features & Improvements
-
[#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](https://github.com/ProcedureKit/ProcedureKit...
4.5.0
4.5.0
Swift 4.0
- #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.
Improvements
- #781 Some significant performance and reliability improvements for
BackgroundObserver
by @swiftlyfalling.
Deprecations
- #818
UserIntent
property onProcedure
has been deprecated. Suggestion is to set the underlying queue priority.
4.4.0 (#805)
4.4.0
Breaking Change
- #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.
Others
4.3.2 (#794)
4.3.2
-
#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.
4.3.1 (#786)
4.3.1
- #785 To get round an error with Xcode 9 betas archiving applications.
4.3.0 (#780)
4.3.0
Documentation
- #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, this release's documentation is at procedure.kit.run/release/4.3.0.
- 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.
Other Improvements
- #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.
Other Notes
- 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.