A Task Queue Class developed in Swift (by Marin Todorov)

Overview

TaskQueue

Platform Cocoapods Compatible Carthage Compatible GitHub License

Table of Contents

Intro

title

TaskQueue is a Swift library which allows you to schedule tasks once and then let the queue execute them in a synchronous manner. The great thing about TaskQueue is that you get to decide on which GCD queue each of your tasks should execute beforehand and leave TaskQueue to do switching of the queues as it goes.

Even if your tasks are asynchronious like fetching location, downloading files, etc. TaskQueue will wait until they are finished before going on with the next task.

Last but not least your tasks have full flow control over the queue, depending on the outcome of the work you are doing in your tasks you can skip the next task, abort the queue, or jump ahead to the queue completion. You can further pause, resume, and stop the queue.

Installation

CocoaPods

CocoaPods is a dependency manager for Cocoa projects.

If you don't already have the Cocoapods gem installed, run the following command:

$ gem install cocoapods

To integrate TaskQueue into your Xcode project using CocoaPods, specify it in your Podfile:

pod 'TaskQueue'

Then, run the following command:

$ pod install

If you find that you're not having the most recent version installed when you run pod install then try running:

$ pod cache clean
$ pod repo update TaskQueue
$ pod install

Also you'll need to make sure that you've not got the version of TaskQueue locked to an old version in your Podfile.lock file.

Carthage

Carthage is a decentralized dependency manager that automates the process of adding frameworks to your Cocoa application.

You can install Carthage with Homebrew using the following command:

$ brew update
$ brew install carthage

To integrate TaskQueue into your Xcode project using Carthage, specify it in your Cartfile:

github "icanzilb/TaskQueue"

Simple Example

Synchronous tasks

Here's the simplest way to use TaskQueue in Swift:

let queue = TaskQueue()

queue.tasks +=~ {
	... time consuming task on a background queue...
}

queue.tasks +=! {
	... update UI on main queue ...
}

queue.run()

TaskQueue will execute the tasks one after the other waiting for each task to finish and the will execute the next one. By using the operators +=~ and +=! you can easily set whether the task should execute in background or on the main queue.

Asynchronous tasks

More interesting of course is when you have to do some asynchronous work in the background of your tasks. Then you can fetch the next parameter in your task and call it whenever your async work is done:

let queue = TaskQueue()

queue.tasks +=~ { result, next in

    var url = URL(string: "http://jsonmodel.com")

    URLSession.shared.dataTask(with: url,
        completionHandler: { _, _, _ in
            // process the response
            next(nil)
        })
}

queue.tasks +=! {
    print("execute next task after network call is finished")
}

queue.run {
    print("finished")
}

There are a few things to highlight in the example above:

  1. The first task closure gets two parameters: result is the result from the previous task (nil in the case of the first task) and next. next is a closure you need to call whenver your async task has finished executing.

  2. Task nr.2 doesn't get started until you call next() in your previous task.

  3. The run function can also take a closure as a parameter - if you pass one it will always get executed after all other tasks has finished.

Serial and Concurrent Tasks

By default TaskQueue executes its tasks one after another or, in other words, the queue has up to one active task at a time.

You can, however, allow a given number of tasks to execute at the same time (e.g. if you need to download a number of image files from web). To do this just increase the number of active tasks and the queue will automatically start executing tasks concurrently. For example:

queue.maximumNumberOfActiveTasks = 10

This will make the queue execute up to 10 tasks at the same time.

Note: As soon as you allow for more than one task at a time certain restrictions apply: you cannot invoke retry(), and you cannot pass a result from one task to another.

GCD Queue Control

Do you want to run couple of heavy duty tasks in the background and then switch to the main queue to update your app UI? Easy. Study the example below, which showcases GCD queue control with TaskQueue:

let queue = TaskQueue()

//
// "+=" adds a task to be executed on the current queue
//
queue.tasks += {
    // update the UI
}

//
// "+=~" adds a task to be executed in the background, e.g. low prio queue
// "~" stands for so~so priority
//
queue.tasks +=~ {
    // do heavy work
}

