Skip to content

Instantly share code, notes, and snippets.

@tarunon
Last active September 25, 2021 09:30
Show Gist options
  • Save tarunon/494d58245fe47be79465bd6f14d4183a to your computer and use it in GitHub Desktop.
Save tarunon/494d58245fe47be79465bd6f14d4183a to your computer and use it in GitHub Desktop.
Swift Task Extension for slim AsyncSequence access.
import UIKit
class Base: UIViewController {
func mySomeAsyncSequence() -> AsyncStream<String> {
fatalError()
}
func use(_ value: String) {
print(value)
}
}
/**
* Today we got async/await and AsyncSequence, we can write extream concurrency programming, but...
* AsyncSequence's memory management is very lazy...
*/
class Common: Base {
override func viewDidLoad() {
super.viewDidLoad()
// start boilerplate 😭
Task { [weak self] in
guard let sequence = self?.mySomeAsyncSequence() else { return }
for await element in sequence {
guard let self = self else { return }
self.use(element)
}
}
// end boilerplate 😭
}
}
/**
* Ok, create Swift Concurrency support classes.
* AsyncSequence, show me your true power!
*/
class TaskContext<T: AnyObject> {
private weak var weakValue: T?
var value: T {
get throws {
guard let value = weakValue else { throw CancellationError() }
return value
}
}
init(weakValue: T?) {
self.weakValue = weakValue
}
}
extension Task where Failure == Error {
@discardableResult
init<T>(priority: TaskPriority? = nil, context: T, operation: @Sendable @escaping (TaskContext<T>) async throws -> Success) {
let taskContext = TaskContext(weakValue: context)
self.init(priority: priority, operation: {
try await operation(taskContext)
})
}
}
/**
* Try it out!
*/
class TunedUp: Base {
override func viewDidLoad() {
super.viewDidLoad()
// begin code ✨
Task(context: self) { context in
for await element in try context.value.mySomeAsyncSequence() {
try context.value.use(element)
}
}
// end code ✨
}
}
@tarunon
Copy link
Author

tarunon commented Sep 25, 2021

Just define tryUnwrapped operator on Optional extension can be more simple? 😭

struct NilError: Error {
  
}

extension Optional {
  var tryUnwrapped: Wrapped {
    get throws {
      switch self {
      case .some(let wrapped): return wrapped
      case .none: throw NilError()
      }
    }
  }
}

class TunedUp: Base {
  override func viewDidLoad() {
    super.viewDidLoad()
    // begin code ✨
    Task { [weak self] in
      for await element in try self.tryUnwrapped.mySomeAsyncSequence() {
        try self.tryUnwrapped.use(element)
      }
    }
    // end code ✨
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment