# Adding Swift Concurrency capabilities to 3rd party projects

Is Swift Concurrency ready to adopt? Yes, because

> Xcode 13.2 brings support for Swift Concurrency in applications that deploy to macOS Catalina 10.15, iOS 13, tvOS 13, and watchOS 6 or newer. This support includes async/await, actors, global actors, structured concurrency, and the task APIs.

However, your project dependencies might not (yet) support async/await.

No problem, I will show you how to to apply the modern Swift [Concurrency](https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html) in such a scenario.

As an example, I use [FeedKit](https://github.com/nmdias/FeedKit).

%[https://github.com/nmdias/FeedKit]

It's an RSS, Atom and JSON Feed parser written in Swift.

It supports asynchronous processing by passing the results, once available, back to the caller through a completion handler.

```swift
// Build a URL pointing to an RSS, Atom or JSON Feed.
let feedURL = URL(string: "http://images.apple.com/main/rss/hotnews/hotnews.rss")!

// Get an instance of `FeedParser`
let parser = FeedParser(URL: feedURL) 

// Parse asynchronously, not to block the UI.
parser.parseAsync(queue: DispatchQueue.global(qos: .userInitiated)) { (result) in
    // Do your thing, then back to the Main thread
    DispatchQueue.main.async {
        // ..and update the UI
    }
}
```

FeedKit is a wonderful framework and works like a charm but it does not support async/await.

So let's add an **async** function through a Swift extension 😊

```swift
import FeedKit

extension FeedParser {
    func asyncParse() async -> Result<Feed, ParserError> {
        await withCheckedContinuation { continuation in
            self.parseAsync(queue: DispatchQueue(label: "my.concurrent.queue", attributes: .concurrent)) { result in
                continuation.resume(returning: result)
            }
        }
    }
```

The implementation uses a [CheckedContinuation](https://developer.apple.com/documentation/swift/checkedcontinuation).

But how to create an **async** function that can fetch a dynamic number of feeds?

```swift
    func feeds(for feedURLs: [URL]) async -> [Feed] {
        let taskResult = await withTaskGroup(of: Optional<Feed>.self, returning: [Feed].self) { group in
            for feedURL in feedURLs {
                group.addTask {
                    let parser = FeedParser(URL: feedURL)
                    let result = await parser.asyncParse()
                    switch result {
                    case let .success(feed):
                        return (feed)
                    case .failure(_):
                        return (nil)
                    }
                }
            }

            var result: [Feed] = []
            for await feedResult in group {
                if let feed = feedResult {
                    result.append(feed)
                }
            }
            return result
        }
        return taskResult
    }
```

This function can run work in parallel thanks to Swift concurrency's  [TaskGroup](https://developer.apple.com/documentation/swift/taskgroup):

Done!

Here is a simplified example usable in a SwiftUI-based iOS application.

```swift
import FeedKit
import SwiftUI

struct ContentView: View {
    @State var parsedCount: Int = 0
    var body: some View {
        Text("Parsed Feeds: \(parsedCount)")
        .task { // perform when this view appears
            let feeds = await self.feeds(for: [
                URL(string: "http://images.apple.com/main/rss/hotnews/hotnews.rss")!,
                URL(string: "https://blog.eidinger.info/rss.xml")!
            ])
            parsedCount = feeds.count
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

extension ContentView {
    func feeds(for feedURLs: [URL]) async -> [Feed] {
        let taskResult = await withTaskGroup(of: Optional<Feed>.self, returning: [Feed].self) { group in
            for feedURL in feedURLs {
                group.addTask {
                    let parser = FeedParser(URL: feedURL)
                    let result = await parser.asyncParse()
                    switch result {
                    case let .success(feed):
                        return (feed)
                    case .failure(_):
                        return (nil)
                    }
                }
            }

            var result: [Feed] = []
            for await feedResult in group {
                if let feed = feedResult {
                    result.append(feed)
                }
            }
            return result
        }
        return taskResult
    }
}

extension FeedParser {
    func asyncParse() async -> Result<Feed, ParserError> {
        await withCheckedContinuation { continuation in
            self.parseAsync(queue: DispatchQueue(label: "FeedKit.concurrent.queue", attributes: .concurrent)) { result in
                continuation.resume(returning: result)
            }
        }
    }
}
```



