Queuer is a queue manager, built on top of OperationQueue and Dispatch (aka GCD).
See the codeQueuer is a queue manager built on top of OperationQueue and Dispatch (aka GCD). It allows you to create asynchronous and synchronous tasks easily, all managed by a queue, with just a few lines.
| Swift | Queuer | iOS | macOS | macCatalyst | tvOS | watchOS | visionOS | Linux | Android | Windows |
|---|---|---|---|---|---|---|---|---|---|---|
| 3.1...3.2 | 1.0.0...1.1.0 | 8.0+ | 10.10+ | 9.0+ | 2.0+ | ✅ | ||||
| 4.0 | 1.3.0 | 8.0+ | 10.10+ | 9.0+ | 2.0+ | ✅ | ||||
| 4.1 | 1.3.1...1.3.2 | 8.0+ | 10.10+ | 9.0+ | 2.0+ | ✅ | ||||
| 4.2 | 2.0.0...2.0.1 | 8.0+ | 10.10+ | 9.0+ | 3.0+ | ✅ | ||||
| 5.0...5.10 | 2.1.0...2.2.0 | 8.0+ | 10.10+ | 9.0+ | 3.0+ | ✅ | ||||
| 5.9...5.10 | 3.0.0...3.0.1 | 12.0+ | 10.13+ | 13.0+ | 12.0+ | 4.0+ | 1.0+ | ✅ | ||
| 5.9...6.3 | 4.0.0 | 12.0+ | 10.13+ | 13.0+ | 12.0+ | 4.0+ | 1.0+ | ✅ | ✅ | ✅ |
[!NOTE] Some APIs require a newer OS than the minimum deployment target:
AsyncConcurrentOperation,addChainedAsyncOperations(_:completionHandler:),addAsyncCompletionHandler(_:),addBarrier(_:), andbarrier(_:)require macOS 10.15, iOS 13, tvOS 13, or watchOS 6.asyncWait(_:tolerance:clock:)requires macOS 13, iOS 16, tvOS 16, or watchOS 9.On Linux, Android, and Windows every API is always available.
See Requirements section to check Swift, Queuer, and OS versions.
In your Package.swift Swift Package Manager manifest, add the following dependency to your dependencies argument:
.package(url: "https://github.com/FabrizioBrancati/Queuer.git", from: "4.0.0"),
Add the dependency to any targets you've declared in your manifest:
.target(
name: "MyTarget",
dependencies: [
.product(name: "Queuer", package: "Queuer"),
]
),
Queuer offers a shared instance that you can use to add operations to a centralized queue:
Queuer.shared.addOperation(operation)
You can also create a custom queue:
let queue = Queuer(name: "MyCustomQueue")
You can even create a queue by defining the maxConcurrentOperationCount and the qualityOfService properties:
let queue = Queuer(name: "MyCustomQueue", maxConcurrentOperationCount: Int.max, qualityOfService: .default)
You have three methods to add an Operation block.
Directly on the queue(or Queuer.shared):
queue.addOperation {
/// Your task here
}
Creating a ConcurrentOperation with a block:
let concurrentOperation = ConcurrentOperation { _ in
/// Your task here
}
queue.addOperation(concurrentOperation)
[!NOTE] We will see how
ConcurrentOperationworks later.
Chained Operations are Operations that add a dependency each other.
They follow the given array order, for example: [A, B, C] = A -> B -> C -> completionBlock.
let concurrentOperationA = ConcurrentOperation { _ in
/// Your task A here
}
let concurrentOperationB = ConcurrentOperation { _ in
/// Your task B here
}
queue.addChainedOperations([concurrentOperationA, concurrentOperationB]) {
/// Your completion task here
}
You can also add a completionHandler after the queue creation with:
queue.addCompletionHandler {
/// Your completion task here
}
Group Operations are Operations that handles a group of Operations with a completion handler.
Allows the execution of a block of Operations with a completion handler that will be called once all the operations are finished, for example: [A -> [[B & C & D] -> completionHandler] -> E] -> completionHandler.
It should usually be used with a Chained Opetation.
let groupOperationA = GroupOperation(
[
ConcurrentOperation { _ in
/// Your task A here
},
ConcurrentOperation { _ in
/// Your task B here
}
]
)
let concurrentOperationC = ConcurrentOperation { _ in
/// Your task C here
}
queue.addChainedOperations([groupOperationA, concurrentOperationC]) {
/// Your completion task here
}
In this case the output will be the following one: [[A & B -> completionHandler] -> C] -> completionHandler.
There are a few method to handle the queue states.
Cancel all Operations in a queue:
queue.cancelAll()
Pause a queue:
queue.pause()
[!WARNING] By calling
pause()you will not be sure that everyOperationwill be paused. If theOperationis already started it will not be on pause until it's a customOperationthat overridespause()function.
Resume a queue:
queue.resume()
[!WARNING] To have a complete
pauseandresumestates you must create a customOperationthat overridespause()andresume()function.
Wait until all Operations are finished:
queue.waitUntilAllOperationsAreFinished()
[!IMPORTANT] This function means that the queue will blocks the current thread until all
Operations are finished.
You can control the ConcurrentOperation states by calling pause(), resume(), cancel().
[!NOTE] If you use a
Queuerobject with allConcurrentOperationobjects inside to manage your queue, you should avoid calling them directly on theConcurrentOperationobject, but you should call them on theQueuerone. TheQueuerobject will handle theConcurrentOperationstates automatically.
Pause a ConcurrentOperation:
concurrentOperation.pause()
Resume a ConcurrentOperation:
concurrentOperation.resume()
Cancel a ConcurrentOperation:
concurrentOperation.cancel()
For convenience, you can set closures to the ConcurrentOperation object to handle the states with onPause, onResume, and onCancel properties. They will be called when the ConcurrentOperation is paused, resumed, or canceled.
Setting the maxConcurrentOperationCount property of a queue to 1 will make you sure that only one task at a time will be executed.
If you want to create your custom Operation, the class ConcurrentOperation is a class ready to be subclassed.
It allows synchronous and asynchronous tasks, has a pause and resume states, can be easily added to a queue and can be created with a block.
You can create your custom ConcurrentOperation by subclassing it.
You must override execute() function and call the finish(success:) function, when the task has finished its job to notify the queue, regardless if it was successful or not.
An Operation is passed to every closure, with it you can set and handle the retry feature.
By default the retry feature is disabled, to enable it simply set the success property to false. With success to false the Operation will retry until reaches maximumRetries property value. To let the Operation know when everything is ok, you must set success to true.
With currentAttempt you can know at which attempt the Operation is.
let concurrentOperation = ConcurrentOperation { operation in
/// Your task here
if /* Successful */ {
operation.success = true
} else {
operation.success = false
}
}
You can throttle the automatic retries with the retryDelay property.
Every retry waits the given time before executing, the first attempt is never delayed.
Default is 0, retries happen immediately.
let concurrentOperation = ConcurrentOperation { operation in
/// Your task here
operation.success = false
}
concurrentOperation.retryDelay = 1
The same property is available on AsyncConcurrentOperation, where canceling the Operation also interrupts the delay right away:
let concurrentOperation = AsyncConcurrentOperation { operation in
/// Your asynchronous task here
operation.success = false
}
concurrentOperation.retryDelay = 1
You can also set it with the chainable syntactic sugar helper:
let concurrentOperation = ConcurrentOperation()
.retryDelay(1)
.executionBlock { operation in
/// Your task here
operation.success = false
}
You can manually retry an Operation when you think that the execution will be successful.
An Operation is passed to every closure, with it you can set and handle the retry feature.
By default the manual retry feature is disabled, to enable it simply set the manualRetry property to true, you must do this outside of the execution closure. You must also set success to true or false to let the Operation know when is everything ok, like the automatic retry feature.
To let the Operation retry your execution closure, you have to call the retry() function. Be sure to call it at least maximumRetries times, it is not a problem if you call retry() more times than is needed, your execution closure will not be executed more times than the maximumRetries value.
let concurrentOperation = ConcurrentOperation { operation in
/// Your task here
if /* Successful */ {
operation.success = true
} else {
operation.success = false
}
}
concurrentOperation.manualRetry = true
/// Later on your code
concurrentOperation.retry()
[!CAUTION] If the
retry()function is not called, you may block the entire queue.
By default the Operation will finish its job when the execution closure is completed.
This means, if you have an asynchronous task insiede the closure, the Operation will finish before the task is completed.
If you want to finish it manually, you can set the manualFinish property to true and call the finish(success:) function when the task is completed.
Call the finish(success:) function with success parameter to true or false to let the Operation know if the task was successful or not. If you don't explicitly set the success parameter, it will be set to true.
let concurrentOperation = ConcurrentOperation { operation in
/// Your asynchonous task here
}
concurrentOperation.manualFinish = true
/// Later on your code in your asynchronous task
concurrentOperation.finish(success: true)
[!CAUTION] If you don't call the
finish(success:)function, your queue will be blocked and it will never ends.
If you want to use async/await tasks, you need to set manualFinish to true in order to be fully in control of the Operation lifecycle.
[!NOTE] Read more about manually finish an
Operationhere.
let concurrentOperation = ConcurrentOperation { operation in
Task {
/// Your asynchonous task here
operation.finish(success: true) // or false
}
/// Your asynchonous task here
}
concurrentOperation.manualFinish = true
[!CAUTION] If you don't set
manualFinishtotrue, yourOperationwill finish before the async task is completed.
AsyncConcurrentOperation is an Operation built for async/await, available on macOS 10.15, iOS 13, tvOS 13, and watchOS 6 or later.
Its execution block is an async throwing function: a thrown error marks the attempt as failed and enables the retry feature, and canceling the Operation also cancels the Task running its block.
let concurrentOperation = AsyncConcurrentOperation { operation in
/// Your asynchronous task here, it can throw
try await yourAsyncTask()
}
concurrentOperation.addToQueue(queue)
You can chain them with an async completion handler:
queue.addChainedAsyncOperations([concurrentOperation1, concurrentOperation2]) {
/// Your asynchronous completion here
await yourAsyncCompletion()
}
A Scheduler is a struct that uses the GDC's DispatchSourceTimer to create a timer that can execute functions with a specified interval and quality of service.
let schedule = Scheduler(deadline: .now(), repeating: .seconds(1)) {
/// Your task here
}
You can even create a Scheduler without the handler and set it later:
var schedule = Scheduler(deadline: .now(), repeating: .seconds(1))
schedule.setHandler {
/// Your task here
}
With timer property you can access to all DispatchSourceTimer properties and functions, like cancel():
schedule.timer.cancel()
A Semaphore is a struct that uses the GCD's DispatchSemaphore to create a semaphore on the function and wait until it finish its job.
[!TIP] Is recommend to use a
defer { semaphore.continue() }right after theSemaphorecreation andwait()call.
let semaphore = Semaphore()
semaphore.wait()
defer { semaphore.continue() }
/// Your task here
You can even set a custom timeout, default is .distantFuture:
semaphore.wait(DispatchTime(uptimeNanoseconds: 1_000_000_000))
It's more useful if used inside an asynchronous task:
let concurrentOperation = ConcurrentOperation {
/// Your task here
semaphore.continue()
}
concurrentOperation.addToQueue(queue)
semaphore.wait()
Queuer offers a set of chainable helpers to create queues and operations in a more compact way. Every helper returns its instance, so the calls can be chained:
Queuer(name: "MyQueue")
.maxConcurrentOperationCount(1)
.qualityOfService(.background)
.concurrent { _ in
/// Your task here
}
.concurrent(retries: 2) { operation in
/// Your retryable task here
operation.success = false
}
.completion {
/// Executed once the last operation currently in the queue is finished
}
.chained(operation1, operation2)
.chained(
{ _ in
/// First chained task here
},
{ _ in
/// Second chained task here
}
)
.group(concurrentOperation1, concurrentOperation2)
.completion {
/// Executed at the end
}
ConcurrentOperations can be configured with the same style:
let operation = ConcurrentOperation()
.name("MyOperation")
.queuePriority(.high)
.qualityOfService(.utility)
.manualFinish()
.manualRetry()
.maximumRetries(5)
.executionBlock { operation in
/// Your task here
operation.finish()
}
.onPause { operation in
/// Executed when the operation is paused
}
.onResume { operation in
/// Executed when the operation is resumed
}
.onCancel { operation in
/// Executed when the operation is canceled
}
A barrier waits for all the operations currently in the queue and blocks every operation added after it, until the barrier is finished (requires macOS 10.15, iOS 13, tvOS 13 or watchOS 6):
queue
.concurrent { _ in
/// Your task here
}
.barrier {
/// Executed once every operation added before the barrier is finished
}
.concurrent { _ in
/// Executed after the barrier is finished
}
You can also make the queue wait, either without blocking a thread (requires macOS 13, iOS 16, tvOS 16 or watchOS 9) or by blocking one of its threads:
queue
.asyncWait(.seconds(1))
.syncWait(1)
.completion {
/// Executed after both waits are finished
}
AsyncConcurrentOperations have the same chainable helpers (requires macOS 10.15, iOS 13, tvOS 13 or watchOS 6):
queue
.asyncConcurrent { _ in
/// Your asynchronous task here, it can throw
try await yourAsyncTask()
}
.asyncConcurrent(retries: 2) { operation in
/// Your retryable asynchronous task here
operation.success = false
}
.asyncCompletion {
/// Your asynchronous completion here
await yourAsyncCompletion()
}
And they can be configured with the same fluent style:
let operation = AsyncConcurrentOperation()
.name("MyAsyncOperation")
.queuePriority(.high)
.maximumRetries(5)
.retryDelay(1)
.executionBlock { operation in
/// Your asynchronous task here, it can throw
try await yourAsyncTask()
}
To see what has changed in recent versions of Queuer, see the CHANGELOG.md file.
See CONTRIBUTING.md file.
Queuer is available under the MIT license. See the LICENSE file for more info.
343 followers · starred Aug 2017
25 followers · starred Aug 2017
8 followers · starred May 2019
95 followers · starred Feb 2021
Queuer is a queue manager, built on top of OperationQueue and Dispatch (aka GCD).
See the codeQueuer is a queue manager built on top of OperationQueue and Dispatch (aka GCD). It allows you to create asynchronous and synchronous tasks easily, all managed by a queue, with just a few lines.
| Swift | Queuer | iOS | macOS | macCatalyst | tvOS | watchOS | visionOS | Linux | Android | Windows |
|---|---|---|---|---|---|---|---|---|---|---|
| 3.1...3.2 | 1.0.0...1.1.0 | 8.0+ | 10.10+ | 9.0+ | 2.0+ | ✅ | ||||
| 4.0 | 1.3.0 | 8.0+ | 10.10+ | 9.0+ | 2.0+ | ✅ | ||||
| 4.1 | 1.3.1...1.3.2 | 8.0+ | 10.10+ | 9.0+ | 2.0+ | ✅ | ||||
| 4.2 | 2.0.0...2.0.1 | 8.0+ | 10.10+ | 9.0+ | 3.0+ | ✅ | ||||
| 5.0...5.10 | 2.1.0...2.2.0 | 8.0+ | 10.10+ | 9.0+ | 3.0+ | ✅ | ||||
| 5.9...5.10 | 3.0.0...3.0.1 | 12.0+ | 10.13+ | 13.0+ | 12.0+ | 4.0+ | 1.0+ | ✅ | ||
| 5.9...6.3 | 4.0.0 | 12.0+ | 10.13+ | 13.0+ | 12.0+ | 4.0+ | 1.0+ | ✅ | ✅ | ✅ |
[!NOTE] Some APIs require a newer OS than the minimum deployment target:
AsyncConcurrentOperation,addChainedAsyncOperations(_:completionHandler:),addAsyncCompletionHandler(_:),addBarrier(_:), andbarrier(_:)require macOS 10.15, iOS 13, tvOS 13, or watchOS 6.asyncWait(_:tolerance:clock:)requires macOS 13, iOS 16, tvOS 16, or watchOS 9.On Linux, Android, and Windows every API is always available.
See Requirements section to check Swift, Queuer, and OS versions.
In your Package.swift Swift Package Manager manifest, add the following dependency to your dependencies argument:
.package(url: "https://github.com/FabrizioBrancati/Queuer.git", from: "4.0.0"),
Add the dependency to any targets you've declared in your manifest:
.target(
name: "MyTarget",
dependencies: [
.product(name: "Queuer", package: "Queuer"),
]
),
Queuer offers a shared instance that you can use to add operations to a centralized queue:
Queuer.shared.addOperation(operation)
You can also create a custom queue:
let queue = Queuer(name: "MyCustomQueue")
You can even create a queue by defining the maxConcurrentOperationCount and the qualityOfService properties:
let queue = Queuer(name: "MyCustomQueue", maxConcurrentOperationCount: Int.max, qualityOfService: .default)
You have three methods to add an Operation block.
Directly on the queue(or Queuer.shared):
queue.addOperation {
/// Your task here
}
Creating a ConcurrentOperation with a block:
let concurrentOperation = ConcurrentOperation { _ in
/// Your task here
}
queue.addOperation(concurrentOperation)
[!NOTE] We will see how
ConcurrentOperationworks later.
Chained Operations are Operations that add a dependency each other.
They follow the given array order, for example: [A, B, C] = A -> B -> C -> completionBlock.
let concurrentOperationA = ConcurrentOperation { _ in
/// Your task A here
}
let concurrentOperationB = ConcurrentOperation { _ in
/// Your task B here
}
queue.addChainedOperations([concurrentOperationA, concurrentOperationB]) {
/// Your completion task here
}
You can also add a completionHandler after the queue creation with:
queue.addCompletionHandler {
/// Your completion task here
}
Group Operations are Operations that handles a group of Operations with a completion handler.
Allows the execution of a block of Operations with a completion handler that will be called once all the operations are finished, for example: [A -> [[B & C & D] -> completionHandler] -> E] -> completionHandler.
It should usually be used with a Chained Opetation.
let groupOperationA = GroupOperation(
[
ConcurrentOperation { _ in
/// Your task A here
},
ConcurrentOperation { _ in
/// Your task B here
}
]
)
let concurrentOperationC = ConcurrentOperation { _ in
/// Your task C here
}
queue.addChainedOperations([groupOperationA, concurrentOperationC]) {
/// Your completion task here
}
In this case the output will be the following one: [[A & B -> completionHandler] -> C] -> completionHandler.
There are a few method to handle the queue states.
Cancel all Operations in a queue:
queue.cancelAll()
Pause a queue:
queue.pause()
[!WARNING] By calling
pause()you will not be sure that everyOperationwill be paused. If theOperationis already started it will not be on pause until it's a customOperationthat overridespause()function.
Resume a queue:
queue.resume()
[!WARNING] To have a complete
pauseandresumestates you must create a customOperationthat overridespause()andresume()function.
Wait until all Operations are finished:
queue.waitUntilAllOperationsAreFinished()
[!IMPORTANT] This function means that the queue will blocks the current thread until all
Operations are finished.
You can control the ConcurrentOperation states by calling pause(), resume(), cancel().
[!NOTE] If you use a
Queuerobject with allConcurrentOperationobjects inside to manage your queue, you should avoid calling them directly on theConcurrentOperationobject, but you should call them on theQueuerone. TheQueuerobject will handle theConcurrentOperationstates automatically.
Pause a ConcurrentOperation:
concurrentOperation.pause()
Resume a ConcurrentOperation:
concurrentOperation.resume()
Cancel a ConcurrentOperation:
concurrentOperation.cancel()
For convenience, you can set closures to the ConcurrentOperation object to handle the states with onPause, onResume, and onCancel properties. They will be called when the ConcurrentOperation is paused, resumed, or canceled.
Setting the maxConcurrentOperationCount property of a queue to 1 will make you sure that only one task at a time will be executed.
If you want to create your custom Operation, the class ConcurrentOperation is a class ready to be subclassed.
It allows synchronous and asynchronous tasks, has a pause and resume states, can be easily added to a queue and can be created with a block.
You can create your custom ConcurrentOperation by subclassing it.
You must override execute() function and call the finish(success:) function, when the task has finished its job to notify the queue, regardless if it was successful or not.
An Operation is passed to every closure, with it you can set and handle the retry feature.
By default the retry feature is disabled, to enable it simply set the success property to false. With success to false the Operation will retry until reaches maximumRetries property value. To let the Operation know when everything is ok, you must set success to true.
With currentAttempt you can know at which attempt the Operation is.
let concurrentOperation = ConcurrentOperation { operation in
/// Your task here
if /* Successful */ {
operation.success = true
} else {
operation.success = false
}
}
You can throttle the automatic retries with the retryDelay property.
Every retry waits the given time before executing, the first attempt is never delayed.
Default is 0, retries happen immediately.
let concurrentOperation = ConcurrentOperation { operation in
/// Your task here
operation.success = false
}
concurrentOperation.retryDelay = 1
The same property is available on AsyncConcurrentOperation, where canceling the Operation also interrupts the delay right away:
let concurrentOperation = AsyncConcurrentOperation { operation in
/// Your asynchronous task here
operation.success = false
}
concurrentOperation.retryDelay = 1
You can also set it with the chainable syntactic sugar helper:
let concurrentOperation = ConcurrentOperation()
.retryDelay(1)
.executionBlock { operation in
/// Your task here
operation.success = false
}
You can manually retry an Operation when you think that the execution will be successful.
An Operation is passed to every closure, with it you can set and handle the retry feature.
By default the manual retry feature is disabled, to enable it simply set the manualRetry property to true, you must do this outside of the execution closure. You must also set success to true or false to let the Operation know when is everything ok, like the automatic retry feature.
To let the Operation retry your execution closure, you have to call the retry() function. Be sure to call it at least maximumRetries times, it is not a problem if you call retry() more times than is needed, your execution closure will not be executed more times than the maximumRetries value.
let concurrentOperation = ConcurrentOperation { operation in
/// Your task here
if /* Successful */ {
operation.success = true
} else {
operation.success = false
}
}
concurrentOperation.manualRetry = true
/// Later on your code
concurrentOperation.retry()
[!CAUTION] If the
retry()function is not called, you may block the entire queue.
By default the Operation will finish its job when the execution closure is completed.
This means, if you have an asynchronous task insiede the closure, the Operation will finish before the task is completed.
If you want to finish it manually, you can set the manualFinish property to true and call the finish(success:) function when the task is completed.
Call the finish(success:) function with success parameter to true or false to let the Operation know if the task was successful or not. If you don't explicitly set the success parameter, it will be set to true.
let concurrentOperation = ConcurrentOperation { operation in
/// Your asynchonous task here
}
concurrentOperation.manualFinish = true
/// Later on your code in your asynchronous task
concurrentOperation.finish(success: true)
[!CAUTION] If you don't call the
finish(success:)function, your queue will be blocked and it will never ends.
If you want to use async/await tasks, you need to set manualFinish to true in order to be fully in control of the Operation lifecycle.
[!NOTE] Read more about manually finish an
Operationhere.
let concurrentOperation = ConcurrentOperation { operation in
Task {
/// Your asynchonous task here
operation.finish(success: true) // or false
}
/// Your asynchonous task here
}
concurrentOperation.manualFinish = true
[!CAUTION] If you don't set
manualFinishtotrue, yourOperationwill finish before the async task is completed.
AsyncConcurrentOperation is an Operation built for async/await, available on macOS 10.15, iOS 13, tvOS 13, and watchOS 6 or later.
Its execution block is an async throwing function: a thrown error marks the attempt as failed and enables the retry feature, and canceling the Operation also cancels the Task running its block.
let concurrentOperation = AsyncConcurrentOperation { operation in
/// Your asynchronous task here, it can throw
try await yourAsyncTask()
}
concurrentOperation.addToQueue(queue)
You can chain them with an async completion handler:
queue.addChainedAsyncOperations([concurrentOperation1, concurrentOperation2]) {
/// Your asynchronous completion here
await yourAsyncCompletion()
}
A Scheduler is a struct that uses the GDC's DispatchSourceTimer to create a timer that can execute functions with a specified interval and quality of service.
let schedule = Scheduler(deadline: .now(), repeating: .seconds(1)) {
/// Your task here
}
You can even create a Scheduler without the handler and set it later:
var schedule = Scheduler(deadline: .now(), repeating: .seconds(1))
schedule.setHandler {
/// Your task here
}
With timer property you can access to all DispatchSourceTimer properties and functions, like cancel():
schedule.timer.cancel()
A Semaphore is a struct that uses the GCD's DispatchSemaphore to create a semaphore on the function and wait until it finish its job.
[!TIP] Is recommend to use a
defer { semaphore.continue() }right after theSemaphorecreation andwait()call.
let semaphore = Semaphore()
semaphore.wait()
defer { semaphore.continue() }
/// Your task here
You can even set a custom timeout, default is .distantFuture:
semaphore.wait(DispatchTime(uptimeNanoseconds: 1_000_000_000))
It's more useful if used inside an asynchronous task:
let concurrentOperation = ConcurrentOperation {
/// Your task here
semaphore.continue()
}
concurrentOperation.addToQueue(queue)
semaphore.wait()
Queuer offers a set of chainable helpers to create queues and operations in a more compact way. Every helper returns its instance, so the calls can be chained:
Queuer(name: "MyQueue")
.maxConcurrentOperationCount(1)
.qualityOfService(.background)
.concurrent { _ in
/// Your task here
}
.concurrent(retries: 2) { operation in
/// Your retryable task here
operation.success = false
}
.completion {
/// Executed once the last operation currently in the queue is finished
}
.chained(operation1, operation2)
.chained(
{ _ in
/// First chained task here
},
{ _ in
/// Second chained task here
}
)
.group(concurrentOperation1, concurrentOperation2)
.completion {
/// Executed at the end
}
ConcurrentOperations can be configured with the same style:
let operation = ConcurrentOperation()
.name("MyOperation")
.queuePriority(.high)
.qualityOfService(.utility)
.manualFinish()
.manualRetry()
.maximumRetries(5)
.executionBlock { operation in
/// Your task here
operation.finish()
}
.onPause { operation in
/// Executed when the operation is paused
}
.onResume { operation in
/// Executed when the operation is resumed
}
.onCancel { operation in
/// Executed when the operation is canceled
}
A barrier waits for all the operations currently in the queue and blocks every operation added after it, until the barrier is finished (requires macOS 10.15, iOS 13, tvOS 13 or watchOS 6):
queue
.concurrent { _ in
/// Your task here
}
.barrier {
/// Executed once every operation added before the barrier is finished
}
.concurrent { _ in
/// Executed after the barrier is finished
}
You can also make the queue wait, either without blocking a thread (requires macOS 13, iOS 16, tvOS 16 or watchOS 9) or by blocking one of its threads:
queue
.asyncWait(.seconds(1))
.syncWait(1)
.completion {
/// Executed after both waits are finished
}
AsyncConcurrentOperations have the same chainable helpers (requires macOS 10.15, iOS 13, tvOS 13 or watchOS 6):
queue
.asyncConcurrent { _ in
/// Your asynchronous task here, it can throw
try await yourAsyncTask()
}
.asyncConcurrent(retries: 2) { operation in
/// Your retryable asynchronous task here
operation.success = false
}
.asyncCompletion {
/// Your asynchronous completion here
await yourAsyncCompletion()
}
And they can be configured with the same fluent style:
let operation = AsyncConcurrentOperation()
.name("MyAsyncOperation")
.queuePriority(.high)
.maximumRetries(5)
.retryDelay(1)
.executionBlock { operation in
/// Your asynchronous task here, it can throw
try await yourAsyncTask()
}
To see what has changed in recent versions of Queuer, see the CHANGELOG.md file.
See CONTRIBUTING.md file.
Queuer is available under the MIT license. See the LICENSE file for more info.
343 followers · starred Aug 2017
25 followers · starred Aug 2017
8 followers · starred May 2019
95 followers · starred Feb 2021