{"id":20852614,"url":"https://github.com/eleev/concurrency-kit","last_synced_at":"2025-12-12T06:15:55.029Z","repository":{"id":56931842,"uuid":"174850531","full_name":"eleev/concurrency-kit","owner":"eleev","description":"🚄 Concurrency abstractions framework for Apple Platforms [Task, Atomic, Lock, Operation, etc.].","archived":false,"fork":false,"pushed_at":"2020-01-05T17:47:57.000Z","size":169,"stargazers_count":22,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-08T10:55:42.787Z","etag":null,"topics":["apple","async","atomics","concurrency","concurrent","dispatch-queues","framework","gcd","grand-central-dispatch","ios","ipados","locks","macos","mutex","spm","swift","swift-package-manager","task","watchos"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eleev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-03-10T16:34:22.000Z","updated_at":"2025-03-25T15:49:10.000Z","dependencies_parsed_at":"2022-08-21T00:00:33.910Z","dependency_job_id":null,"html_url":"https://github.com/eleev/concurrency-kit","commit_stats":null,"previous_names":["jvirus/concurrency-kit"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eleev%2Fconcurrency-kit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eleev%2Fconcurrency-kit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eleev%2Fconcurrency-kit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eleev%2Fconcurrency-kit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eleev","download_url":"https://codeload.github.com/eleev/concurrency-kit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253681886,"owners_count":21946823,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apple","async","atomics","concurrency","concurrent","dispatch-queues","framework","gcd","grand-central-dispatch","ios","ipados","locks","macos","mutex","spm","swift","swift-package-manager","task","watchos"],"created_at":"2024-11-18T03:18:13.060Z","updated_at":"2025-12-12T06:15:54.983Z","avatar_url":"https://github.com/eleev.png","language":"Swift","readme":"# concurrency-kit [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/sindresorhus/awesome)\n\n[![Build](https://github.com/jvirus/concurrency-kit/workflows/Build/badge.svg)]()\n[![Coverage](https://codecov.io/gh/jVirus/concurrency-kit/branch/master/graph/badge.svg)](https://codecov.io/gh/jVirus/concurrency-kit)\n[![Platforms](https://img.shields.io/badge/Platforms-iOS/iPadOS/WatchOS/macOS-yellow.svg)]()\n[![Language](https://img.shields.io/badge/Language-Swift_5.1-orange.svg)]()\n[![SPM](https://img.shields.io/badge/SPM-Supported-red.svg)]()\n[![License](https://img.shields.io/badge/License-MIT-blue.svg)]()\n\n**Last Update: 05/January/2020.**\n\n![](logo-concurrency_kit.png)\n\n### If you like the project, please give it a star ⭐ It will show the creator your appreciation and help others to discover the repo.\n\n# ✍️ About\n🚄 Concurrency abstractions framework for `iOS` development [`Task`, `Atomic`, `Lock`, etc.].\n\n# 🔥 Features\n- **Atomics** - synchronization primitive that is implemented in several forms: `Generic`, `Int` and `Bool`.\n  - `Fast`. Under the hood a mutex (`pthread_mutex_lock`) that is more efficient than `OSSpinLock` and faster than `NSLock`.\n  - `Throwable`. You can safely throw `Errors` and be able to delegate the handling.\n- **Locks** - contains a number of locks, such as:\n  - `UnfairLock` - A lock which causes a thread trying to acquire it to simply wait in a loop (\"spin\") while repeatedly checking if the lock is available.\n  - `ReadWriteLock` - An `RW` lock allows concurrent access for read-only operations, while write operations require exclusive access.\n  - `Mutex` - Allows only one thread to be active in a given region of code.\n- **DispatchQueue+Extensions** - extended `DispatchQueue`, where `asyncAfter` and `once` methods add convenience.\n- **Task** - A unit of work that performs a specific job and usually runs concurrently with other tasks.\n  - Tasks can be `grouped` - meaning that you are able to compose the tasks, similar to `Futures \u0026 Promises` and execute them serially.\n  - Tasks can be `sequenced` - meaning that you are able to compose different `groups` and execute them concurrently. No need to repeatedly use `DispatchGroup` (`enter`/`leave`). \n- **Stateful Operation** - is a custom `Operation` class that supports modern, `Swifty` state management through the usage of `Atomics` and `Enum` types.\n- **Thoroughly** tested.\n\n# 📚 Examples\n\n## Task\nIn order to create a `Task`, you need to simply use the `Task` struct and the `trailing closure` syntax:\n\n```swift\nlet uploadingTask = Task { controller in\n  uploader(photos) { result in \n    switch result {\n      case .success:\n        controller.finish()\n      case .failure(let error):          \n        controller.fail(with error)\n    }\n  }\n}\n        \nuploadingTask.perform { outcome in\n  handle(outcome)\n}\n```\n\nYou can group the tasks, so the concurrent operations will be performed sequentially, one after another. Then, you can chain a completion closure to handle the outcome:\n\n```swift\nlet filesToUpload = [file, photo, video, xml]\n\nlet group = Task.group(fileToUpload)\ngroup.perform { outcome in \n  handle(outcome)\n}\n```\n\nOr you can concurrently perform a collection of tasks. They will be executed asynchronously, in parallel (if possible) or concurrently, that is up to the `GCD`:\n\n```swift\nlet filesToUpload = [file, photo, video, xml]\n\nlet group = Task.sequence(filesToUpload)\ngroup.perform { outcome in \n  handle(outcome)\n}\n```\n\n## Stateful Operation\nOperation that has more 'Swifty' state management system, where state is an enum type with a number of possible cases. In order to demostrate the typical usage, let's define a new custom operation for network request:\n\n```swift\nclass NetworkStatefulOperation: StatefullOperation {\n\n  // MARK: - Properties\n  \n  private let callback: (StatefullOperation?) -\u003e Void\n  private let service: NetworkService             \n  private let dataHandler: Parsable            \n            \n  // MARK: - Initializers\n  \n  init(_ service: NetworkService, _ dataHandler: Parser, callback: @escaping (StatefullOperation?) -\u003e Void) {\n    self.service = service\n    self.dataHandler = dataHandler\n    self.callback = callback\n  }\n  \n  // MARK: - Overrides\n  \n  override func executableSection() {\n    service.dataTask { [weak self] result in \n      self?.dataHandler.parse(result)\n      self?.finishIfNotCancelled()\n      self?.callback(self)\n    }\n  }\n}\n```\n\nThen, the usage of the `NetworkStatefulOperation` class is quite straightforward:\n\n```swift\n// 1. Create an instance of `NetworkStatefulOperation` class:\nlet networkiOperation = NetworkStatefulOperation(service, parser) {\n  // 3. As soon as the operation is finished, this closure will be executed with the operation state that can futher be handled to properly update the UI:\n  updateUI(with: $0.state)\n}\n// 2. Then call the `start` method:\nnetworkOperation.start()\n```\n\n## Atomics\nGuarantees safe mutation of a property in multiple async dispatch queues. Simply wrap a property in `Atomic` type:\n\n```swift\nlet atomic = Atomic(0)\n\nDispatchQueue.global().async {\n  atomic.modify { $0 + 1 }\n}\n\nDispatchQueue.global().async {\n  atomic.modify { $0 + 1 }\n}\n```\n\nYou can also use slightly more performance-friendly `AtomicInt` and `AtomicBool` classes, hence there is no dynamic dispatch involved (though `Swift` compiler is smart enough to apply complier optimization called [`compile-time generic specialization`](https://forums.swift.org/t/compile-time-generic-specialization/5082))\n\n## Locks\n\n### Read Write Lock\nA syncronozation primitive that solves one of the readers–writers problems:\n\n```swift\nlet rwLock = ReadWriteLock()\n\nrwLock.writeLock()\nsharedValue += 1\nrwLock.unlock()\n```\n\nOr you can restrict the reading access, so other threads will not be able to read the mutated value of a property until the lock will be released:\n\n```swift\nlet rwLock = ReadWriteLock()\n\nrwLock.readLock()\nsharedValue += 1\nrwLock.unlock()\n```\n\n### Unfair Lock\nA lock which causes a thread trying to acquire it to simply wait in a loop (\"spin\"), while repeatedly checking if the lock is available:\n\n```swift\nlet unfairLock = UnfairLock()\n\nunfairLock.lock()\nsharedValue += 1\nunfairLock.unlock()\n```\n\n### Mutex\nUsed to protect shared resources. A mutex is owned by the task that takes it. In a given region of code only one thread is active:\n\n```swift\nlet mutex = Mutex()\n\nmutex.withCriticalScope {\n  return sharedValue += 1\n}\n```\n\n## Dispatch Queue\nThere is a convenience method that removes the need to pass `.now() + time` in order to make an async call:\n\n```swift\nDispatchQueue.main.asyncAfter(seconds: 2.5) {\n  expectation.fulfill()\n}\n```\n\nAlso, `DispatchQueue.once` was returned back::\n\n```swift\n// The following concurrentQueue is called multiple times, though the caughtValue will be set to value only once.\nconcurrentQueue.async {\n  DispatchQueue.once(token: \"caught\") {\n    caughtValue = value\n  }\n}\n```\n\n# 🏗 Installation\n\n## Swift Package Manager\n\n### Xcode 11+\n\n1. Open `MenuBar` → `File` → `Swift Packages` → `Add Package Dependency...`\n2. Paste the package repository url `https://github.com/jVirus/concurrency-kit` and hit `Next`.\n3. Select the installment rules.\n\nAfter specifying which version do you want to install, the package will be downloaded and attached to your project. \n\n### Package.swift\nIf you already have a `Package.swift` or you are building your own package simply add a new dependency:\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/jVirus/concurrency-kit\", from: \"1.0.0\")\n]\n```\n\n## Manual \nYou can always use copy-paste the sources method 😄. Or you can compile the framework and include it with your project.\n\n\n\n# 👨‍💻 Author \n[Astemir Eleev](https://github.com/jVirus)\n\n# 🔖 Licence\nThe project is available under [MIT licence](https://github.com/jVirus/concurrency-kit/blob/master/LICENSE)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feleev%2Fconcurrency-kit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feleev%2Fconcurrency-kit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feleev%2Fconcurrency-kit/lists"}