Async and concurrent versions of Swift’s forEach, map, flatMap, and compactMap APIs.

Overview

CollectionConcurrencyKit

Welcome to CollectionConcurrencyKit, a lightweight Swift package that adds asynchronous and concurrent versions of the standard map, flatMap, compactMap, and forEach APIs to all Swift collections that conform to the Sequence protocol. That includes built-in types, like Array, Set and, Dictionary, as well as any custom collections that conform to that protocol.

CollectionConcurrencyKit can be used to implement high-performance data processing and algorithms in a way that fully utilizes Swift’s built-in concurrency system. It’s heavily unit tested, fully documented, and used in production to generate swiftbysundell.com.

Asynchronous iterations

The async variants of CollectionConcurrencyKit’s APIs enable you to call async-marked functions within your various mapping and forEach iterations, while still maintaining a completely predictable, sequential execution order.

For example, here’s how we could use asyncMap to download a series of HTML strings from a collection of URLs:

let urls = [
    URL(string: "https://apple.com")!,
    URL(string: "https://swift.org")!,
    URL(string: "https://swiftbysundell.com")!
]

let htmlStrings = try await urls.asyncMap { url -> String in
    let (data, _) = try await URLSession.shared.data(from: url)
    return String(decoding: data, as: UTF8.self)
}

And here’s how we could use asyncCompactMap to ignore any download that failed, by returning an optional value, rather than throwing an error:

let htmlStrings = await urls.asyncCompactMap { url -> String? in
    do {
        let (data, _) = try await URLSession.shared.data(from: url)
        return String(decoding: data, as: UTF8.self)
    } catch {
        return nil
    }
}

Each of CollectionConcurrencyKit’s APIs come in both throwing and non-throwing variants, so since the above call to asyncCompactMap doesn’t throw, we don’t need to use try when calling it.

Concurrency

CollectionConcurrencyKit also includes concurrent variants of forEach, map, flatMap, and compactMap, which perform their iterations in parallel, while still maintaining a predictable order when producing their results.

For example, since our above HTML downloading code consists of completely separate operations, we could instead use concurrentMap to perform each of those operations in parallel for a significant speed boost:

let htmlStrings = try await urls.concurrentMap { url -> String in
    let (data, _) = try await URLSession.shared.data(from: url)
    return String(decoding: data, as: UTF8.self)
}

And if we instead wanted to parallelize our asyncCompactMap-based variant of the above code, then we could do so by using concurrentCompactMap:

let htmlStrings = await urls.concurrentCompactMap { url -> String? in
    do {
        let (data, _) = try await URLSession.shared.data(from: url)
        return String(decoding: data, as: UTF8.self)
    } catch {
        return nil
    }
}

Regardless of whether we choose the async or concurrent versions of CollectionConcurrencyKit’s APIs, the order of the returned results is always guaranteed to be the exact same as when calling the standard library’s non-async versions of those APIs. So, the order of the htmlStrings array will be identical across all of the above four code samples (ignoring any nil values produced by the compactMap-variants).

System requirements

CollectionConcurrencyKit works on all operating system versions that support Swift’s concurrency system, which currently includes iOS 15, macOS 12, watchOS 8, and tvOS 15, as well as Linux (when using a Swift toolchain of version 5.5 or higher).

Apple is currently working on adding backward compatibility to Swift’s concurrency system, and once that feature has become fully available (it’s currently in beta, as part of Xcode 13.2), then CollectionConcurrencyKit will be updated to support earlier OS versions as well.

Installation

CollectionConcurrencyKit is distributed using the Swift Package Manager. To install it within another Swift package, add it as a dependency within your Package.swift manifest:

let package = Package(
    ...
    dependencies: [
        .package(url: "https://github.com/JohnSundell/CollectionConcurrencyKit.git", from: "0.1.0")
    ],
    ...
)

If you’d like to use CollectionConcurrencyKit within an iOS, macOS, watchOS or tvOS app, then use Xcode’s File > Add Packages... menu command to add it to your project.

Then import CollectionConcurrencyKit wherever you’d like to use it:

import CollectionConcurrencyKit

For more information on how to use the Swift Package Manager, check out this article, or its official documentation.

Support and contributions

CollectionConcurrencyKit has been made freely available to the entire Swift community under the very permissive MIT license, but please note that it doesn’t come with any official support channels, such as GitHub issues, or Twitter/email-based support. So, before you start using CollectionConcurrencyKit within one of your projects, it’s highly recommended that you spend some time familiarizing yourself with its implementation, in case you’ll run into any issues that you’ll need to debug.

If you’ve found a bug, documentation typo, or if you want to propose a performance improvement, then feel free to open a Pull Request (even if it just contains a unit test that reproduces a given issue). While all sorts of fixes and tweaks are more than welcome, CollectionConcurrencyKit is meant to be a very small, focused library, so it’s considered more or less feature-complete. So, if you’d like to add any significant new features to the library, then it’s recommended that you fork it, which will let you extend and customize it to fit your needs.

Hope you’ll enjoy using CollectionConcurrencyKit!

You might also like...
🎭 Swift async/await & Actor-powered effectful state-management framework.
🎭 Swift async/await & Actor-powered effectful state-management framework.

🎭 Actomaton 🧑‍🎤 Actor + 🤖 Automaton = 🎭 Actomaton Actomaton is Swift async/await & Actor-powered effectful state-management framework inspired by

Hydra: Lightweight full-featured Promises, Async-Await Library in Swift

