강의를 들으며 공부했던 내용을 기록해본다...
import UIKit
import Combine
// Create a publisher that emits a single value (123)
let publisher = Just(123) // 단일 값 방출 pubisher
// Subscribe to the publisher and print the emitted value
let cancellable = publisher.sink { value in
// sink 사용하여 구독하고 값 출력
print(value) // Output: 123
}
// Cancel the subscription (not really needed in this case)
// Just는 즉시 값을 방출하고 종료됨. 따라서 여기서는 큰 의미가 없음
cancellable.cancel()
// Create a publisher that emits numbers from an array
let numbersPublisher = [1,2,3,4,5,6].publisher
// Transform each number by multiplying it by 2
let doublePublisher = numbersPublisher.map { $0 * 2 }
// Subscribe and print each transformed value
let cancellable = doublePublisher.sink { value in
print(value) // Output: 2, 4, 6, 8, 10, 12
}
import Foundation
import Combine
// Create a timer publisher that emits values every 1 second
let timerPublisher = Timer.publish(every: 1, on: .main, in: .common)
// 1초마다 이벤트 발생
// 왜 Main thread ? -> UI updates...
// .common : 일반적인 실행 루프에서 동작
// Automatically start the timer and subscribe to its events
let cancellable = timerPublisher
.autoconnect() // Automatically starts emitting values
// 타이머 publisher는 기본적으로 수동 시작 필요 -> authconnect로 자동 시작
.sink { timestamp in
print("Timestamp: \(timestamp)") // Output: Prints time every second
}
import UIKit
import Combine
class ViewController: UIViewController {
// Store subscriptions to avoid memory leaks
private var cancellables: Set<AnyCancellable> = [] // 구독을 관리하기 위한 Container
// view controller 해제될 때, 자동으로 subscribes are cancelled
// Create a text field
lazy var textField: UITextField = {
let tf = UITextField()
tf.placeholder = "Enter name"
tf.translatesAutoresizingMaskIntoConstraints = false
return tf
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.green
// Add text field to the view
view.addSubview(textField)
// Set up constraints (center the text field)
textField.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
textField.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
textField.widthAnchor.constraint(equalToConstant: 100).isActive = true
// Create a publisher to observe text changes in the text field
NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: textField)
.compactMap { $0.object as? UITextField } // Get the text field from the notification
.sink { textField in
if let text = textField.text {
print(text) // Output the text whenever it changes
}
}
.store(in: &cancellables) // Store subscription for automatic cancellation
}
}
import SwiftUI
import Combine
@main
struct DeviceOrientationPublisherApp: App {
// Store subscriptions to manage Combine publishers
private var cancellables: Set<AnyCancellable> = [] // 앱이 실행되는 동안 구독을 유지하고, 필요할 때 해제
init() {
// Create a publisher to listen for device orientation changes
NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)
.sink { _ in
let currentOrientation = UIDevice.current.orientation
print(currentOrientation) // Output the current device orientation
}
.store(in: &cancellables) // Store subscription for automatic cancellation
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
import UIKit
import Combine
// Create a publisher that emits numbers from 1 to 10
let numbersPublisher = (1...10).publisher
// Subscribe and print each emitted value
let cancellable = numbersPublisher.sink { value in
print(value) // Output: 1, 2, 3, 4, ..., 10 (printed immediately)
}
// Cancel the subscription after 2 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
cancellable.cancel() // This won't stop the publisher because all values are already emitted
}
import SwiftUI
import Combine
// ViewModel that manages the counter value
class ContentViewModel: ObservableObject {
@Published var value: Int = 0 // Published property that updates the view
private var cancellable: AnyCancellable? // Store the subscription
init() {
// Create a timer publisher that fires every second
let publisher = Timer.publish(every: 1.0, on: .main, in: .default)
.autoconnect() // Automatically starts the timer
.map { _ in self.value + 1 } // Increment value every second
// Assign the published value to the 'value' property
cancellable = publisher.assign(to: \.value, on: self)
}
}
struct ContentView: View {
@StateObject private var vm = ContentViewModel() // ViewModel instance
var body: some View {
VStack {
Text("\(vm.value)") // Display the counter value
.font(.largeTitle)
}
.padding()
}
}
#Preview {
ContentView()
}
import UIKit
import Combine
// Define a custom error
enum NumberError: Error {
case operationFailed
}
// Create a publisher that emits numbers from an array
let numbersPublisher = [1, 2, 3, 4, 5].publisher
// First approach: Handle error with mapError (Stops on error)
let doubledPublisher = numbersPublisher
.tryMap { number in
if number == 4 {
throw NumberError.operationFailed // Throw an error when number is 4
}
return number * 2 // Multiply number by 2
}
.mapError { error in
return NumberError.operationFailed // Convert any error to operationFailed
}
let cancellable = doubledPublisher.sink { completion in
switch completion {
case .finished:
print("finished")
case .failure(let error):
print(error) // Print the error
}
} receiveValue: { value in
print(value) // Print the doubled values
}
/* Output
2
4
6
operationFailed
*/
/* Second approach: Handle error with catch (Continues execution) */
let doubledPublisher2 = numbersPublisher
.tryMap { number in
if number == 4 {
throw NumberError.operationFailed // Throw an error when number is 4
}
return number * 2 // Multiply number by 2
}
.catch { error in
if let numberError = error as? NumberError {
print("Error occurred: \(numberError)") // Print error message
}
return Just(0) // Instead of stopping, emit 0 and continue
}
let cancellable2 = doubledPublisher2.sink { completion in
switch completion {
case .finished:
print("finished")
case .failure(let error):
print(error) // This will never be called because of catch
}
} receiveValue: { value in
print(value) // Print the values including 0 in case of an error
}
/* Output
2
4
6
Error occurred: operationFailed
0
10
finished
*/
receiveValue: { value in print(value) // Print the doubled values }→ catch 블록을 사용하지 않으면, number == 4일 때 throw를 만나면서 스트림이 종료됨. 이후 어떤 값도 방출되지 않음 → sink가 즉시 .failure로 완료됨