Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
是時候了!將RxSwift 程式碼全部改寫成 Swift Concurrency吧!/slid...
Search
LINE Digital Frontier - TECH
July 25, 2026
Technology
36
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
是時候了!將RxSwift 程式碼全部改寫成 Swift Concurrency吧!/slides_iplayground_2026_20min_static
「iPlayground 2026」の登壇資料です。
https://iplayground.io/2026/
LINE Digital Frontier - TECH
July 25, 2026
More Decks by LINE Digital Frontier - TECH
See All by LINE Digital Frontier - TECH
Kotlin言語仕様書へ招待 〜コード「なぜ」を読み解く〜
ldf_tech
0
1.3k
累計5000万DLサービスの裏側 – LINEマンガのKotlinで挑む大規模 Server-side ETLの最適化
ldf_tech
2
470
5000万ダウンロードを超える漫画サービスを支えるログ基盤の設計開発の全て
ldf_tech
0
2.7k
Android端末で実現するオンデバイスLLM 2025
ldf_tech
0
74
How LINE MANGA Uses ClickHouse for Real-Time AnalysisSolving Data Integration Challenges with ClickHouse
ldf_tech
0
450
会社紹介資料
ldf_tech
1
5.8k
SwiftSyntaxでUIKitとSwiftUIの使用率を完璧に計測できちゃう件について
ldf_tech
0
400
Kotlin 2.0が与えるAndroid開発の進化
ldf_tech
0
330
Road to Kotlin 〜10年続くPerl運用からの脱却〜
ldf_tech
0
130
Other Decks in Technology
See All in Technology
AI時代の開発生産性は、個人技からチーム設計へ
moongift
PRO
4
2.5k
AI_Dev_Day_製造業領域でのAI活用から見た活用の罠と成功に導く実践知.pdf
kintotechdev
0
160
Oracle Base Database Service 技術詳細
oracle4engineer
PRO
15
110k
大量データに対しても、生成AIを用いてリーズナブルにデータ加工をしたい!Databricksのai_queryについて調べてみた
kamoshika
1
280
副作用のある Lambda でも Lambda Power Tuning は使えるのか / lambda-power-tuning-side-effects
koukihosaka
1
140
SRENEXT_2026_Chairs__Talks_in_Tamachi.sre.pdf
srenext
1
150
発表と総括 / Presentations and Summary
ks91
PRO
0
190
OpenTelemetryにおけるGoのゼロコード・コンパイル時計装について #fukuokago
quiver
0
210
Aurora MySQL 8.4リリース! Rubyistが備えること / what-rubyist-should-prepare-for-aurora-mysql-8-4
fkmy
0
750
インシデント事例と パッケージの全量解析に学ぶ ソフトウェアサプライチェーンの守り方 / supply-chain-attack-defense
flatt_security
0
890
探索・可視化・自動化を一本化 Amazon Quickでデータ活用スピードを上げる方法
koheiyoshikawa
0
170
仕様駆動開発、導入半年。「本当に速くなってるの?」にデータで答える / AICon2026_hirakawa
rakus_dev
0
320
Featured
See All Featured
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
160
The Curse of the Amulet
leimatthew05
2
13k
The Cost Of JavaScript in 2023
addyosmani
55
10k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
118
120k
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
660
AI Search: Implications for SEO and How to Move Forward - #ShenzhenSEOConference
aleyda
1
1.3k
Navigating Weather and Climate Data
rabernat
0
400
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6k
Navigating Algorithm Shifts & AI Overviews - #SMXNext
aleyda
1
1.5k
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
470
How to Think Like a Performance Engineer
csswizardry
28
2.7k
Transcript
是時候了!將RxSwift 程式碼全部改寫成 Swift Concurrency吧! 2026.07.25 Dex(デスクス) © LINE Digital Frontier
Corporation
) 10 10 LINE Digital Frontier LINE Digital Frontier LINE
LY Corp. LINE LY Corpration UI iOSDC Japan iOSDC Japan VT 🏍💨 人 日 身 5 2 日 0 2 車 ・ 5 2 0 2 面 ・ ・ VTuber 己 自 自 Dex( @dsxsxsxs
ViewModel disposeBag ViewModel disposeBag
ViewModel ViewModel 8 flatMapLatest • •8 ViewModel disposeBag disposeBag ViewModel
BehaviorRelay disposed(by:) BehaviorRelay flatMapLatest Rx • 3 • Rx 2026 • ・ 入 一 一 ⾒ ・ 見 二 見 一 AI 2026 • 日 disposed(by:) AI
2026 Rx 2026 • 2015 Rx • 2015 ⾒ Rx
⾒ • • Combine UI @Observable • async/await • 2026 AsyncSequence Combine @Observable • Rx . async/await AsyncSequence . • Swift Async Algorithms 2025 buffer replay 日 share / buffer 穴 入 ・ 非 一 ・ ・ 一 Swift Async Algorithms 一 ・ ・ 6.10.2 子 非 2 0 1 6 6 2 • Rx share replay • 0 2 Rx
行 而
AI plan mode • plan mode • • Prompt •
codebase 查 RxSwift Swift Concurrency • Combine tasks • • task(.md) PR md AI plan mode feature flag tasks task 1 PR flag 行 3 工 月 人 一 支 一 RxSwift 月 • codebase 3 ・ 9 ・ 1 • 行 • 人 用 工 AI
AI AI • plan mode • plan mode • Prompt
• Wave 查 rule rule • rule 用 rule 一 入 wave 一 •
Farewell-RxSwift
rules rules wave wave actor • wave • rules Combine
rules Rx async Rx state vs event actor / Combine async state / event • rules • rules wave rules 用 十 一 用 手 用 AI 一 一 一 rules • AI 土 •
& Combine Single<T> async throws -> T Future<T?, Error> Observable<T>
AsyncSequence<T> AnyPublisher<T, Error> BehaviorRelay<T> AsyncStream+share(history:1) CurrentValueSubject PublishRelay<T> AsyncStream+share() PassthroughSubject DisposeBag Task Set<AnyCancellable> flatMapLatest flatMapLatest (iOS18+) map { }.switchToLatest() do(onNext:) 無 handleEvents(receiveOutput:) 一 Swift Concurrency 一 RxSwift
UIKit binding & UIKit binding RxSwift Swift Concurrency Combine withLatestFrom
combineLatest+ 取最新(無直接對應) share(replay:1) share(history:1)(polyfill) .rx.tap / .rx.text UIAction / addTarget(.editingChanged) collectionView.rx.didScroll publisher(for: \.contentOffset)(KVO) share() (沒有replay) 族繁不及備載,完整對照見 RxSwift Combine Cheatsheet(CombineCommunity) 與 Swift Async Algorithms Docs → Combine Cheatsheet 子 RxSwift Async Algorithms Docs CombineCommunity Swift
60 • • 60 rules /9 / 9 wave 2
3 4 4 PR task task PR md • • skill 2 3 md • AI rules skill wave • AI Skill 1 skill /f-rxswift E-03 支 一 – 一 支 月 一 月 手 ・ 支 人 一 一 ・ 一 一 一 行 (skill を呼び出す prompt) 行 人 md md
skill / rules 1 wave • skill = rules =
• skill = rules = task / wave • task / wave • lessons lessons skill rules skill / rules 回顧這次 E-03 學到的 lessons & takeaways,適當反映到 f-rxswift 的 skill、rules 或各 task md E-03 で得た lessons と takeaways を振り返り、必要に応じて f-rxswift の skill / rules、または各 task md に反映してください。 guideline Review Bot PR 人 AI reviewer CI 一 一 一 走 review guideline CI 生 人 人 用 一 手 • F-RxSwift Review Guideline 示 F-RxSwift PR • 人 面 skill / rules guideline
API 一 一 API
repository.fetch(id: "42") .subscribe(onSuccess: { render($0) }, onFailure: { show($0) })
.disposed(by: disposeBag) task = Task { do { let result = try await repository.fetch(id: "42") Task.checkCancellation() render(result) } catch { CancellationError {} catch { show(error) } } deinit { task?.cancel() }
async async Single Single func fetch(id: String) -> Single<Profile> {
Single.create { observer in let task = Task { do { observer(.success(try await repository.fetch(id: id))) } catch { observer(.failure(error)) } } return Disposables.create { task.cancel() } } } • 讓尚未重構的 Rx 下游仍可沿用 Single ,底層其實已是 async 未移行の Rx 利用側はそのまま Single を使えるが、内部はすでに async • 關鍵: Disposables.create { task.cancel() } 將 Rx 的 dispose 接回 Task 取消 要点:Disposables.create で task.cancel() を呼び、Rx の dispose を Task キャンセルへつなぐ
方 而 & &
Rx 話 Rx Before:Rx 滲透內層 After:Rx 僅存最外圈 UI UI ・Rx
UI ・Rx Domain ・Rx Domain ✓ RxSwift package Infra ・Rx • Infra • Infra Infra ✓ Infrastructure Infrastructure RxSwift package Data Layer Data Layer • Clean Domain / Infra 見 Rx 日 • Clean Architecture Rx
Observable Observable • Repository • Repository AsyncStream AsyncStream UseCase UseCase
VM VM View View AsyncStream AsyncStream •Observable → AsyncSequence(Domain)/ AnyPublisher(UI); .accept() → continuation.yield() • Observable AsyncShareSequence / AnyPublisher • AsyncSequence • AsyncSequence • continuation.yield() multicast / replay multicast / replay multicast • multicast .accept() API flatMap cold API share() share() Rx 的 hot stream 無法 1:1 對應 AsyncSequence ── 需以 share,或自行補上 replay。 支 一 生 行 用 方 方 • Rx の hot stream は、AsyncSequence へ 1:1 では移せない。share を使うか、replay を補う必要がある。
UseCase UseCase 生 final class PageListUseCase { private let trigger
= PublishRelay<Void>() let state: Observable<Page> init(repo: Repo) { state = trigger .flatMapLatest { repo.fetch().asObservable() } .share(replay: 1, scope: .forever) } func reload() { trigger.accept(()) } }
UseCase UseCase final class PageListUseCase: PageListUseCase { private let (reloads,
reloadCont) = AsyncStream<Void>.makeStream() let state: AsyncShareSequence<Result<Page, DemoError>> init(repo: Repo) { state = reloads .flatMapLatest { resultStream { try await repo.fetch() } } .share(history: 1) } func reload() { reloadCont.yield(()) } 生 }
UseCase UseCase final class PageListUseCase { private var task: Task<Void,
Never>? func reload() { task?.cancel() task = Task { do { continuation.yield(.success(try await repo.fetch())) } catch is CancellationError { // The request was replaced by a newer reload. } catch { continuation.yield(.failure(.failed(String(describing: error)))) } } } deinit { task?.cancel() } 生 }
ViewModel ViewModel 面 力 面 final class ItemListViewModel { private
let bag = DisposeBag() let items: Driver<[Row]> init(useCase: PageListUseCase) { items = useCase.state .compactMap { $0.value?.rows } .asDriver(onErrorDriveWith: .empty()) } }
ViewState ViewState @MainActor final class PageListViewState: ObservableObject { @Published private(set)
var items: [Row] = [] private var task: Task<Void, Never>? init(useCase: PageListUseCase) { task = Task { @MainActor [weak self, useCase] in for await result in useCase.state { guard let self else { return } items = result.page?.rows ?? items } @Observable } @Observable } deinit { task?.cancel() } 面 力 面 }
polyfill polyfill // iOS 18+:官方多訂閱 —— 但沒有 replay import AsyncAlgorithms
let shared = stream.share() // multicast、無回放 // 任一 iOS,且需回放最新值(狀態流):本專案 polyfill let state = stream.share(history: 1) // multicast + 回放最新 = Rx share(replay:1) replay 自 行 自 replay 自 share 穴 8 8 1 iOS share 自 iOS 1 一 replay replay
polyfill / polyfill / AsyncShareSequence AsyncShareSequence + replay + @available(iOS,
deprecated: 18.0) public final class AsyncShareSequence<Element: Sendable>: AsyncSequence, Sendable { private func subscribe() -> AsyncStream<Element> { ... } // 同步註冊 + replay init(base: AsyncStream<Element>, history: Int = 0) { ... } public func makeAsyncIterator() -> Iterator { Iterator(base: subscribe().makeAsyncIterator()) } } extension AsyncSequence where Self: Sendable, Element: Sendable { public func share(history: Int = 0) -> AsyncShareSequence<Element> { AsyncShareSequence(base: self, history: history) } } 使用端只看見 share(history:);狀態流固定用 history: 1。 一 一 4 1 4 1 利用側に見えるのは share(history:) だけ。状態ストリームには history: 1 を使う。
polyfill / polyfill AsyncShareSequence AsyncShareSequence private struct State { var
subscribers: [Int: AsyncStream<Element>.Continuation] = [:] var history: [Element] = [] let historyCount: Int mutating func push(_ value: Element) { history.append(value) lock 內只更新 history、取得 history = Array(history.suffix(historyCount)) 訂閱者快照; } 不在 lock 內呼叫下游。 } lock 内では history の更新と購読者の private let state: OSAllocatedUnfairLock<State> private let iterationTask: Task<Void, Never> 二 二 4 2 public func subscribe() -> AsyncStream<Element> {} snapshot 取得だけを行い、 下流は呼び出さない。
polyfill / polyfill / 一 片 → 4 一 3
4 3 iterationTask = Task { [state] in broadcast 前檢查 cancellation; defer { Self.finishSubscribers(in: state) } 所有離開路徑都由 defer 清理。 do { 配信前に cancellation を確認し、どの終了経路で for try await value in base { も defer で後片付けする。 try Task.checkCancellation() state.withLock { $0.push(value) $0.subscribers.forEach { $0.yield(value) } } } } catch is CancellationError where Task.isCancelled { // defer cleanup } catch { // unexpected throw normal finish } }
polyfill / polyfill / replay replay 4 4 4 4
private func subscribe() -> AsyncStream<Element> { let (stream, cont) = AsyncStream<Element>.makeStream() let id = UUID() state.withLock { $0.history.forEach { cont.yield($0) } $0.subscribers[id] = cont } cont.onTermination = { [state] _ in state.withLock { $0.subscribers[id] = nil } } return stream replay 與註冊放在同一個 critical section,避免兩者之間漏值 } replay と購読者登録を同じ critical section で行い、その間の取りこぼしを防ぐ
方 方
Single<Response> Single<Response>; 3 ×150 APIClient • A. client async client
• B. async client • B. async async • C. async • C. async client core core Single 用 Single 生 立 行 • A. 行 RxSwift shim 而 •
150 150 caller extension APIRequest { var asAsync: T {
get async throws { try await execute() } } func asSingle() -> Single<T> { Single.create { observer in let task = Task { do { observer(.success(try await self.asAsync)) } catch { observer(.failure(error)) } } return Disposables.create { task.cancel() } } } 一 }
150 caller 150 • APIRequest.asAsync • APIRequest.asAsync API .asSingle 150
Single.create .asAsync Rx 150 shim 150 一 生 一 行 一 行 150 • Single.create shim shim Rx • 人 .asSingle .asSingle 150 caller • .asSingle caller 143 .asAsync await .asAsync • 143 • 150 一 • API
3 • 1. async async • • 2. API /
API Domain UseCase Domain UseCase Combine • Combine UI binding / UI binding replay replay SwiftUI SwiftUI • 3. • 第三條的真意:能省一事就省一事,不把時間浪費在短命的重構上。 ・ 面 面 ・ 一 ・ 支 ・ 一 ・ 止 走 3 つ目の趣旨は、やらずに済むことはやらないこと。すぐ捨てられるリファクタリングに時間を使わない。
言 一
End Of doc. Demo