SlideShare a Scribd company logo
1 of 126
Download to read offline
뱅크샐러드박보영
RxSwift to Combine
feat. SwiftUI
Introduce
Introduce
Introduce
Introduce
Introduce
Introduce
Customize handling
of asynchronous events
by combining event-processing operators.
https://developer.apple.com/documentation/combine
Introduce
Combine declares publishers to
expose values that can change over
time, and subscribers to receive
those values from the publishers.
https://developer.apple.com/documentation/combine
Introduce
By adopting Combine, you’ll make your
code easier to read and maintain, by
centralizing your event-processing code
and eliminating troublesome techniques
like nested closures and convention-based
callbacks.
https://developer.apple.com/documentation/combine
Introduce
Index
개념
비교하기
예제로
확인하기
정리
개념 비교하기
Asynchronous Interfaces
•Target/Action
•Notification Center
•URLSession
•KVO
•Ad-hoc callbacks
왜? Combine
https://developer.apple.com/videos/play/wwdc2019/722/
A unified declarative API for
processing values over time
Combine은,
https://developer.apple.com/videos/play/wwdc2019/722/
Combine 핵심 요소
Publishers Subscribers Operators
Combine vs. RxSwift
Publishers Subscribers Operators
Observable Observer Operators
Publishers
/Subscribers
Observable
/Observer
Operator Operator
Combine vs. RxSwift
Publishers
/Subscribers
Observable
/Observer
Subject
Operator Operator
Cancellable
Subscribe(on:)
Combine vs. RxSwift
Publishers
/Subscribers
Observable
/Observer
Subject
Operator
Subject
Operator
Cancellable
Subscribe(on:)
Combine vs. RxSwift
Publishers
/Subscribers
Observable
/Observer
Subject
Operator
Subject
Operator
Cancellable Disposable
Subscribe(on:)
Combine vs. RxSwift
Publishers
/Subscribers
Observable
/Observer
Subject
Operator
Subject
Operator
Cancellable Disposable
subscribe(on:) subscribeOn(_:)
Combine vs. RxSwift
Publisher vs. Observable
• AnyPublisher • Observable
class Observable: ObservableType { }
struct AnyPublisher: Publisher { }
Publisher vs. Observable
• AnyPublisher • Observable
protocol Publisher { }⋯
⋯
⋯
Publisher vs. Observable
• AnyPublisher • Observable
protocol Publisher { }
struct AnyPublisher: Publisher { }
class Observable: ObservableType { }
⋯
⋯
⋯
Publisher vs. Observable
• AnyPublisher
• Value Type
• Observable
• Reference Type
struct AnyPublisher: Publisher { }
class Observable: ObservableType { }
⋯
⋯
Publisher vs. Observable
• AnyPublisher
• Value Type
• Output(Data type)
• Failure(Error type)
• Observable
• Reference Type
• Element(Data type)
• ❌
Publisher vs. Observable
• AnyPublisher
• Value Type
• Output(Data type)
• Failure(Error type)
• Observable
• Reference Type
• Element(Data type)
• ❌
AnyPublisher<String, Error> Observable<Result<String, Error>>
Publisher vs. Observable
• AnyPublisher
• Value Type
• Output(Data type)
• Failure(Error type)
• Observable
• Reference Type
• Element(Data type)
• ❌
AnyPublisher<String, Error>
AnyPublisher<String, Never>
Observable<Result<String, Error>>
Observable<String>
Operators, RxSwift Only
Combine에는 없는 Operators
•amb()
•asObserver()
•concatMap
•create
•delaySubscription
•dematerialize
•enumerated
•flatMapFirst
•from
•groupBy
•ifEmpty(switchTo:)
•interval
•materialize
•range
•repeatElement
•retryWhen
•sample
•withLatestFrom
https://github.com/CombineCommunity/rxswift-to-combine-cheatsheet
👋
Operators, Combine Only
Combine에만 있는 Operators
•tryMap
•tryScan
•tryFilter
•tryCompactMap
•tryRemoveDuplicates(by:)
•tryReduce
•tryMax(by:)
•tryMin(by:)
•tryContains(where:)
•tryAllSatisfy
•tryDrop(while:)
•tryPrefix(while:)
•tryFirst(where:)
•tryLast(where:)
•tryCatch
👀
Map vs. tryMap
func map<T>(_ transform: (Output) -> T)
-> Just<T>
func tryMap<T>(_ transform: (Output) throws -> T)
-> Result<T, Error>.Publisher
Combine Operators
• Merge, Merge3,
Merge4, Merge5,
Merge6, Merge7,
Merge8, MergeMany
• merge
• combineLatest• CombineLatest,
CombineLatest3,
CombineLatest4
• zip• Zip, Zip3, Zip4
• ReplaySubject• ❌
• BehaviorSubject• CurrentValueSubject
Subjects
• PassthroughSubject • PublishSubject
• ReplaySubject• ❌
• BehaviorSubject• CurrentValueSubject
Subjects
• PassthroughSubject • PublishSubject
class PassthroughSubject {
public init()
class PublishSubject {
public override init()
⋯
⋯
• ReplaySubject• ❌
• BehaviorSubject• CurrentValueSubject
Subjects
class CurrentValueSubject {
public init(_ value: Output)
class BehaviorSubject {
public init(value: Element)
⋯
⋯
🧟
Cancellable vs. Disposable
🧟
🧟
Cancellable vs. Disposable
🧟🗑 • Disposable
• DisposeBag
🧟
Cancellable vs. Disposable
🧟🗑 • Disposable
• DisposeBag
• Cancellable
• AnyCancellable
Cancellable vs. Disposable
NO DISPOSE BAG!
🗑 🙅
Cancellable vs. Disposable
var disposeBag = DisposeBag()
Observable.just(1)
.subscribe(onNext: { number in
print(number)
})
.disposed(by: disposeBag)
var cancellables = Set<AnyCancellable>()
Just(1)
.sink { number in
print(number)
}
.store(in: &cancellables)
Cancellable vs. Disposable
var disposeBag = DisposeBag()
Observable.just(1)
.subscribe(onNext: { number in
print(number)
})
.disposed(by: disposeBag)
var cancellables = Set<AnyCancellable>()
Just(1)
.sink { number in
print(number)
}
.store(in: &cancellables)
Thread Handling
http://reactivex.io/documentation/operators/subscribeon.html
Thread Handling
http://reactivex.io/documentation/operators/subscribeon.html
Just(1)
.subscribe(on: DispatchQueue.main)
.map { _ in
implements()
}
.sink { … }
Combine vs. RxSwift
Publishers
/Subscribers
Observable
/Observer
Subject
Operator
Subject
Operator
Cancellable Disposable
subscribe(on:) subscribeOn(_:)
WWDC 2019
•Introducing Combine
https://developer.apple.com/videos/play/wwdc2019/722/
•Combine in Practice
https://developer.apple.com/videos/play/wwdc2019/721/
•Modern Swift API Design
https://developer.apple.com/videos/play/wwdc2019/415/
Reference
예제로 확인하기
BringMyOwnBeer🍺
https://github.com/fimuxd/BringMyOwnBeer-
PunkAPI by Brewdog
List / Search / Random
https://github.com/fimuxd/BringMyOwnBeer-Combine
BringMyOwnBeer🍺
📡
import RxSwift
import Combine
func getBeers(page: Int?) -> Observable<Result<[Beer], PunkNetworkError>> {
return session.rx.data(request: URLRequest(url: url))
.map { data in
do {
let beers = try JSONDecoder().decode([Beer].self, from: data)
return .success(beers)
} catch {
return .failure(.error("JSON parsing 에러”))
}
}
}
func getBeers(page: Int?) -> AnyPublisher<[Beer], PunkNetworkError> {
return session.dataTaskPublisher(for: URLRequest(url: url))
.flatMap { data in
return Just(data.data)
.decode(type: [Beer].self, decoder: JSONDecoder())
.mapError { _ in
.error("JSON parsing 에러”)
}
}
.eraseToAnyPublisher()
}
import RxSwift
import Combine
func getBeers(page: Int?) -> Observable<Result<[Beer], PunkNetworkError>> {
return session.rx.data(request: URLRequest(url: url))
.map { data in
do {
let beers = try JSONDecoder().decode([Beer].self, from: data)
return .success(beers)
} catch {
return .failure(.error("JSON parsing 에러”))
}
}
}
func getBeers(page: Int?) -> AnyPublisher<[Beer], PunkNetworkError> {
return session.dataTaskPublisher(for: URLRequest(url: url))
.flatMap { data in
return Just(data.data)
.decode(type: [Beer].self, decoder: JSONDecoder())
.mapError { _ in
.error("JSON parsing 에러”)
}
}
.eraseToAnyPublisher()
}
import RxSwift
import Combine
import RxSwift
import Combine
func getBeers(page: Int?) -> Observable<Result<[Beer], PunkNetworkError>> {
return session.rx.data(request: URLRequest(url: url))
.map { data in
do {
let beers = try JSONDecoder().decode([Beer].self, from: data)
return .success(beers)
} catch {
return .failure(.error("JSON parsing 에러”))
}
}
}
func getBeers(page: Int?) -> AnyPublisher<[Beer], PunkNetworkError> {
return session.dataTaskPublisher(for: URLRequest(url: url))
.flatMap { data in
return Just(data.data)
.decode(type: [Beer].self, decoder: JSONDecoder())
.mapError { _ in
.error("JSON parsing 에러”)
}
}
.eraseToAnyPublisher()
}
import RxSwift
import Combine
func getBeers(page: Int?) -> Observable<Result<[Beer], PunkNetworkError>> {
return session.rx.data(request: URLRequest(url: url))
.map { data in
do {
let beers = try JSONDecoder().decode([Beer].self, from: data)
return .success(beers)
} catch {
return .failure(.error("JSON parsing 에러”))
}
}
}
func getBeers(page: Int?) -> AnyPublisher<[Beer], PunkNetworkError> {
return session.dataTaskPublisher(for: URLRequest(url: url))
.flatMap { data in
return Just(data.data)
.decode(type: [Beer].self, decoder: JSONDecoder())
.mapError { _ in
.error("JSON parsing 에러”)
}
}
.eraseToAnyPublisher()
}
Observable<Result<[Beer], PunkNetworkError>>
AnyPublisher<[Beer], PunkNetworkError>
import RxSwift
import Combine
func getBeers(page: Int?) -> Observable<Result<[Beer], PunkNetworkError>> {
return session.rx.data(request: URLRequest(url: url))
.map { data in
do {
let beers = try JSONDecoder().decode([Beer].self, from: data)
return .success(beers)
} catch {
return .failure(.error("JSON parsing 에러”))
}
}
}
func getBeers(page: Int?) -> AnyPublisher<[Beer], PunkNetworkError> {
return session.dataTaskPublisher(for: URLRequest(url: url))
.flatMap { data in
return Just(data.data)
.decode(type: [Beer].self, decoder: JSONDecoder())
.mapError { _ in
.error("JSON parsing 에러”)
}
}
.eraseToAnyPublisher()
}
import RxSwift
import Combine
func getBeers(page: Int?) -> Observable<Result<[Beer], PunkNetworkError>> {
return session.rx.data(request: URLRequest(url: url))
.map { data in
do {
let beers = try JSONDecoder().decode([Beer].self, from: data)
return .success(beers)
} catch {
return .failure(.error("JSON parsing 에러”))
}
}
}
func getBeers(page: Int?) -> AnyPublisher<[Beer], PunkNetworkError> {
return session.dataTaskPublisher(for: URLRequest(url: url))
.flatMap { data in
return Just(data.data)
.decode(type: [Beer].self, decoder: JSONDecoder())
.mapError { _ in
.error("JSON parsing 에러”)
}
}
.eraseToAnyPublisher()
}
session.rx.data
session.dataTaskPublisher
import RxSwift
import Combine
func getBeers(page: Int?) -> Observable<Result<[Beer], PunkNetworkError>> {
return session.rx.data(request: URLRequest(url: url))
.map { data in
do {
let beers = try JSONDecoder().decode([Beer].self, from: data)
return .success(beers)
} catch {
return .failure(.error("JSON parsing 에러”))
}
}
}
func getBeers(page: Int?) -> AnyPublisher<[Beer], PunkNetworkError> {
return session.dataTaskPublisher(for: URLRequest(url: url))
.flatMap { data in
return Just(data.data)
.decode(type: [Beer].self, decoder: JSONDecoder())
.mapError { _ in
.error("JSON parsing 에러”)
}
}
.eraseToAnyPublisher()
}
import RxSwift
import Combine
func getBeers(page: Int?) -> Observable<Result<[Beer], PunkNetworkError>> {
return session.rx.data(request: URLRequest(url: url))
.map { data in
do {
let beers = try JSONDecoder().decode([Beer].self, from: data)
return .success(beers)
} catch {
return .failure(.error("JSON parsing 에러”))
}
}
}
func getBeers(page: Int?) -> AnyPublisher<[Beer], PunkNetworkError> {
return session.dataTaskPublisher(for: URLRequest(url: url))
.flatMap { data in
return Just(data.data)
.decode(type: [Beer].self, decoder: JSONDecoder())
.mapError { _ in
.error("JSON parsing 에러”)
}
}
.eraseToAnyPublisher()
}
.decode(type:decoder:)
do {
try JSONDecoder().decode(_:from:)
} catch { }
import RxSwift
import Combine
func getBeers(page: Int?) -> Observable<Result<[Beer], PunkNetworkError>> {
return session.rx.data(request: URLRequest(url: url))
.map { data in
do {
let beers = try JSONDecoder().decode([Beer].self, from: data)
return .success(beers)
} catch {
return .failure(.error("JSON parsing 에러”))
}
}
}
func getBeers(page: Int?) -> AnyPublisher<[Beer], PunkNetworkError> {
return session.dataTaskPublisher(for: URLRequest(url: url))
.flatMap { data in
return Just(data.data)
.decode(type: [Beer].self, decoder: JSONDecoder())
.mapError { _ in
.error("JSON parsing 에러”)
}
}
.eraseToAnyPublisher()
}
🍻
•리스트 형태
•GET beerList API 이용
•한 번에 25개의 맥주 정보
•최대 325개의 맥주 정보
Beer List
import RxSwift
import RxCocoa
Beer List with RxSwift
import RxSwift
import RxCocoa
Beer List with RxSwift
ViewModel
Bindable
View
import RxSwift
import RxCocoa
Beer List with RxSwift
View
UIViewController
import RxSwift
import RxCocoa
Beer List with RxSwift
View
UIViewController
UITableView
import RxSwift
import RxCocoa
Beer List with RxSwift
View
UIViewController
UITableView
UITableViewDelegate
import RxSwift
import RxCocoa
Beer List with RxSwift
View
func items<S, O>(_ source: O)
-> (@escaping (UITableView, Int, S.Iterator.Element)
-> UITableViewCell)
-> Disposable
where S : Sequence, S == O.E, O : ObservableType
public var willDisplayCell: ControlEvent<WillDisplayCellEvent> { }
UIViewController
UITableView
UITableViewDelegate
import RxSwift
import RxCocoa
Beer List with RxSwift
View
ViewModel
Bindable
UIViewController
UITableView
UITableViewDelegate
UIViewController
UITableView
UITableViewDelegate
import RxSwift
import RxCocoa
Beer List with RxSwift
View
ViewModel
Bindable
var viewWillAppear: PublishSubject<Void> { get }
var willDisplayCell: PublishRelay<IndexPath> { get }
UIViewController
UITableView
UITableViewDelegate
import RxSwift
import RxCocoa
Beer List with RxSwift
View
ViewModel
Bindable
var cellData: Driver<[BeerListCell.Data]> { get }
var errorMessage: Signal<String> { get }
class BeerListViewController: UIViewController {
let tableView = UITableView()
func bind(_ viewModel: BeerListViewBindable) {
self.disposeBag = DisposeBag()
self.rx.viewWillAppear
.map { _ in Void() }
.bind(to: viewModel.viewWillAppear)
.disposed(by: disposeBag)
viewModel.cellData
.drive(tableView.rx.items) { tv, row, data in
let index = IndexPath(row: row, section: 0)
let cell = tv.dequeueReusableCell(
withIdentifier: String(describing: BeerListCell.self),
for: index
) as! BeerListCell
cell.setData(data: data)
return cell
}
.disposed(by: disposeBag)
}
func attribute() { }
func layout() { }
class BeerListViewController: UIViewController {
let tableView = UITableView()
func bind(_ viewModel: BeerListViewBindable) {
self.disposeBag = DisposeBag()
self.rx.viewWillAppear
.map { _ in Void() }
.bind(to: viewModel.viewWillAppear)
.disposed(by: disposeBag)
viewModel.cellData
.drive(tableView.rx.items) { tv, row, data in
let index = IndexPath(row: row, section: 0)
let cell = tv.dequeueReusableCell(
withIdentifier: String(describing: BeerListCell.self),
for: index
) as! BeerListCell
cell.setData(data: data)
return cell
}
.disposed(by: disposeBag)
}
func attribute() { }
func layout() { }
let tableView = UITableView()
class BeerListViewController: UIViewController {
let tableView = UITableView()
func bind(_ viewModel: BeerListViewBindable) {
self.disposeBag = DisposeBag()
self.rx.viewWillAppear
.map { _ in Void() }
.bind(to: viewModel.viewWillAppear)
.disposed(by: disposeBag)
viewModel.cellData
.drive(tableView.rx.items) { tv, row, data in
let index = IndexPath(row: row, section: 0)
let cell = tv.dequeueReusableCell(
withIdentifier: String(describing: BeerListCell.self),
for: index
) as! BeerListCell
cell.setData(data: data)
return cell
}
.disposed(by: disposeBag)
}
func attribute() { }
func layout() { }
class BeerListViewController: UIViewController {
let tableView = UITableView()
func bind(_ viewModel: BeerListViewBindable) {
self.disposeBag = DisposeBag()
self.rx.viewWillAppear
.map { _ in Void() }
.bind(to: viewModel.viewWillAppear)
.disposed(by: disposeBag)
viewModel.cellData
.drive(tableView.rx.items) { tv, row, data in
let index = IndexPath(row: row, section: 0)
let cell = tv.dequeueReusableCell(
withIdentifier: String(describing: BeerListCell.self),
for: index
) as! BeerListCell
cell.setData(data: data)
return cell
}
.disposed(by: disposeBag)
}
func attribute() { }
func layout() { }
self.rx.viewWillAppear
.map { _ in Void() }
.bind(to: viewModel.viewWillAppear)
.disposed(by: disposeBag)
class BeerListViewController: UIViewController {
let tableView = UITableView()
func bind(_ viewModel: BeerListViewBindable) {
self.disposeBag = DisposeBag()
self.rx.viewWillAppear
.map { _ in Void() }
.bind(to: viewModel.viewWillAppear)
.disposed(by: disposeBag)
viewModel.cellData
.drive(tableView.rx.items) { tv, row, data in
let index = IndexPath(row: row, section: 0)
let cell = tv.dequeueReusableCell(
withIdentifier: String(describing: BeerListCell.self),
for: index
) as! BeerListCell
cell.setData(data: data)
return cell
}
.disposed(by: disposeBag)
}
func attribute() { }
func layout() { }
class BeerListViewController: UIViewController {
let tableView = UITableView()
func bind(_ viewModel: BeerListViewBindable) {
self.disposeBag = DisposeBag()
self.rx.viewWillAppear
.map { _ in Void() }
.bind(to: viewModel.viewWillAppear)
.disposed(by: disposeBag)
viewModel.cellData
.drive(tableView.rx.items) { tv, row, data in
let index = IndexPath(row: row, section: 0)
let cell = tv.dequeueReusableCell(
withIdentifier: String(describing: BeerListCell.self),
for: index
) as! BeerListCell
cell.setData(data: data)
return cell
}
.disposed(by: disposeBag)
}
func attribute() { }
func layout() { }
viewModel.cellData
.drive(tableView.rx.items) { }
.disposed(by: disposeBag)
protocol BeerListViewBindable {
//View -> ViewModel
var viewWillAppear: PublishSubject<Void> { get }
var willDisplayCell: PublishRelay<IndexPath> { get }
//ViewModel -> View
var cellData: Driver<[BeerListCell.Data]> { get }
var errorMessage: Signal<String> { get }
}
protocol BeerListViewBindable {
//View -> ViewModel
var viewWillAppear: PublishSubject<Void> { get }
var willDisplayCell: PublishRelay<IndexPath> { get }
//ViewModel -> View
var cellData: Driver<[BeerListCell.Data]> { get }
var errorMessage: Signal<String> { get }
}
protocol BeerListViewBindable {
//View -> ViewModel
var viewWillAppear: PublishSubject<Void> { get }
var willDisplayCell: PublishRelay<IndexPath> { get }
//ViewModel -> View
var cellData: Driver<[BeerListCell.Data]> { get }
var errorMessage: Signal<String> { get }
}
struct BeerListViewModel: BeerListViewBindable {
let disposeBag = DisposeBag()
let viewWillAppear = PublishSubject<Void>()
let cellData: Driver<[BeerListCell.Data]>
let willDisplayCell = PublishRelay<IndexPath>()
let errorMessage: Signal<String>
private var cells = BehaviorRelay<[Beer]>(value: [])
init(model: BeerListModel = BeerListModel()) {
struct BeerListViewModel: BeerListViewBindable {
init(model: BeerListModel = BeerListModel()) {
let beerListResult = viewWillAppear
.flatMapLatest(model.getBeerList)
.asObservable()
.share()
let beerListValue = beerListResult
.map { result -> [Beer]? in
guard case .success(let value) = result else {
return nil
}
return value
}
.filterNil()
let beerListError = beerListResult
.map { result -> String? in
guard case .failure(let error) = result else {
return nil
}
return error.message
}
.filterNil()
struct BeerListViewModel: BeerListViewBindable {
init(model: BeerListModel = BeerListModel()) {
let beerListResult = viewWillAppear
.flatMapLatest(model.getBeerList)
.asObservable()
.share()
let beerListValue = beerListResult
.map { result -> [Beer]? in
guard case .success(let value) = result else {
return nil
}
return value
}
.filterNil()
let beerListError = beerListResult
.map { result -> String? in
guard case .failure(let error) = result else {
return nil
}
return error.message
}
.filterNil()
struct BeerListViewModel: BeerListViewBindable {
init(model: BeerListModel = BeerListModel()) {
let beerListResult = viewWillAppear
.flatMapLatest(model.getBeerList)
.asObservable()
.share()
let beerListValue = beerListResult
.map { result -> [Beer]? in
guard case .success(let value) = result else {
return nil
}
return value
}
.filterNil()
let beerListError = beerListResult
.map { result -> String? in
guard case .failure(let error) = result else {
return nil
}
return error.message
}
.filterNil()
struct BeerListViewModel: BeerListViewBindable {
Observable
.merge(
beerListValue,
fetchedList
)
.scan([]){ prev, newList in
return newList.isEmpty ? [] : prev + newList
}
.bind(to: cells)
.disposed(by: disposeBag)
self.cellData = cells
.map(model.parseData)
.asDriver(onErrorDriveWith: .empty())
self.errorMessage = Observable
.merge(
beerListError,
fetchedError
)
.asSignal(onErrorJustReturn: .defaultError.message ?? “")
}
import Combine
import SwiftUI
Beer List with Combine
UI Binding
Combine
SwiftUI
RxSwift
RxCocoa
View 는,
https://developer.apple.com/videos/play/wwdc2019/226
Views are a function of state,
not of a sequence of events.
Property Wrapper
@State @ObservableObject
https://developer.apple.com/videos/play/wwdc2019/226
Property Wrapper
@State @ObservableObject
https://developer.apple.com/videos/play/wwdc2019/226
View-local External
Property Wrapper
@State @ObservableObject
https://developer.apple.com/videos/play/wwdc2019/226
View-local External
prev) BindableObject
Property Wrapper
@State @ObservableObject
https://developer.apple.com/videos/play/wwdc2019/226
View-local
Value type
External
Reference type
prev) BindableObject
Property Wrapper
@State @ObservableObject
https://developer.apple.com/videos/play/wwdc2019/226
View-local
Value type
Framework Managed
External
Reference type
Developer Managed
prev) BindableObject
🧑💻
SwiftUI
Action
User Interaction
https://developer.apple.com/videos/play/wwdc2019/226
Data Flow through SwiftUI
🧑💻
SwiftUI
Action
@StateMutation
User Interaction
https://developer.apple.com/videos/play/wwdc2019/226
Data Flow through SwiftUI
🧑💻
SwiftUI
Action View
@State UpdatesMutation
User Interaction
https://developer.apple.com/videos/play/wwdc2019/226
Data Flow through SwiftUI
🧑💻
SwiftUI
Action View
@State
Render
UpdatesMutation
User Interaction
https://developer.apple.com/videos/play/wwdc2019/226
Data Flow through SwiftUI
🧑💻
SwiftUI
Action
@State
Render
UpdatesMutation
User Interaction
https://developer.apple.com/videos/play/wwdc2019/226
🧨 🍺
@Publisher
View
Data Flow through SwiftUI
import SwiftUI
struct BeerList: View {
@ObservedObject var viewModel: BeerListViewModel
init(viewModel: BeerListViewModel) {
self.viewModel = viewModel
}
var body: some View {
NavigationView {
List(viewModel.beers, id: .id) { beer in
BeerRow(beer: beer).onAppear {
self.viewModel.appearedID.send(beer.id)
}
}
.navigationBarTitle(Text("맥주리스트"))
}
.alert(isPresented: $viewModel.showingAlert) {
Alert(title: Text(viewModel.errorMessage))
}
}
}
struct BeerList: View {
@ObservedObject var viewModel: BeerListViewModel
init(viewModel: BeerListViewModel) {
self.viewModel = viewModel
}
var body: some View {
NavigationView {
List(viewModel.beers, id: .id) { beer in
BeerRow(beer: beer).onAppear {
self.viewModel.appearedID.send(beer.id)
}
}
.navigationBarTitle(Text("맥주리스트"))
}
.alert(isPresented: $viewModel.showingAlert) {
Alert(title: Text(viewModel.errorMessage))
}
}
}
class BeerListViewModel: ObservableObject {
@Published var beers: [Beer] = []
@Published var showingAlert: Bool = false
@Published var errorMessage: String = ""
let appearedID = PassthroughSubject<Int?, PunkNetworkError>()
private var cancellables = Set<AnyCancellable>()
init(model: BeerListModel = BeerListModel()) {
class BeerListViewModel: ObservableObject {
//ViewModel -> View
@Published var beers: [Beer] = []
@Published var showingAlert: Bool = false
@Published var errorMessage: String = ""
//View -> ViewModel
let appearedID = PassthroughSubject<Int?, PunkNetworkError>()
private var cancellables = Set<AnyCancellable>()
init(model: BeerListModel = BeerListModel()) {
class BeerListViewModel: ObservableObject {
//ViewModel -> View
@Published var beers: [Beer] = []
@Published var showingAlert: Bool = false
@Published var errorMessage: String = ""
//View -> ViewModel
let appearedID = PassthroughSubject<Int?, PunkNetworkError>()
private var cancellables = Set<AnyCancellable>()
init(model: BeerListModel = BeerListModel()) {
class BeerListViewModel: ObservableObject {
init(model: BeerListModel = BeerListModel()) {
let loadBeerList = appearedID
.map { model.getPageToPatch(beers: self.beers, id: $0) }
.filter { $0 != nil }
.eraseToAnyPublisher()
class BeerListViewModel: ObservableObject {
init(model: BeerListModel = BeerListModel()) {
let loadBeerList = appearedID
.map { model.getPageToPatch(beers: self.beers, id: $0) }
.filter { $0 != nil }
.eraseToAnyPublisher()
.eraseToAnyPublisher() .asObservable()
class BeerListViewModel: ObservableObject {
.eraseToAnyPublisher()
loadBeerList
.prepend(nil)
.flatMap(model.getBeerList)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: {
guard case .failure(let error) = $0 else { return }
self.beers = []
self.showingAlert = true
self.errorMessage = error.message ?? "에러 발생🚨”
}, receiveValue: { beers in
self.beers += beers
})
.store(in: &cancellables)
}
}
class BeerListViewModel: ObservableObject {
.eraseToAnyPublisher()
loadBeerList
.prepend(nil)
.flatMap(model.getBeerList)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: {
guard case .failure(let error) = $0 else { return }
self.beers = []
self.showingAlert = true
self.errorMessage = error.message ?? "에러 발생🚨”
}, receiveValue: { beers in
self.beers += beers
})
.store(in: &cancellables)
}
}
class BeerListViewModel: ObservableObject {
.eraseToAnyPublisher()
loadBeerList
.prepend(nil)
.flatMap(model.getBeerList)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: {
guard case .failure(let error) = $0 else { return }
self.beers = []
self.showingAlert = true
self.errorMessage = error.message ?? "에러 발생🚨”
}, receiveValue: { beers in
self.beers += beers
})
.store(in: &cancellables)
}
}
class BeerListViewModel: ObservableObject {
.eraseToAnyPublisher()
loadBeerList
.prepend(nil)
.flatMap(model.getBeerList)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: {
guard case .failure(let error) = $0 else { return }
self.beers = []
self.showingAlert = true
self.errorMessage = error.message ?? "에러 발생🚨”
}, receiveValue: { beers in
self.beers += beers
})
.store(in: &cancellables)
}
}
.sink(
receiveCompletion:
receiveValue:
)
.subscribe(
onNext:
onError:
onCompleted:
onDisposed:
)
class BeerListViewModel: ObservableObject {
.eraseToAnyPublisher()
loadBeerList
.prepend(nil)
.flatMap(model.getBeerList)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: {
guard case .failure(let error) = $0 else { return }
self.beers = []
self.showingAlert = true
self.errorMessage = error.message ?? "에러 발생🚨”
}, receiveValue: { beers in
self.beers += beers
})
.store(in: &cancellables)
}
}
@Published var beers: [Beer] = []
import SwiftUI
struct BeerList: View {
@ObservedObject var viewModel: BeerListViewModel
init(viewModel: BeerListViewModel) {
self.viewModel = viewModel
}
var body: some View {
NavigationView {
List(viewModel.beers, id: .id) { beer in
BeerRow(beer: beer).onAppear {
self.viewModel.appearedID.send(beer.id)
}
}
.navigationBarTitle(Text("맥주리스트"))
}
.alert(isPresented: $viewModel.showingAlert) {
Alert(title: Text(viewModel.errorMessage))
}
}
}
import SwiftUI
struct BeerList: View {
@ObservedObject var viewModel: BeerListViewModel
init(viewModel: BeerListViewModel) {
self.viewModel = viewModel
}
var body: some View {
NavigationView {
List(viewModel.beers, id: .id) { beer in
BeerRow(beer: beer).onAppear {
self.viewModel.appearedID.send(beer.id)
}
}
.navigationBarTitle(Text("맥주리스트"))
}
.alert(isPresented: $viewModel.showingAlert) {
Alert(title: Text(viewModel.errorMessage))
}
}
}
struct List: View {
public init(
_ data: Data,
id: KeyPath<Data.Element, ID>,
⋯
BringMyOwnBeer🍺
정리
Combine/SwiftUI RxSwift/RxCocoa
Summary
App Size
0MB
1MB
2MB
3MB
4MB
Combine RxSwift
Summary
Summary
https://medium.com/flawless-app-stories/will-combine-kill-rxswift-64780a150d89
Summary
The memory models of RxSwift and Combine are very different.
Combine is really made for performance.
https://engineering.q42.nl/swift-combine-framework/
Summary
👨💻 ❤ RxSwift
Summary
👨💻 ❤ Combine
Summary
👨💻 ❤ Combine
👩💻 ❓ RxSwift
Summary
👨💻 ❤ Combine
👩💻 ❤ Combine
Summary
👨💻 ❤ Combine
👩💻 ❤ Combine
🎁 SwiftUI
✨
✨ ✨
✨ ✨
Summary
👨💻
❤
Combine👩💻
❤
Combine
🎁 SwiftUI
🎁 SwiftUI
👨💻
❤
Combine👩💻
❤
Combine
Summary
iOS 13.0+
⚡⚡⚡
⚡⚡⚡
⚡⚡⚡
완벽함이란 더 이상 더할 것이 없는 상태가 아니라,
더 이상 뺄 것이 없는 상태를 말한다.
Antoine de Saint-Exupéry
RxSwift to Combine

More Related Content

What's hot

Hibernate tutorial for beginners
Hibernate tutorial for beginnersHibernate tutorial for beginners
Hibernate tutorial for beginnersRahul Jain
 
알아보자 Dependency Injection과 Deli
알아보자 Dependency Injection과 Deli알아보자 Dependency Injection과 Deli
알아보자 Dependency Injection과 DeliJungwon An
 
LetSwift 2017 - 토스 iOS 앱의 개발/배포 환경
LetSwift 2017 - 토스 iOS 앱의 개발/배포 환경LetSwift 2017 - 토스 iOS 앱의 개발/배포 환경
LetSwift 2017 - 토스 iOS 앱의 개발/배포 환경Mintak Son
 
Preparing for Growth - Architecting Giant Apps for Scalability and Build Speed
Preparing for Growth - Architecting Giant Apps for Scalability and Build SpeedPreparing for Growth - Architecting Giant Apps for Scalability and Build Speed
Preparing for Growth - Architecting Giant Apps for Scalability and Build SpeedBruno Rocha
 
Node-REDなら、DIYで産業用センサ・コントローラを繋いで見える化
Node-REDなら、DIYで産業用センサ・コントローラを繋いで見える化Node-REDなら、DIYで産業用センサ・コントローラを繋いで見える化
Node-REDなら、DIYで産業用センサ・コントローラを繋いで見える化nodered_ug_jp
 
Redux Toolkit - Quick Intro - 2022
Redux Toolkit - Quick Intro - 2022Redux Toolkit - Quick Intro - 2022
Redux Toolkit - Quick Intro - 2022Fabio Biondi
 
Let'Swift 2022 PencilKit과 Point in Polygon 알고리즘을 활용한 올가미 툴 개발기
Let'Swift 2022 PencilKit과 Point in Polygon 알고리즘을 활용한 올가미 툴 개발기Let'Swift 2022 PencilKit과 Point in Polygon 알고리즘을 활용한 올가미 툴 개발기
Let'Swift 2022 PencilKit과 Point in Polygon 알고리즘을 활용한 올가미 툴 개발기Haeseok Lee
 
20220716_만들면서 느껴보는 POP
20220716_만들면서 느껴보는 POP20220716_만들면서 느껴보는 POP
20220716_만들면서 느껴보는 POPChiwon Song
 
ガード節を使おう
ガード節を使おうガード節を使おう
ガード節を使おうsmicle
 
DI Container를 이용하여 레거시와 모듈화를 동시에 잡기
DI Container를 이용하여 레거시와 모듈화를 동시에 잡기DI Container를 이용하여 레거시와 모듈화를 동시에 잡기
DI Container를 이용하여 레거시와 모듈화를 동시에 잡기정민 안
 
RxSwift 활용하기 - Let'Swift 2017
RxSwift 활용하기 - Let'Swift 2017RxSwift 활용하기 - Let'Swift 2017
RxSwift 활용하기 - Let'Swift 2017Wanbok Choi
 
T90 きっと怖くないmvvm & mvpvm
T90 きっと怖くないmvvm & mvpvmT90 きっと怖くないmvvm & mvpvm
T90 きっと怖くないmvvm & mvpvm伸男 伊藤
 
Introduction à spring boot
Introduction à spring bootIntroduction à spring boot
Introduction à spring bootAntoine Rey
 

What's hot (20)

Hibernate tutorial for beginners
Hibernate tutorial for beginnersHibernate tutorial for beginners
Hibernate tutorial for beginners
 
알아보자 Dependency Injection과 Deli
알아보자 Dependency Injection과 Deli알아보자 Dependency Injection과 Deli
알아보자 Dependency Injection과 Deli
 
Java - Lombok
Java - LombokJava - Lombok
Java - Lombok
 
LetSwift 2017 - 토스 iOS 앱의 개발/배포 환경
LetSwift 2017 - 토스 iOS 앱의 개발/배포 환경LetSwift 2017 - 토스 iOS 앱의 개발/배포 환경
LetSwift 2017 - 토스 iOS 앱의 개발/배포 환경
 
React Hooks
React HooksReact Hooks
React Hooks
 
Preparing for Growth - Architecting Giant Apps for Scalability and Build Speed
Preparing for Growth - Architecting Giant Apps for Scalability and Build SpeedPreparing for Growth - Architecting Giant Apps for Scalability and Build Speed
Preparing for Growth - Architecting Giant Apps for Scalability and Build Speed
 
Node-REDなら、DIYで産業用センサ・コントローラを繋いで見える化
Node-REDなら、DIYで産業用センサ・コントローラを繋いで見える化Node-REDなら、DIYで産業用センサ・コントローラを繋いで見える化
Node-REDなら、DIYで産業用センサ・コントローラを繋いで見える化
 
Redux Toolkit - Quick Intro - 2022
Redux Toolkit - Quick Intro - 2022Redux Toolkit - Quick Intro - 2022
Redux Toolkit - Quick Intro - 2022
 
Lombok
LombokLombok
Lombok
 
Let'Swift 2022 PencilKit과 Point in Polygon 알고리즘을 활용한 올가미 툴 개발기
Let'Swift 2022 PencilKit과 Point in Polygon 알고리즘을 활용한 올가미 툴 개발기Let'Swift 2022 PencilKit과 Point in Polygon 알고리즘을 활용한 올가미 툴 개발기
Let'Swift 2022 PencilKit과 Point in Polygon 알고리즘을 활용한 올가미 툴 개발기
 
Introduction to Spring Boot
Introduction to Spring BootIntroduction to Spring Boot
Introduction to Spring Boot
 
Spring & hibernate
Spring & hibernateSpring & hibernate
Spring & hibernate
 
20220716_만들면서 느껴보는 POP
20220716_만들면서 느껴보는 POP20220716_만들면서 느껴보는 POP
20220716_만들면서 느껴보는 POP
 
ガード節を使おう
ガード節を使おうガード節を使おう
ガード節を使おう
 
RxSwift
RxSwiftRxSwift
RxSwift
 
DI Container를 이용하여 레거시와 모듈화를 동시에 잡기
DI Container를 이용하여 레거시와 모듈화를 동시에 잡기DI Container를 이용하여 레거시와 모듈화를 동시에 잡기
DI Container를 이용하여 레거시와 모듈화를 동시에 잡기
 
Mvpvm pattern
Mvpvm patternMvpvm pattern
Mvpvm pattern
 
RxSwift 활용하기 - Let'Swift 2017
RxSwift 활용하기 - Let'Swift 2017RxSwift 활용하기 - Let'Swift 2017
RxSwift 활용하기 - Let'Swift 2017
 
T90 きっと怖くないmvvm & mvpvm
T90 きっと怖くないmvvm & mvpvmT90 きっと怖くないmvvm & mvpvm
T90 きっと怖くないmvvm & mvpvm
 
Introduction à spring boot
Introduction à spring bootIntroduction à spring boot
Introduction à spring boot
 

Similar to RxSwift to Combine

Marvel of Annotation Preprocessing in Java by Alexey Buzdin
Marvel of Annotation Preprocessing in Java by Alexey BuzdinMarvel of Annotation Preprocessing in Java by Alexey Buzdin
Marvel of Annotation Preprocessing in Java by Alexey BuzdinJava User Group Latvia
 
How Bitbucket Pipelines Loads Connect UI Assets Super-fast
How Bitbucket Pipelines Loads Connect UI Assets Super-fastHow Bitbucket Pipelines Loads Connect UI Assets Super-fast
How Bitbucket Pipelines Loads Connect UI Assets Super-fastAtlassian
 
Supercharging reflective libraries with InvokeDynamic
Supercharging reflective libraries with InvokeDynamicSupercharging reflective libraries with InvokeDynamic
Supercharging reflective libraries with InvokeDynamicIan Robertson
 
Integrating React.js Into a PHP Application
Integrating React.js Into a PHP ApplicationIntegrating React.js Into a PHP Application
Integrating React.js Into a PHP ApplicationAndrew Rota
 
Javascript Everywhere
Javascript EverywhereJavascript Everywhere
Javascript EverywherePascal Rettig
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasminePaulo Ragonha
 
Introduction to Django
Introduction to DjangoIntroduction to Django
Introduction to DjangoJames Casey
 
From Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) AgainFrom Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) Againjonknapp
 
Riak at The NYC Cloud Computing Meetup Group
Riak at The NYC Cloud Computing Meetup GroupRiak at The NYC Cloud Computing Meetup Group
Riak at The NYC Cloud Computing Meetup Groupsiculars
 
FrenchKit 2017: Server(less) Swift
FrenchKit 2017: Server(less) SwiftFrenchKit 2017: Server(less) Swift
FrenchKit 2017: Server(less) SwiftChris Bailey
 
Intro to Asynchronous Javascript
Intro to Asynchronous JavascriptIntro to Asynchronous Javascript
Intro to Asynchronous JavascriptGarrett Welson
 
How to Contribute to Apache Usergrid
How to Contribute to Apache UsergridHow to Contribute to Apache Usergrid
How to Contribute to Apache UsergridDavid M. Johnson
 
Protocol-Oriented Programming in Swift
Protocol-Oriented Programming in SwiftProtocol-Oriented Programming in Swift
Protocol-Oriented Programming in SwiftOleksandr Stepanov
 
React starter-kitでとっとと始めるisomorphic開発
React starter-kitでとっとと始めるisomorphic開発React starter-kitでとっとと始めるisomorphic開発
React starter-kitでとっとと始めるisomorphic開発Yoichi Toyota
 
Firefox OS, une plateforme à découvrir - IO Saglac - 2014-09-09
Firefox OS, une plateforme à découvrir - IO Saglac - 2014-09-09Firefox OS, une plateforme à découvrir - IO Saglac - 2014-09-09
Firefox OS, une plateforme à découvrir - IO Saglac - 2014-09-09Frédéric Harper
 
OSGi and Spring Data for simple (Web) Application Development
OSGi and Spring Data  for simple (Web) Application DevelopmentOSGi and Spring Data  for simple (Web) Application Development
OSGi and Spring Data for simple (Web) Application DevelopmentChristian Baranowski
 
OSGi and Spring Data for simple (Web) Application Development - Christian Bar...
OSGi and Spring Data for simple (Web) Application Development - Christian Bar...OSGi and Spring Data for simple (Web) Application Development - Christian Bar...
OSGi and Spring Data for simple (Web) Application Development - Christian Bar...mfrancis
 
OpenWhisk: Event-driven Design
OpenWhisk: Event-driven DesignOpenWhisk: Event-driven Design
OpenWhisk: Event-driven DesignAltoros
 
OpenWhisk Under the Hood -- London Oct 16 2016
OpenWhisk Under the Hood -- London Oct 16 2016OpenWhisk Under the Hood -- London Oct 16 2016
OpenWhisk Under the Hood -- London Oct 16 2016Stephen Fink
 

Similar to RxSwift to Combine (20)

RxSwift to Combine
RxSwift to CombineRxSwift to Combine
RxSwift to Combine
 
Marvel of Annotation Preprocessing in Java by Alexey Buzdin
Marvel of Annotation Preprocessing in Java by Alexey BuzdinMarvel of Annotation Preprocessing in Java by Alexey Buzdin
Marvel of Annotation Preprocessing in Java by Alexey Buzdin
 
How Bitbucket Pipelines Loads Connect UI Assets Super-fast
How Bitbucket Pipelines Loads Connect UI Assets Super-fastHow Bitbucket Pipelines Loads Connect UI Assets Super-fast
How Bitbucket Pipelines Loads Connect UI Assets Super-fast
 
Supercharging reflective libraries with InvokeDynamic
Supercharging reflective libraries with InvokeDynamicSupercharging reflective libraries with InvokeDynamic
Supercharging reflective libraries with InvokeDynamic
 
Integrating React.js Into a PHP Application
Integrating React.js Into a PHP ApplicationIntegrating React.js Into a PHP Application
Integrating React.js Into a PHP Application
 
Javascript Everywhere
Javascript EverywhereJavascript Everywhere
Javascript Everywhere
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
 
Introduction to Django
Introduction to DjangoIntroduction to Django
Introduction to Django
 
From Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) AgainFrom Backbone to Ember and Back(bone) Again
From Backbone to Ember and Back(bone) Again
 
Riak at The NYC Cloud Computing Meetup Group
Riak at The NYC Cloud Computing Meetup GroupRiak at The NYC Cloud Computing Meetup Group
Riak at The NYC Cloud Computing Meetup Group
 
FrenchKit 2017: Server(less) Swift
FrenchKit 2017: Server(less) SwiftFrenchKit 2017: Server(less) Swift
FrenchKit 2017: Server(less) Swift
 
Intro to Asynchronous Javascript
Intro to Asynchronous JavascriptIntro to Asynchronous Javascript
Intro to Asynchronous Javascript
 
How to Contribute to Apache Usergrid
How to Contribute to Apache UsergridHow to Contribute to Apache Usergrid
How to Contribute to Apache Usergrid
 
Protocol-Oriented Programming in Swift
Protocol-Oriented Programming in SwiftProtocol-Oriented Programming in Swift
Protocol-Oriented Programming in Swift
 
React starter-kitでとっとと始めるisomorphic開発
React starter-kitでとっとと始めるisomorphic開発React starter-kitでとっとと始めるisomorphic開発
React starter-kitでとっとと始めるisomorphic開発
 
Firefox OS, une plateforme à découvrir - IO Saglac - 2014-09-09
Firefox OS, une plateforme à découvrir - IO Saglac - 2014-09-09Firefox OS, une plateforme à découvrir - IO Saglac - 2014-09-09
Firefox OS, une plateforme à découvrir - IO Saglac - 2014-09-09
 
OSGi and Spring Data for simple (Web) Application Development
OSGi and Spring Data  for simple (Web) Application DevelopmentOSGi and Spring Data  for simple (Web) Application Development
OSGi and Spring Data for simple (Web) Application Development
 
OSGi and Spring Data for simple (Web) Application Development - Christian Bar...
OSGi and Spring Data for simple (Web) Application Development - Christian Bar...OSGi and Spring Data for simple (Web) Application Development - Christian Bar...
OSGi and Spring Data for simple (Web) Application Development - Christian Bar...
 
OpenWhisk: Event-driven Design
OpenWhisk: Event-driven DesignOpenWhisk: Event-driven Design
OpenWhisk: Event-driven Design
 
OpenWhisk Under the Hood -- London Oct 16 2016
OpenWhisk Under the Hood -- London Oct 16 2016OpenWhisk Under the Hood -- London Oct 16 2016
OpenWhisk Under the Hood -- London Oct 16 2016
 

Recently uploaded

英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作qr0udbr0
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Hr365.us smith
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesPhilip Schwarz
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024StefanoLambiase
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWave PLM
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfAlina Yurenko
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackVICTOR MAESTRE RAMIREZ
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceBrainSell Technologies
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Andreas Granig
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfStefano Stabellini
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaHanief Utama
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odishasmiwainfosol
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 
MYjobs Presentation Django-based project
MYjobs Presentation Django-based projectMYjobs Presentation Django-based project
MYjobs Presentation Django-based projectAnoyGreter
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsAhmed Mohamed
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Velvetech LLC
 

Recently uploaded (20)

英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a series
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need It
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStack
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. Salesforce
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdf
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief Utama
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
2.pdf Ejercicios de programación competitiva
2.pdf Ejercicios de programación competitiva2.pdf Ejercicios de programación competitiva
2.pdf Ejercicios de programación competitiva
 
MYjobs Presentation Django-based project
MYjobs Presentation Django-based projectMYjobs Presentation Django-based project
MYjobs Presentation Django-based project
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML Diagrams
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...
 

RxSwift to Combine