Async Functions for ECMAScript The introduction of Promises and Generators in EC

Swift TableView pagination with async API request.
Swift TableView pagination with async API request.

SwiftTableViewPagination Swift TableView pagination with async API request. Output UML Create puml file. $ cd SwiftTableViewPagination/scripts/swiftum

AsyncLocationKit - Async/await CoreLocation With Swift

AsyncLocationKit Wrapper for Apple CoreLocation framework with new Concurency Mo

AsyncExtensions aims to mimic Swift Combine operators for async sequences.

AsyncExtensions AsyncExtensions provides a collection of operators, async sequences and async streams that mimics Combine behaviour. The purpose is to

A Swift lib for network with async/await

SmileNetwork A Swift network utility with async/await applied UseAge enum MockEndpoint { case weather(cityId: String) } extension MockEndpoint: S

straightforward networking and error handling with async-await and URLSession

AsyncAwaitNetworkingPlayground How To Run Just clone the project, open it and run. Some notes about AsyncAwaitNetworkingPlayground It's a straightforw

A demonstration for bridging between Combine and your new async functions

CombineAsyncually This is a DEMONSTRATION of how you can bridge the new async / await functionality in Swift 5.5 with Combine. There is NO WARRANTY. T

A simple network layer for use in small iOS projects with async/await support

SimpleNetwork Intro SimpleNetwork is simple network layer for use in small projects. Swift Package Manager Note: Instructions below are for using Swif

Comments
  • Implement maximum concurrency for concurrentForEach

    Implement maximum concurrency for concurrentForEach

    This PR provides the option to limit the number of concurrent tasks when running concurrentForEach. The original behavior is maintained - run as many items in parallel as possible.

    If you like the direction of this PR, I can implement for the other functions in a similar manner as well as include corresponding tests.

    opened by AndrewBarba 1
  • Tests: Don't assume that all concurrent tasks will run when throwing an error

    Tests: Don't assume that all concurrent tasks will run when throwing an error

    If the error gets thrown before some tasks have had a chance to run, then the collector won't actually collect all values, so we can't assume that it will. Instead, we now only verify that those throwing concurrent tasks actually end up throwing an error.

    opened by JohnSundell 0
  • Make unit tests Linux-compatible

    Make unit tests Linux-compatible

    Swift's test auto-discovery feature doesn't seem to work with asynchronous unit tests on Linux just yet. So, for now, we'll wrap all of our testing logic using a runAsyncTest utility method that can be executed synchronously.

    opened by JohnSundell 0
  • Use TaskGroups instead of array of tasks

    Use TaskGroups instead of array of tasks

    Rationale for this comes from this thread https://forums.swift.org/t/taskgroup-vs-an-array-of-tasks/53931 Apparently launching a lot of Tasks just like that is not very good, and we should use task groups instead.

    I did not test the performance implication of this change, though. Maybe it’s actually worse because of the additional sort needed. In theory it should be better though.

    opened by Frizlab 5
Releases(0.2.0)
  • 0.2.0(Dec 17, 2021)

    CollectionConcurrencyKit now supports the same backward compatibility as Swift's concurrency system itself, meaning that it can now be used on iOS 13+, macOS 10.15+, watchOS 6+, and tvOS 13+ (as long as Xcode 13.2 or later is used).

    Source code(tar.gz)
    Source code(zip)
  • 0.1.0(Nov 8, 2021)

Owner
John Sundell
I build apps, games and Swift developer tools! Passionate about open source & developer productivity. You can follow me on Twitter @JohnSundell.
John Sundell
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
Job Scheduler for IOS with Concurrent run, failure/retry, persistence, repeat, delay and more

SwiftQueue Schedule tasks with constraints made easy. SwiftQueue is a job scheduler for iOS inspired by popular android libraries like android-priorit

Lucas Nelaupe 367 Dec 24, 2022
A general purpose embedded hierarchical lock manager used to build highly concurrent applications of all types. Same type of locker used in many of the large and small DBMSs in existence today.

StickyLocking StickyLocking is a general purpose embedded lock manager which allows for locking any resource hierarchy. Installable Lock modes allow f

Sticky Tools 2 Jun 15, 2021
AsyncButton is the simple way to run concurrent code in your views.

SwiftUI AsyncButton ??️ AsyncButton is a Button capable of running concurrent code. Usage AsyncButton has the exact same API as Button, so you just ha

Lorenzo Fiamingo 13 Dec 14, 2022
React Native FlyBuy module. Supports Core, Pickup, Notify, and Presence Native SDK APIs.

The FlyBuy cloud service and mobile SDK enables developers to add FlyBuy functionality directly into their react native apps for a full white label im

BILDIT, LLC. 8 Sep 16, 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
A lightweight swift network layer with Combine, Async-Await, and a traditional completion block.

CombineNetwork A simple light-weight network library to make network requesting simpler. It supports newer techonology such as async/await as well as

Dushant Singh 4 Jan 3, 2022
Edit images and video with async / await in Swift, powered by Metal.

AsyncGraphics The core value type in AsyncGraphics is a Graphic. It's like an image, tho it can be used with various async methods. Documentation Swif

Anton Heestand 33 Dec 27, 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
⏳ Collection of Swift 5.5 async/await utility functions.

⏳ FunAsync Collection of Swift 5.5 async/await utility functions. Throw <-> Result conversion asyncThrowsToAsyncResult asyncResultToAsyncThrows More C

Yasuhiro Inami 23 Oct 14, 2022