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
iOSDC 2021 Restore
Search
coe
September 19, 2021
Technology
6
2.1k
iOSDC 2021 Restore
iOSDC 2021 バックグラウンドでアプリがキルされても怖くない!アプリの状態を元に戻すリストア機能の全て
coe
September 19, 2021
Tweet
Share
More Decks by coe
See All by coe
すべてのヘルスケアデータを紐解く.pdf
coe
0
3.1k
About HealthKit nutrition
coe
0
430
Kotlin Dynamic type
coe
0
370
iOSDC 2023 Web in tvOS and watchOS
coe
1
790
iOSDC 2023 Push To Talk
coe
1
2.3k
iOSDC手で触れずにアプリを動かす技術
coe
1
2k
iOSDC令和時代のXML処理を考える
coe
3
2.8k
詳解Storyboard
coe
7
3.8k
あなたの知らない連絡先の世界
coe
15
8.2k
Other Decks in Technology
See All in Technology
DDD x Microservice Architecture : Findy Architecture Conf 2025
syobochim
13
4.8k
Pandocでmd→pptx便利すぎワロタwww
meow_noisy
2
950
ABEMAのCM配信を支えるスケーラブルな分散カウンタの実装
hono0130
4
1.1k
信頼性が求められる業務のAIAgentのアーキテクチャ設計の勘所と課題
miyatakoji
0
150
スタートアップの事業成長を支えるアーキテクチャとエンジニアリング
doragt
1
7.8k
Android Studio Otter の最新 Gemini 機能 / Latest Gemini features in Android Studio Otter
yanzm
0
380
Greenは本当にGreenか? - B/GデプロイとAPI自動テストで安心デプロイ
kaz29
1
130
レガシーシステム刷新における TypeSpec スキーマ駆動開発のすゝめ
tsukuha
3
710
How We Built a Secure Sandbox Platform for AI
flatt_security
1
110
不確実性に備える ABEMA の信頼性設計とオブザーバビリティ基盤
nagapad
4
7.7k
技術広報のOKRで生み出す 開発組織への価値 〜 カンファレンス協賛を通して育む学びの文化 〜 / Creating Value for Development Organisations Through Technical Communications OKRs — Nurturing a Culture of Learning Through Conference Sponsorship —
pauli
5
550
組織の“見えない壁”を越えよ!エンタープライズシフトに必須な3つのPMの「在り方」変革 #pmconf2025
masakazu178
1
920
Featured
See All Featured
Into the Great Unknown - MozCon
thekraken
40
2.2k
Speed Design
sergeychernyshev
33
1.3k
Side Projects
sachag
455
43k
A Tale of Four Properties
chriscoyier
162
23k
The Invisible Side of Design
smashingmag
302
51k
Designing for Performance
lara
610
69k
VelocityConf: Rendering Performance Case Studies
addyosmani
333
24k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.5k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.3k
Unsuck your backbone
ammeep
671
58k
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
Transcript
όοΫάϥϯυͰΞϓϦ͕Ωϧ͞Εͯ ා͘ͳ͍ʂ ΞϓϦͷঢ়ଶΛݩʹ͢ϦετΞػೳͷશͯ J04%$5TVZPTIJIZVHB
όοΫάϥϯυͰΞϓϦ͕Ω ϧ͞Εͯා͘ͳ͍
όοΫάϥϯυͰΞϓϦ͕Ωϧ͞Εͯා͘ͳ͍ w J04%$ w ৄղ4UPSZCPBSEͰগ͠ղઆ
.PSF*OGPSNBUJPO IUUQTXXXZPVUVCFDPNXBUDI WV'I6DIC0@TUT ৄղ4UPSZCPBSE
όοΫάϥϯυͰΞϓϦ͕Ω ϧ͞Εͯා͘ͳ͍
"QQ" -BVODI4DSFFO ΞϓϦ"Λىಈ
ΞϓϦ"Λར༻த
ΞϓϦ"Ͱɺ͍͍͘͢͝໘·ͰਐΜͰ͍Δ
ΞϓϦ# ΞϓϦ#͔Β௨
ΞϓϦ#ʹભҠͯ͠ɺ৭ʑΔ "QQ#
ΞϓϦ"ʹΔ "QQ" -BVODI4DSFFO
ΞϓϦ"࠷ॳ͔Βʹͳ͍ͬͯΔ
ΞϓϦར༻ΛΊΔ
ΞϓϦ͕ऴྃͨ࣌͠ͷରࡦ
ͦΕɺ4UPSZCPBSEͰ ରࡦͰ͖·͢Α
3FTUPSBUJPO
3FTUPSBUJPO w 6*"QQMJDBUJPO%FMFHBUFͰঢ়ଶͷอଘͱ෮ݩΛ༗ޮʹ͢Δ w ෮ݩ͍ͨ͠7JFX$POUSPMMFSʹରͯ͠3FTUPSBUJPO*%Λઃఆ͢Δ
6*"QQMJDBUJPO%FMFHBUFͰ ঢ়ଶͷอଘͱ෮ݩΛ༗ޮʹ͢Δ
ঢ়ଶͷอଘͱ෮ݩΛ༗ޮʹ͢Δ w 6*"QQMJDBUJPO%FMFHBUFͷ3FTUPSFܥϝιουΛUSVFʹ͢Δ w BQQMJDBUJPO @TIPVME4BWF4FDVSF"QQMJDBUJPO4UBUF w BQQMJDBUJPO @TIPVME3FTUPSF4FDVSF"QQMJDBUJPO4UBUF
w BQQMJDBUJPO @TIPVME4BWF"QQMJDBUJPO4UBUF w BQQMJDBUJPO @TIPVME3FTUPSF"QQMJDBUJPO4UBUF
extension AppDelegate { func application(_ application: UIApplication, shouldSaveApplicationState coder: NSCoder)
-> Bool { return true } func application(_ application: UIApplication, shouldRestoreApplicationState coder: NSCoder) -> Bool { return true } @available(iOS 13.2, *) func application(_ application: UIApplication, shouldSaveSecureApplicationState coder: NSCoder) -> Bool { return true } @available(iOS 13.2, *) func application(_ application: UIApplication, shouldRestoreSecureApplicationState coder: NSCoder) -> Bool { return true } }
෮ݩ͍ͨ͠7JFX$POUSPMMFSʹରͯ͠ 3FTUPSBUJPO*%Λઃఆ͢Δ
3FTUPSBUJPO*%ͷઃఆ ෮ݩ͍ͨ͠ରͷ7JFX$POUSPMMFSશͯʹϢχʔΫͳ3FTUPSBUJPO*%ΛׂΓͯΔ
ϦετΞͷςετ
ϦετΞͷςετ తͷը໘·ͰભҠ͢Δ ྫͱͯ͠.PEBMભҠΛߦ͏ ʢ/BWJHBUJPOભҠͰՄʣ
ϦετΞͷςετ ҰϗʔϜը໘ʹΔ
ϦετΞͷςετ 9DPEF͔Β3VOΛߦ͏
ϦετΞͷςετ ௨ৗͰ͋Εॳظը໘͔Β࢝· Δͱ͜Ζɺઌఔͷଓ͖ͷϞʔμ ϧը໘͔ΒΞϓϦ͕࢝·Δ
͔͠͠
໊લڧ
ॴଐגࣜձࣾ"NB[JB
w #-&ͰJ04"OESPJEؒͰͦͦ͜͜ େ͖ͳαΠζͷσʔλ௨৴Λ࣮ݱ͢Δ -$"1͋ΔΑ w ͋ͳͨͷΒͳ͍࿈བྷઌͷੈք w ৄղ4UPSZCPBSE
झຯύϯ८Γ
ϦετΞͷςετ ϗʔϜը໘ʹͬͨޙɺ࠶9DPEF3VO
ը໘ભҠอͨΕ͍ͯΔ͕ɺೖ ྗͨ͠σʔλ͕ফ͍͑ͯΔ
/4$PEFS
7JFX$POUSPMMFSͷσʔλ෮ݩͷΈ w ϦετΞͰ෮ݩ͞ΕΔͷ4UPSZCPBSE্ʹઃఆͯ͋͠ΔσʔλͷΈ w ͭ·Γɺ4UPSZCPBSEͷσʔλҎ֎͍࣋ͬͯͳ͍ͷͰɺޙ͔Βೖྗ ͨ͠ςΩετσʔλ͍࣋ͬͯͳ͍ w 4UPSZCPBSEҎ֎ͷσʔλΛ࣋ͨͤΔ͜ͱ͕ඞཁ w ͦͷσʔλͷอ࣋ઌ͕/4$PEFS
$PEFSͷσʔλՃ w 6*7JFX$POUSPMMFSFODPEF3FTUPSBCMF4UBUF w ϗʔϜը໘ʹભҠ͢Δͱ͖ͳͲʹݺΕΔ w $PEFSʹର͠ɺ֤छFODPEF ͰύϥϝʔλΛอଘ͓ͯ͘͠ w 6*7JFX$POUSPMMFSEFDPEF3FTUPSBCMF4UBUF
w ֤छEFDPEF ͰɺFODPEF3FTUPSBCMF4UBUFͰอଘ͓͍ͯͨ͠σʔλΛ औಘ͢Δ
$PEFSͷσʔλՃɺ෮ݩ class HobbyViewController: UIViewController { @IBOutlet weak var textField: UITextField!
override func encodeRestorableState(with coder: NSCoder) { super.encodeRestorableState(with: coder) coder.encode(textField.text, forKey: "textFieldText") } override func decodeRestorableState(with coder: NSCoder) { super.decodeRestorableState(with: coder) textField.text = coder.decodeObject(forKey: "textFieldText") as? String } }
࠶֬ೝ
໊લڧ
ॴଐגࣜձࣾ"NB[JB
w #-&ͰJ04"OESPJEؒͰͦͦ͜͜ େ͖ͳαΠζͷσʔλ௨৴Λ࣮ݱ͢Δ -$"1͋ΔΑ w ͋ͳͨͷΒͳ͍࿈བྷઌͷੈք w ৄղ4UPSZCPBSE
झຯύϯ८Γ
ϦετΞͷςετ ϗʔϜը໘ʹͬͨޙɺ࠶9DPEF3VO
w #-&ͰJ04"OESPJEؒͰͦͦ͜͜ େ͖ͳαΠζͷσʔλ௨৴Λ࣮ݱ͢Δ -$"1͋ΔΑ w ͋ͳͨͷΒͳ͍࿈བྷઌͷੈք w ৄղ4UPSZCPBSE
·ͱΊ w 6*"QQMJDBUJPO%FMFHBUFͰঢ়ଶͷอଘͱ෮ݩΛ༗ޮʹ͢Δ w ෮ݩ͍ͨ͠7JFX$POUSPMMFSʹରͯ͠3FTUPSBUJPO*%Λઃఆ͢Δ w /4$PEFSʹɺඞཁͳσʔλΛอଘ͠ɺ෮ݩͷλΠϛϯάͰ/4$PEFS͔Β σʔλΛऔಘ͢Δ
ࠓ·Ͱͷ͜ͱΕ͍ͯͩ͘͞
4UPSZCPBSEͷϦετΞ w ΞϓϦ͕4DFOF%FMFHBUFʹରԠ͍ͯ͠Δ߹͜ͷϦετΞ͕ಈ͔ͳ͍ w ผͳରԠΛ͢Δඞཁ͕͋Δ
/46TFS"DUJWJUZ
ΞϓϦͷঢ়ଶͷอଘͱ෮ݩ w /46TFS"DUJWJUZΛอଘ͢Δ w ىಈ࣌ɺ/46TFS"DUJWJUZ͔ΒσʔλΛ෮ݩ͢Δ
/46TFS"DUJWJUZͷอଘ
*OGPQMJTUͷฤू w *OGPQMJTUʹ/46TFS"DUJWJUZ5ZQFTΛՃ͢Δ w "SSBZ4USJOH
/46TFS"DUJWJUZͷอଘ w /46TFS"DUJWJUZͷอଘͷλΠϛϯάɺ7JFX$POUSPMMFSͷ WJFX%JE"QQFBSҎ߱ʹߦ͏
func updateUserActivity() { // ݱࡏͷγʔϯͷUserActivityͷऔಘ ͳ͚Ε࡞Δ var currentUserActivity = view.window?.windowScene?.userActivity
if currentUserActivity == nil { currentUserActivity = NSUserActivity(activityType: "com.example.staterestore.mainActivity") } // UserActivityʹσʔλΛ٧ΊΔ currentUserActivity?.title = "λΠτϧ" currentUserActivity?.targetContentIdentifier = "unique id" currentUserActivity?.addUserInfoEntries(from: ["key1": "value1"]) currentUserActivity?.addUserInfoEntries(from: ["key2": 2]) // ݱࡏͷγʔϯʹUserActivityΛ͢ view.window?.windowScene?.userActivity = currentUserActivity }
/46TFS"DUJWJUZͷอଘ w ݱࡏͷγʔϯ͔Β/46TFS"DUJWJUZΛऔΓग़͢ w WJFXXJOEPX XJOEPX4DFOF VTFS"DUJWJUZ͔Βݱࡏͷ/46TFS"DUJWJUZΛऔΓग़͢ w OJMͷ߹ɺ/46TFS"DUJWJUZΛ࡞͢Δ w
BDUJWJUZ5ZQFʹઌఔ*OGPQMJTUͰઃఆͨ͠ͷΛ͏ w /46TFS"DUJWJUZʹର͠ɺ෮ݩʹඞཁͳใΛ٧ΊΔʢޙड़ʣ w ใΛ٧ΊͨޙɺWJFXXJOEPX XJOEPX4DFOF VTFS"DUJWJUZʹ/46TFS"DUJWJUZΛฦ ͢
/46TFS"DUJWJUZʹઃఆ͢Δใ w UJUMF w UBSHFU$POUFOU*EFOUJ fi FS w VTFS*OGP BEE6TFS*OGP&OUSJFT
w ෮ݩʹඞཁͳΛ٧ΊΔ
/46TFS"DUJWJUZΛอଘ͢ΔλΠϛϯά VQEBUF6TFS"DUJWJUZ Λ࣮ߦ͢ΔλΠϛϯάྫ w WJFX%JE"QQFBS w ֤छσʔλ͕มΘͬͨͱ͖ w ฤू͞ΕͨςΩετ w
Ϣʔβʔ͕બதͷΞΠςϜͷมߋ
ϗʔϜը໘ભҠ࣌ʹ/46TFS"DUJWJUZΛอଘ w 4DFOF%FMFHBUFʹTUBUF3FTUPSBUJPO"DUJWJUZ GPS Λ࣮͢Δ w 7JFX$POUSPMMFSͰઃఆ͍ͯͨ͠6TFS"DUJWJUZΛฦ͢ w લड़ͷॲཧΛߦͳ͍ͬͯΕɺTDFOFVTFS"DUJWJUZΛฦ͚ͩ͢Ͱ0,
func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? { scene.userActivity }
ΞϓϦͷ෮ݩ
ΞϓϦͷ෮ݩ w TDFOF @TDFOF6*4DFOF XJMM$POOFDU5PTFTTJPO6*4DFOF4FTTJPO PQUJPOTDPOOFDUJPO0QUJPOT6*4DFOF$POOFDUJPO0QUJPOT w ΞϓϦىಈͷλΠϛϯάͰɺTFTTJPOTUBUF3FTUPSBUJPO"DUJWJUZΛ֬ೝ͢
Δ w TUBUF3FTUPSBUJPO"DUJWJUZͰอଘ͍ͯͨ͠/46TFS"DUJWJUZ͕औಘͰ͖Δ w ը໘ભҠͳͲࣗͰߏங͢Δඞཁ͕͋Δ w 6TFS*OGPͷઃܭ͕ඞཁ
guard let activity = session.stateRestorationActivity else { return } if
activity.activityType == "com.example.staterestore.mainActivity" { let storyboard = UIStoryboard(name: "Main", bundle: .main) if let userInfo = activity.userInfo { // userInfoͷ༰ͰతͷViewControllerΛ෮ݩ͢Δ let detailParentViewController = storyboard.instantiateViewController(withIdentifier: "DetailParentViewController") detailParentViewController.hoge = userInfo[“detailParentViewControllerValue"] // ը໘ભҠΛ෮ݩ͢Δ(ྫͱͯ͠NavigationControllerͷ߹) if let navigationController = window?.rootViewController as? UINavigationController { navigationController.pushViewController(detailParentViewController, animated: false) } } }
4XJGU6*ͰͷϦετΞ
4DFOF4UPSBHF
4DFOF4UPSBHF struct ContentView: View { @State private var isPresented: Bool
= false var body: some View { VStack { Button(action: { isPresented.toggle() }) { Text("Button") } } .sheet(isPresented: $isPresented, content: { Text("present") }) } } 4IFFUද੍ࣔޚΛߦ͏JT1SFTFOUFEΛϦετΞରʹؚΊͯɺ࣍ճىಈ࣌ʹγʔτͷ 1SFTFOUঢ়ଶΛอͭʹʁ
4DFOF4UPSBHF struct ContentView: View { @SceneStorage("ContentView.isPresented") private var isPresented: Bool
= false var body: some View { VStack { Button(action: { isPresented.toggle() }) { Text("Button") } } .sheet(isPresented: $isPresented, content: { Text("present") }) } } !4UBUFΛ!4DFOF4UPSBHFʹม͑Δͱɺ࣍ճىಈ͕࣌อͨΕΔ Ωʔ໊ϢχʔΫʹ͢Δ
4XJGU6* /46TFS"DUJWJUZ
struct ContentView: View { @State private var selectedTitle: Book? =
nil var body: some View { NavigationView { List(bookList) { book in NavigationLink(destination: SwiftUIView(book: $selectedTitle), tag: book, selection: $selectedTitle, label: { Text(book.title) }) } .navigationTitle(Text("Նᕸੴ")) } .onContinueUserActivity("app.hyuga.SwiftUIActivity.restore", perform: { userActivity in selectedTitle = try! userActivity.typedPayload(Book.self) }) } } struct Book: Identifiable, Codable, Hashable { var id: String { title } let title: String let contents: String } τοϓϖʔδͷ7JFXʹPO$POUJOVF6TFS"DUJWJUZΛ࣮ VTFS"DUJWJUZ͔ΒΛΒͬͯ4UBUFΛߋ৽͢ΔΑ͏ʹ͓ͯ͘͜͠ͱͰɺ"DUJWJUZىಈ࣌ʹ/BWJHBUJPO-JOL͕࡞ಈ͢Δ
struct SwiftUIView: View { @Binding var book: Book? var body:
some View { Text(book?.contents ?? "") .userActivity("app.hyuga.SwiftUIActivity.restore", { activity in let returnBook: Book if let activityBook = try? activity.typedPayload(Book.self) { returnBook = activityBook } else { returnBook = book! } activity.title = returnBook.title activity.targetContentIdentifier = returnBook.id activity.isEligibleForSearch = true activity.userInfo = ["title":returnBook.title, "contents": returnBook.contents] }) .navigationTitle(book?.title ?? "") } } VTFS"DUJWJUZΛ࣮ͯ͠ɺBDUJWJUZ͕͋Δ͜ͱΛΒͤΔ JT&MJHJCMF'PS4FBSDIΛUSVFʹͯ͠ɺ4QPUMJHIUݕࡧ͔Β"DUJWJUZ͕ىಈͰ͖ΔΑ͏ʹ͓ͯ͘͠
4XJGU6* /46TFS"DUJWJUZ εϙοτϥΠτݕࡧͰɺຊ Λ։͘͜ͱ͕ՄೳʹͳΔ
ࢀߟϦϯΫ w 3FTUPSJOH:PVS"QQ`T4UBUF w IUUQTEFWFMPQFSBQQMFDPNEPDVNFOUBUJPOVJLJU VJWJFXDPOUSPMMFSSFTUPSJOH@ZPVS@BQQ@T@TUBUF w 3FTUPSJOH:PVS"QQ`T4UBUFXJUI4XJGU6* w IUUQTEFWFMPQFSBQQMFDPNEPDVNFOUBUJPOVJLJU
WJFX@DPOUSPMMFSTSFTUPSJOH@ZPVS@BQQ@T@TUBUF@XJUI@TXJGUVJ