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
2.4k
6
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
iOSDC 2021 Restore
iOSDC 2021 バックグラウンドでアプリがキルされても怖くない!アプリの状態を元に戻すリストア機能の全て
coe
September 19, 2021
More Decks by coe
See All by coe
すべてのヘルスケアデータを紐解く.pdf
coe
0
3.4k
About HealthKit nutrition
coe
0
480
Kotlin Dynamic type
coe
0
410
iOSDC 2023 Web in tvOS and watchOS
coe
1
1.1k
iOSDC 2023 Push To Talk
coe
1
2.7k
iOSDC手で触れずにアプリを動かす技術
coe
1
2.3k
iOSDC令和時代のXML処理を考える
coe
3
3.1k
詳解Storyboard
coe
7
4.1k
あなたの知らない連絡先の世界
coe
15
8.4k
Other Decks in Technology
See All in Technology
2026 TECHFRESH 畢業分享會 - 開發日常大解密!從領域驅動到企業級上線
line_developers_tw
PRO
0
990
Oracle AI Database@AWS:サービス概要のご紹介
oracle4engineer
PRO
4
2.9k
Android の公式 Skill / Android skills
yanzm
0
140
気づかぬうちにセキュリティ負債を生むAPIキー運用
sgwrmctk
0
120
EventBridge Connection
_kensh
5
710
AIエージェントが名古屋の猛暑からあなたを守る
happysamurai294
0
110
FDE という解 ― 暗黙知と明示知をつなぐ、伴走型エンジニアリング ―
otanet
0
150
Snowflakeと仲良くなる第一歩
coco_se
4
470
SONiCで構築・運用する生成AI向けパブリッククラウドネットワーク ~実装編~
sonic
0
200
AIネイティブな開発のサプライチェーンリスク対策 〜激動の開発現場でリスクに立ち向かう〜【ZennFes】
cscengineer
PRO
2
120
20260619 私の日常業務での生成 AI 活用
masaruogura
1
200
LayerX コーポレートエンジニアリング室におけるサプライチェーンセキュリティへの取り組み / Supply Chain Security at LayerX Corporate Engineering
yuyatakeyama
0
110
Featured
See All Featured
The Mindset for Success: Future Career Progression
greggifford
PRO
0
360
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
5
1.1k
Un-Boring Meetings
codingconduct
0
310
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
160
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
720
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
840
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Tips & Tricks on How to Get Your First Job In Tech
honzajavorek
1
540
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
230
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.2k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
Music & Morning Musume
bryan
47
7.2k
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