一键导入
combine-framework
Apple Combine framework for reactive programming. Publishers, subscribers, operators, and error handling.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Apple Combine framework for reactive programming. Publishers, subscribers, operators, and error handling.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Core Android development patterns for Kotlin, including coroutines, lifecycle management, and functional programming idioms.
Pattern extraction and skill generation for mobile development sessions. Automatically learns from your coding patterns.
Instinct-based learning system with confidence scoring for mobile patterns. Automatically extracts and evolves patterns.
Core Data for iOS persistence. Data models, fetch requests, background contexts, and SwiftData migration.
Kotlin Coroutines and Flow patterns for structured concurrency, error handling, and async operations.
Kotlin Multiplatform expect/actual patterns for platform-specific APIs. Learn to declare shared interfaces with platform implementations.
| name | combine-framework |
| description | Apple Combine framework for reactive programming. Publishers, subscribers, operators, and error handling. |
Reactive programming with Apple's Combine framework.
// ✅ Basic subscription
let publisher = Just("Hello")
let subscriber = publisher.sink { value in
print(value) // "Hello"
}
// ✅ Just - single value
Just(42)
// ✅ Future - async result
Future { promise in
promise(.success(42))
// or
promise(.failure(Error()))
}
// ✅ PassthroughSubject - broadcast
let subject = PassthroughSubject<String, Never>()
subject.send("Hello")
subject.send(completion: .finished)
// ✅ CurrentValueSubject - holds current value
let currentValue = CurrentValueSubject<Int, Never>(0)
currentValue.send(1)
print(currentValue.value) // 1
// ✅ @Published in ObservableObject
class ViewModel: ObservableObject {
@Published var count = 0
}
// ✅ map - transform values
[1, 2, 3].publisher
.map { $0 * 2 }
.sink { print($0) } // 2, 4, 6
// ✅ flatMap - nested publishers
[1, 2, 3].publisher
.flatMap { value in
Future { promise in
promise(.success(value * 2))
}
}
.sink { print($0) }
// ✅ scan - accumulate
(1...10).publisher
.scan(0, +)
.sink { print($0) } // 1, 3, 6, 10, 15...
// ✅ filter
(1...10).publisher
.filter { $0 % 2 == 0 }
.sink { print($0) } // 2, 4, 6, 8, 10
// ✅ removeDuplicates
[1, 1, 2, 2, 3].publisher
.removeDuplicates()
.sink { print($0) } // 1, 2, 3
// ✅ debounce - wait for pause
searchTextPublisher
.debounce(for: .milliseconds(300), scheduler: RunLoop.main)
.sink { text in
performSearch(text)
}
// ✅ combineLatest - waits for both
let namePublisher = CurrentValueSubject<String, Never>("")
let agePublisher = CurrentValueSubject<Int, Never>(0)
Publishers.CombineLatest(namePublisher, agePublisher)
.sink { name, age in
print("\(name), \(age)")
}
// ✅ merge - same type, interleaved
let p1 = PassthroughSubject<Int, Never>()
let p2 = PassthroughSubject<Int, Never>()
p1.merge(with: p2)
.sink { print($0) }
// ✅ zip - pairs up
let names = PassthroughSubject<String, Never>()
let ages = PassthroughSubject<Int, Never>()
names.zip(ages)
.sink { print("\($0) is \($1) years old") }
// ✅ catch - recover from error
failingPublisher
.catch { error in
return Just("Fallback value")
}
.sink { print($0) }
// ✅ retry - attempt on failure
failingPublisher
.retry(3)
.sink { print($0) }
// ✅ replaceError - replace with default
riskyPublisher
.replaceError(with: "Default")
.sink { print($0) }
// ✅ mapError - transform error
riskyPublisher
.mapError { error in
return MyError.custom(error)
}
.sink { _ in } receiveValue: { error in
print(error)
}
class HomeViewModel: ObservableObject {
@Published var users: [User] = []
@Published var isLoading = false
@Published var errorMessage: String?
private var cancellables = Set<AnyCancellable>()
func loadUsers() {
isLoading = true
service.getUsers()
.receive(on: DispatchQueue.main)
.sink { [weak self] completion in
self?.isLoading = false
if case .failure(let error) = completion {
self?.errorMessage = error.localizedDescription
}
} receiveValue: { [weak self] users in
self?.users = users
}
.store(in: &cancellables)
}
}
// ✅ assign to property
publisher
.map(\.name)
.assign(to: &$viewModel.userName)
// ✅ assign to KVO-compliant property
publisher
.map(\.text)
.assign(to: \.text, on: label)
class SearchViewModel: ObservableObject {
@Published var searchText = ""
@Published var results: [Result] = []
private let searchSubject = PassthroughSubject<String, Never>()
private var cancellables = Set<AnyCancellable>()
init() {
$searchText
.debounce(for: .milliseconds(300), scheduler: RunLoop.main)
.removeDuplicates()
.flatMap { query in
self.search(query)
.catch { _ in Just([]) }
}
.assign(to: &$results)
}
private func search(_ query: String) -> AnyPublisher<[Result], Never> {
// Perform search
}
}
// ✅ Publisher for notifications
extension NotificationCenter {
func publisher(for name: Notification.Name, object: AnyObject? = nil) -> NotificationCenter.Publisher {
Publisher(center: self, name: name, object: object)
}
struct Publisher: Combine.Publisher {
typealias Output = Notification
typealias Failure = Never
let center: NotificationCenter
let name: Notification.Name
let object: AnyObject?
func receive<S>(subscriber: S) where S: Subscriber, Notification == S.Input, Never == S.Failure {
let subscription = Subscription(
subscriber: subscriber,
center: center,
name: name,
object: object
)
subscriber.receive(subscription: subscription)
}
}
}
// Usage
NotificationCenter.default
.publisher(for: UIApplication.didBecomeActiveNotification)
.sink { notification in
print("App became active")
}
.store(in: &cancellables)
class ViewModel {
private var cancellables = Set<AnyCancellable>()
func setup() {
// ✅ Store subscription
publisher
.sink { value in
print(value)
}
.store(in: &cancellables)
// ✅ Or use retain cycle
var cancellable: AnyCancellable? = publisher
.sink { value in
print(value)
}
// cancellable = nil when done
}
}
func testPublisher_transformsValues() {
let publisher = [1, 2, 3].publisher
var results: [Int] = []
publisher
.map { $0 * 2 }
.sink { results.append($0) }
XCTAssertEqual(results, [2, 4, 6])
}
Remember: Combine is declarative. Describe your data flow chain, and let the framework handle execution.