This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Closures cannot be weak, but we can wrap an object and its static factory method that returns an | |
// instance to create a pseudo weak closure | |
// | |
struct Method { | |
static func weak<T: AnyObject, A>(_ base: T, factory: @escaping (T) -> ((A) -> Void)) -> (A) -> Void { | |
{ [weak base] in base.map(factory)?($0) } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
/// Property wrapper that protects the wrapped value with `NSLock`. | |
/// Non trivial read/writes should access via `projectedValue` which acquires lock | |
/// and runs the supplied closure against it. | |
/// | |
/// ``` | |
/// @Locked var count: Int | |
/// $count { $0 = doSomething($0) } | |
/// ``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Combine | |
/// Subscriber that weakly references its target with a closure. | |
/// The target is sent to the action when it exists, so the caller can avoid | |
/// the [weak self] dance. | |
/// | |
/// ``` | |
/// let binder = Binder(self) { $0.doSomething($1) ) | |
/// ``` | |
/// |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Combine | |
extension Publisher { | |
/// Binds a publisher to a subscriber with a resulting cancellable | |
func bind<S: Subscriber>(to subscriber: S) -> AnyCancellable where S.Failure == Failure, S.Input == Output { | |
let binding = Binding(subscriber) | |
subscribe(binding) | |
return AnyCancellable(binding.cancel) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Combine.Publishers.ReceiveOn is seriously broken on iOS 13.0–2 | |
// https://forums.swift.org/t/combine-receive-on-runloop-main-loses-sent-value-how-can-i-make-it-work/28631 | |
// Provides fix by using OpenCombine version on iOS 13.0–2 and Combine version for iOS 13.3+ | |
import Combine | |
import Foundation | |
@available(iOS, deprecated: 13.3, message: "use receive(on:)") | |
public extension Publisher { |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Publisher type erasure that uses SE-0309 + unsafebitcast instead of the subclass approach of AnyPublisher. | |
// https://github.com/apple/swift-evolution/blob/main/proposals/0309-unlock-existential-types-for-all-protocols.md | |
// Available in latest toolchains on swift.org | |
// OpenCombine.AnyPublisher | |
// https://github.com/OpenCombine/OpenCombine/blob/master/Sources/OpenCombine/AnyPublisher.swift | |
extension Publisher { | |
func eraseToAnyPub() -> AnyPub<Output, Failure> { | |
AnyPub(self) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension NSAttributedString { | |
/// Iterates over all embedded `UIImage` instances within the string, updating the configuration | |
/// to include the provided `UITraitCollection`. | |
/// | |
func updatingImageAttachments(to traitCollection: UITraitCollection) -> NSAttributedString { | |
var updatedImages = [(Int, UIImage)]() | |
enumerateAttributes(in: NSRange(location: 0, length: length), options: .init(rawValue: 0)) { attributes, range, _ in | |
if let attachment = attributes[.attachment] as? NSTextAttachment, | |
let image = attachment.image ?? attachment.image(forBounds: .zero, textContainer: nil, characterIndex: 0), |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Combine | |
extension Publisher where Failure == Never { | |
/// Converts publisher to AsyncSequence | |
@available(iOS, deprecated: 15.0, message: "Use publisher.values directly") | |
var valuesAsync: AsyncPublisher<Self> { | |
AsyncPublisher(self) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Combine | |
extension Publisher { | |
/// Converts publisher to AsyncSequence. | |
@available(iOS, deprecated: 15.0, message: "Use publisher.values directly") | |
var valuesAsync: AsyncThrowingPublisher<Self> { | |
AsyncThrowingPublisher(self) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension Task where Failure == Error { | |
// Start a new Task with a timeout. If the timeout expires before the operation is | |
// completed then the task is cancelled and an error is thrown. | |
init(priority: TaskPriority? = nil, timeout: TimeInterval, operation: @escaping @Sendable () async throws -> Success) { | |
self = Task(priority: priority) { | |
try await withThrowingTaskGroup(of: Success.self) { group -> Success in | |
group.addTask(operation: operation) | |
group.addTask { | |
try await _Concurrency.Task.sleep(nanoseconds: UInt64(timeout * 1_000_000_000)) |