//
// "+=!" adds a task to be executed on the main queue
// "!" stands for High! priority
//
queue.tasks +=! {
    // update the UI again
}

// to start the queue on the current GCD queue
queue.run()

Extensive example

let queue = TaskQueue()

//
// Simple sync task, just prints to console
//
queue.tasks += {
    print("====== tasks ======")
    print("task #1: run")
}

//
// A task, which can be asynchronious because it gets
// result and next params and can call next() when ready
// with async work to tell the queue to continue running
//
queue.tasks += { result, next in
    print("task #2: begin")

    delay(seconds: 2) {
        print("task #2: end")
        next(nil)
    }

}

//
// A task which retries the same task over and over again
// until it succeeds (i.e. util when you make network calls)
// NB! Important to capture **queue** as weak to prevent
// memory leaks!
//
var cnt = 1
queue.tasks += { [weak queue] result, next in
    print("task #3: try #\(cnt)")
    cnt += 1

    if cnt > 3 {
        next(nil)
    } else {
        queue!.retry(delay: 1)
    }
}

//
// This task skips the next task in queue
// (no capture cycle here)
//
queue.tasks += {
    print("task #4: run")
    print("task #4: will skip next task")

    queue.skip()
}

queue.tasks += {
    print("task #5: run")
}

//
// This task removes all remaining tasks in the queue
// i.e. util when an operation fails and the rest of the queueud
// tasks don't make sense anymore
// NB: This does not remove the completions added
//
queue.tasks += {
    print("task #6: run")

    print("task #6: will append one more completion")
    queue.run { _ in
        print("completion: appended completion run")
    }

    print("task #6: will skip all remaining tasks")
    queue.removeAll()
}

queue.tasks += {
    print("task #7: run")
}

//
// This either runs or resumes the queue
// If queue is running doesn't do anything
//
queue.run()

//
// This either runs or resumes the queue
// and adds the given closure to the lists of completions.
// You can add as many completions as you want (also half way)
// trough executing the queue.
//
queue.run { result in
    print("====== completions ======")
    print("initial completion: run")
}

Run the included demo app to see some of these examples above in action.

Credit

Author: Marin Todorov

License

TaskQueue is available under the MIT license. See the LICENSE file for more info.

