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.5k
About HealthKit nutrition
coe
0
490
Kotlin Dynamic type
coe
0
420
iOSDC 2023 Web in tvOS and watchOS
coe
1
1.1k
iOSDC 2023 Push To Talk
coe
1
2.8k
iOSDC手で触れずにアプリを動かす技術
coe
1
2.4k
iOSDC令和時代のXML処理を考える
coe
3
3.3k
詳解Storyboard
coe
7
4.2k
あなたの知らない連絡先の世界
coe
15
8.4k
Other Decks in Technology
See All in Technology
医療の現場を変革に挑戦した半年間の軌跡 - PythonとAIで現場を変える / From Code to Care
soudai
PRO
1
620
Kiro WebとCloud Sessions
nagisa53
2
200
EMの役割で 変わったこと・変わらなかったこと
sansantech
PRO
0
110
マイナンバーカード本人確認の実装比較(OAuth/OIDC Numa (Immersion) Workshop 2026) / 20260825 numa-12
oidfj
PRO
0
250
顧客の要望は2次情報である 〜アンテナを張るFDEの構造論〜
noriakioji
2
460
サイボウズ 開発本部採用ピッチ / Cybozu Engineer Recruit
cybozuinsideout
PRO
12
85k
All About Sansan – for New Global Engineers
sansan33
PRO
1
1.5k
ハッカソンで入賞した話 @ Findy LT
asari194617
0
1.5k
国家プロジェクトを支える「さくらONE」 大規模LLM開発におけるGPU障害を乗り越えるクラスター運用戦略
gpuunite_official
0
280
Windows の互換機能 - 古いプログラムはなぜ動作できるのか
murachiakira
PRO
0
100
AIに持続⼒を与える 判断の⻑期記憶設計
eiei114
1
710
AI時代のアウトプット――変わったこと、変わらないこと / Devsumi 2026 Kansai #devsumi
jnchito
0
580
Featured
See All Featured
Into the Great Unknown - MozCon
thekraken
41
2.7k
So, you think you're a good person
axbom
PRO
2
2.1k
Why Our Code Smells
bkeepers
PRO
340
58k
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
220
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.3k
Impact Scores and Hybrid Strategies: The future of link building
tamaranovitovic
0
420
Done Done
chrislema
186
16k
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
2
650
Are puppies a ranking factor?
jonoalderson
2
3.8k
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
210
jQuery: Nuts, Bolts and Bling
dougneiner
66
8.6k
The Mindset for Success: Future Career Progression
greggifford
PRO
0
480
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