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
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
LINE Digital Frontier - TECH
July 25, 2026
Technology
90
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
510
5000万ダウンロードを超える漫画サービスを支えるログ基盤の設計開発の全て
ldf_tech
0
2.8k
Android端末で実現するオンデバイスLLM 2025
ldf_tech
0
81
How LINE MANGA Uses ClickHouse for Real-Time AnalysisSolving Data Integration Challenges with ClickHouse
ldf_tech
0
470
会社紹介資料
ldf_tech
1
6.1k
SwiftSyntaxでUIKitとSwiftUIの使用率を完璧に計測できちゃう件について
ldf_tech
0
420
Kotlin 2.0が与えるAndroid開発の進化
ldf_tech
0
340
Road to Kotlin 〜10年続くPerl運用からの脱却〜
ldf_tech
0
140
Other Decks in Technology
See All in Technology
GuardDuty 検知対応を DevOps Agent で効率化しようとしている話 / GuardDuty Investigations with DevOps Agent
masahirokawahara
1
350
薬剤師(ドメインエキスパート)と一緒に育てる薬局向けAIアシスタント
kakehashi
PRO
2
140
When Does a Local Qwen Start to Break
morshoto
0
200
深夜のクラウド懺悔室 1:29:300 or 1:0:0
kazzpapa3
1
230
Snowflakeのコスト最適化を支えるアーキテクチャ設計
ktatsuya
0
1.4k
作り直せるコードは迅速に 作り直せないDBは慎重に - AI時代のプロダクトエンジニアが「判断の不可逆性」で開発速度を変える話
kinosuke01
0
300
「重なり」は迎える側がつくる ― 人もAIエージェントも歓迎するプロダクトエンジニアリング ―
go0517go
PRO
0
280
アプリをもっと"iOSアプリっぽく"する小さな工夫 / Small Touches That Make Your App Feel More Like an iOS App
matsuji
1
300
ASTを使って影響範囲を特定する
nealle
0
140
Podは生きているのにGoだけが落ちる:GOGCとGOMEMLIMITで追うInvisible OOM Killの謎
tkc66buzz
1
500
Code4Lib JAPANカンファレンス2026 開会挨拶 / Code4Lib JAPAN Conference 2026: Opening Remarks
ykiyota
0
320
Webとヘルスデータ
yukukotani
1
340
Featured
See All Featured
Building AI with AI
inesmontani
PRO
1
1.2k
SEO for Brand Visibility & Recognition
aleyda
0
4.7k
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
270
Stop Working from a Prison Cell
hatefulcrawdad
274
21k
So, you think you're a good person
axbom
PRO
2
2.1k
It's Worth the Effort
3n
188
29k
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
240
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
123
22k
The agentic SEO stack - context over prompts
schlessera
0
920
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
2
500
The Mindset for Success: Future Career Progression
greggifford
PRO
0
490
The State of eCommerce SEO: How to Win in Today's Products SERPs - #SEOweek
aleyda
2
11k
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