Comments
  • Is there a way to do nested tasks?

    Is there a way to do nested tasks?

    I have a method with a task queue that calls another method with a task queue

    I'm guessing this is because they are on the same queue?

    I have 1 2 a b 3

    It executes 1 2 a 3 b

    opened by alexbosworth 3
  • When do I use queue.run()?

    When do I use queue.run()?

    I'm not sure if this is an actual issue but I was wondering. When is the right time to call .run() on a queue? In your Readme, you always called it after you've added all the tasks you wanted to add. Is it possible to call .run() say in a class initialiser and keep adding tasks to it AFTER. So that it just keeps processing tasks as they come (one after the other)?

    opened by timigod 2
  • Do the tasks continue to run if the application enters background?

    Do the tasks continue to run if the application enters background?

    And if they do, for how long?

    It seems to me that the queue is subject to the limited time given by iOS to tasks created by application.beginBackgroundTaskWithExpirationHandler, since there is no additional setup required to use it.

    opened by ahmedk92 1
  • run is not working.

    run is not working.

    Hi,

    I don't know why but I am using Reachability to pause and resume the queue, so I do paused = true when there is no internet connexion, then when internet is back, I do paused = false and run.

    They are task in queue, but for some reasons they are never called, and the finish of the run block is never called. Any idea?

    Thanks

    opened by cyril94440 1
  • Regarding Synchronous operation

    Regarding Synchronous operation

    I used the api mentioned in the documentation but the request and response are not in the order they are random its like not waiting for the previous response before executing next .

    opened by gthrb22 1
  • Compile error when using COCOAPODS

    Compile error when using COCOAPODS

    Hi. I had compile error because TaskQueue class has no public init method.

    I commented at https://github.com/icanzilb/TaskQueue/commit/0ed1061a1df2559914499df73629fca8099d2881.

    Please check.

    opened by sapzildj 1
  • new operator to add a task on the main or bg queue

    new operator to add a task on the main or bg queue

    It'd be great if there is a way to say smth like:

    queue += { code executed on same thread as queue was created on }

    queue +=! { code to be executed on main thread }

    queue +=? { code to be executed on bg thread }

    opened by icanzilb 1
  • Restructure to make things more conventional and easier to work with

    Restructure to make things more conventional and easier to work with

    • Use Sources and Tests directories
    • Add a tests target for TaskQueue project
    • Rename demo app to iOSExample and include it in the workspace
    • Other general cleanup
    opened by hamchapman 0
  • Make it Carthage compatible for macOS and tvOS (and still iOS)

    Make it Carthage compatible for macOS and tvOS (and still iOS)

    I updated the project settings to make it build on macOS, tvOS, and iOS through the existing scheme. That means that it now works with users who are using Carthage for those platforms too.

    I also added a pauseAndResetCurrentTask function that I've been using but I can remove that if you don't want that as part of the library.

    Other things:

    • Updated SWIFT_VERSION to 4.0
    • Removed code signing as it's unnecessary
    • Updated Podspec to specify deployment_targets for iOS, macOS, and tvOS
    • Set APPLICATION_EXTENSION_API_ONLY = YES
    opened by hamchapman 0
  • can I add on the task queue from another task that's already in the queue?

    can I add on the task queue from another task that's already in the queue?

    Basically can I nest adding to the task queue or is that going to mess things up?

    queue.tasks +=~ { result, next in queue.tasks +=~ { result , next in print("foo") next(nil) } next(nil) }

    opened by artangco 0
  • Is it possible to have an active queue all the time

    Is it possible to have an active queue all the time

    Is it possible to have an active queue available that I feed tasks I need to run in order. In my case I don't have the tasks ahead of time before I call .run. Is that possible?

    opened by artangco 0
  • Status callbacks

    Status callbacks

    Hi there! Thanks for this excellent library. I've been a longtime user, and am thrilled to contribute back an enhancement that would've saved me a ton of duplicated logic in my tasks over the years.

    I've long been including UI updating logic in my tasks, and have wanted a view on the queue's state so I can more directly inform the user about what's happening in the program.

    Let me know if there's more I can do to help this code land 😉

    opened by zshannon 0
  • Queue not running

    Queue not running

    The following is the setup and the issue I am running into:

    • Singleton object contains a TaskQueue object.
    • At some point, when deemed correct(attempt at unauthorized use), the TaskQueue object is cleared using remove all
    • Afterwards, when trying to add tasks to TaskQueue object, tasks in the queue are not being run.
    let queue = TaskQueue()
    
    func init() {
         downloadStuff()
    }
    
    func downloadStuff() {
       queue.tasks += {[weak queue] result, next in
    
           myTask(completion: { success in
              if success {
                   next(nil)
              }
              else {
                 print("error out")
              }
           }
    
       }
    
       queue.run() {
           print("completed")
       }
    }
    
    func unauthorizedUse() {
         queue.removeAll()
    }
    
    func authorizedUse() {
         downloadStuff()
    }
    
    opened by sameerKLT 1
Releases(1.0.3)
Owner
Marin Todorov
Experienced on  platforms. Author of "Modern Concurrency in Swift", "Combine: Asynchronous Programming with Swift", and others.
Marin Todorov
Kommander is a Swift library to manage the task execution in different threads.

A lightweight, pure-Swift library for manage the task execution in different threads. Through the definition a simple but powerful concept, Kommand.

Intelygenz 173 Apr 11, 2022
Schedule timing task in Swift using a fluent API. (A friendly alternative to Timer)

Schedule(简体中文) Schedule is a timing tasks scheduler written in Swift. It allows you run timing tasks with elegant and intuitive syntax. Features Elega

Luo Xiu 1.8k Jan 7, 2023
Queues, timers, and task groups in Swift

Dispatcher eases the pain of using Grand Central Dispatch by introducing 4 new Swift classes. Dispatcher Queue Group Timer Requirements Swift 2.0+ Ins

Alec Larson 109 Jan 29, 2022
⚡️ Fast async task based Swift framework with focus on type safety, concurrency and multi threading

Our apps constantly do work. The faster you react to user input and produce an output, the more likely is that the user will continue to use your appl

Said Sikira 814 Oct 30, 2022
Several synchronization primitives and task synchronization mechanisms introduced to aid in modern swift concurrency.

AsyncObjects Several synchronization primitives and task synchronization mechanisms introduced to aid in modern swift concurrency. Overview While Swif

SwiftyLab 20 Jan 3, 2023
AsyncTaskKit - contains some additions to async/await Task

AsyncTaskKit This repo contains some additions to async/await Task. In general i

Michał Zaborowski 0 Jan 2, 2022
A complete set of primitives for concurrency and reactive programming on Swift

A complete set of primitives for concurrency and reactive programming on Swift 1.4.0 is the latest and greatest, but only for Swift 4.2 and 5.0 use 1.

AsyncNinja 156 Aug 31, 2022
Futures is a cross-platform framework for simplifying asynchronous programming, written in Swift.

Futures Futures is a cross-platform framework for simplifying asynchronous programming, written in Swift. It's lightweight, fast, and easy to understa

David Ask 60 Aug 11, 2022
GroupWork is an easy to use Swift framework that helps you orchestrate your concurrent, asynchronous functions in a clean and organized way

GroupWork is an easy to use Swift framework that helps you orchestrate your concurrent, asynchronous functions in a clean and organized way. This help

Quan Vo 42 Oct 5, 2022
Hydra ⚡️ Lightweight full-featured Promises, Async & Await Library in Swift

Lightweight full-featured Promises, Async & Await Library in Swift What's this? Hydra is full-featured lightweight library which allows you to write b

Daniele Margutti 2k Dec 24, 2022
SwiftCoroutine - Swift coroutines for iOS, macOS and Linux.

Many languages, such as Kotlin, Go, JavaScript, Python, Rust, C#, C++ and others, already have coroutines support that makes the async/await pattern i

Alex Belozierov 808 Dec 1, 2022
Venice - Coroutines, structured concurrency and CSP for Swift on macOS and Linux.

Venice provides structured concurrency and CSP for Swift. Features Coroutines Coroutine cancelation Coroutine groups Channels Receive-only chan

Zewo 1.5k Dec 22, 2022
Syntactic sugar in Swift for asynchronous dispatches in Grand Central Dispatch

Async Now more than syntactic sugar for asynchronous dispatches in Grand Central Dispatch (GCD) in Swift Async sugar looks like this: Async.userInitia

Tobias Due Munk 4.6k Dec 27, 2022
AwaitKit is a powerful Swift library which provides a powerful way to write asynchronous code in a sequential manner.

AwaitKit is a powerful Swift library inspired by the Async/Await specification in ES8 (ECMAScript 2017) which provides a powerful way to write asynchronous code in a sequential manner.

Yannick Loriot 752 Dec 5, 2022
Elegant ⏱ interface for Swift apps

Each Elegant ⏱ interface for Swift apps Each is a NSTimer bridge library written in Swift. Features Requirements Installation Usage Leaks License Feat

Luca D'Alberti 764 Dec 29, 2022
GCDTimer - Well tested Grand Central Dispatch (GCD) Timer in Swift

GCDTimer Well tested Grand Central Dispatch (GCD) Timer in Swift. Checkout the test file. Usage Long running timer import GCDTimer

Hemant Sapkota 183 Sep 9, 2022
Grand Central Dispatch simplified with swift.

GCDKit GCDKit is Grand Central Dispatch simplified with Swift. for Swift 1.2: Use version 1.0.1 for Swift 2.1 / 2.2: Use the master branch Introductio

John Estropia 317 Dec 6, 2022
A wrapper of Grand Central Dispatch written in Swift

GCD A wrapper of Grand Central Dispatch written in Swift. Examples gcd // submit your code for asynchronous execution on a global queue with high prio

Le Van Nghia 75 May 19, 2022
A Swift microframework for very easy atomic values.

Atomic Atomic is a fast, safe class for making values thread-safe in Swift. It is backed by pthread_mutex_lock which is the fastest, most-efficient lo

Adlai Holler 36 Sep 26, 2022