Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
是時候了!將RxSwift 程式碼全部改寫成 Swift Concurrency吧!/slid...
Search
LINE Digital Frontier - TECH
July 25, 2026
Technology
220
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.4k
累計5000万DLサービスの裏側 – LINEマンガのKotlinで挑む大規模 Server-side ETLの最適化
ldf_tech
2
590
5000万ダウンロードを超える漫画サービスを支えるログ基盤の設計開発の全て
ldf_tech
0
2.9k
Android端末で実現するオンデバイスLLM 2025
ldf_tech
0
86
How LINE MANGA Uses ClickHouse for Real-Time AnalysisSolving Data Integration Challenges with ClickHouse
ldf_tech
0
500
会社紹介資料
ldf_tech
1
6.3k
SwiftSyntaxでUIKitとSwiftUIの使用率を完璧に計測できちゃう件について
ldf_tech
0
440
Kotlin 2.0が与えるAndroid開発の進化
ldf_tech
0
350
Road to Kotlin 〜10年続くPerl運用からの脱却〜
ldf_tech
0
150
Other Decks in Technology
See All in Technology
S3 Filesで挑む! 八戸せんべい汁の食べ頃問題
usanchuu
0
140
[JAWS-UG彩の国埼玉#12]そろそろ準備する?re:Invent2026をやさしく解説 / reinvent2026-sainokuni
sh_fk2
1
390
sue445's proposals 2026
sue445
0
130
初海外で注意すべきこと12選!!!安全快適なre:invent生活にするために
yuuuuuuu168
1
200
JAWSUG TOHOKU 青森支部 「LoGoチャット」のAWS移行で 乗り越えたこと
taketakekaho
0
370
オントロジーって 結局なにもの? AI エージェントに「意味」を渡す知識設計の話
greenmidori
0
260
フィンテック養成勉強会#68
finengine
0
200
Active Record Connection Adaptersにおける長年の課題に再び取り組む
yahonda
0
410
React改造してみた
tsukina_7mochi
0
150
Steerable Visual Representations
sansantech
PRO
0
130
野良MCPサーバーを排除せよ!MCPサーバーのガバナンス
yukiogawa
1
150
日本語に強いオープンな大規模言語モデル「LLM-jp」
odashi
5
1.2k
Featured
See All Featured
VelocityConf: Rendering Performance Case Studies
addyosmani
331
25k
The Curious Case for Waylosing
cassininazir
1
570
Java REST API Framework Comparison - PWX 2021
mraible
34
9.8k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
7k
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.8k
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
280
We Are The Robots
honzajavorek
0
400
Building an army of robots
kneath
307
47k
The World Runs on Bad Software
bkeepers
PRO
72
12k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.3k
Visualization
eitanlees
153
17k
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
280
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