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
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
AI時代のAPI品質を支えるガードレール / API Guardrails for API quality in the AI era
yokawasa
1
210
「図書館」という名前のままでいいのか -Code4Lib JAPANカンファレンス2026 アンカンファレンス報告- / Code4Lib JAPAN Conference 2026: Unconference Report
ykiyota
0
150
2026-09-10 【Snowflake World Tour Tokyo 2026】dbt Core と Snowflake で実現する多層的なデータガバナンス / Multi-Layered Data Governance Powered by dbt Core and Snowflake
civitaspo
0
300
日経電子版を支えていく Kasane Design System/fec_fukuoka
nikkei_engineer_recruiting
0
590
コスト最適化の「めんどくさい」を AWS FinOps Agent でチョット楽にする
classmethod_kaz
0
300
AI活用の現在地、 ちゃんと見えてますか?/XPfest-2026
visional_engineering_and_design
0
180
振り返りこそエンジニアの本領
negima
0
360
例外の正しい扱い方 そのエラー try-catchして大丈夫?
jinwatanabe
3
440
Where Is JetBrains AI Heading- — Central CLI, Air Alpha, and the Agentic Development Stack
x5gtrn
PRO
0
150
なぜSRE・セキュリティは評価されないのか?守りの組織を事業成長エンジンに変えた実践
cscengineer
PRO
3
2.4k
Amazon Quick on DesktopがIAM Identity Centerで動かない理由
yukiogawa
0
150
2026-09-04 SRE Tech Talk #15 怠惰なTerraform / Lazy Terraform
masasuzu
0
230
Featured
See All Featured
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.7k
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
470
WENDY [Excerpt]
tessaabrams
13
39k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.8k
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.6k
My Coaching Mixtape
mlcsv
0
310
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.9k
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
230
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
340
Why Your Marketing Sucks and What You Can Do About It - Sophie Logan
marketingsoph
0
400
[RailsConf 2023] Rails as a piece of cake
palkan
59
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