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
2k
iOSDC 2021 Restore
iOSDC 2021 バックグラウンドでアプリがキルされても怖くない!アプリの状態を元に戻すリストア機能の全て
coe
September 19, 2021
Tweet
Share
More Decks by coe
See All by coe
すべてのヘルスケアデータを紐解く.pdf
coe
0
2.7k
About HealthKit nutrition
coe
0
400
Kotlin Dynamic type
coe
0
350
iOSDC 2023 Web in tvOS and watchOS
coe
1
640
iOSDC 2023 Push To Talk
coe
1
2k
iOSDC手で触れずにアプリを動かす技術
coe
1
1.7k
iOSDC令和時代のXML処理を考える
coe
3
2.5k
詳解Storyboard
coe
7
3.6k
あなたの知らない連絡先の世界
coe
15
8.1k
Other Decks in Technology
See All in Technology
エンジニア採用から始まる技術広報と組織づくり/202506lt
nishiuma
8
1.7k
Autonomous Database サービス・アップデート (FY25)
oracle4engineer
PRO
2
770
CIでのgolangci-lintの実行を約90%削減した話
kazukihayase
0
240
型システムを知りたい人のための型検査器作成入門
mame
15
3.8k
JSX - 歴史を振り返り、⾯⽩がって、エモくなろう
pal4de
2
490
「規約、知識、オペレーション」から考える中規模以上の開発組織のCursorルールの 考え方・育て方 / Cursor Rules for Coding Styles, Domain Knowledges and Operations
yuitosato
6
1.6k
上長や社内ステークホルダーに対する解像度を上げて、より良い補完関係を築く方法 / How-to-increase-resolution-and-build-better-complementary-relationships-with-your-bosses-and-internal-stakeholders
madoxten
13
7.6k
菸酒生在 LINE Taiwan 的後端雙刀流
line_developers_tw
PRO
0
140
Contract One Engineering Unit 紹介資料
sansan33
PRO
0
6.4k
VCpp Link and Library - C++ breaktime 2025 Summer
harukasao
0
160
名刺メーカーDevグループ 紹介資料
sansan33
PRO
0
770
活きてなかったデータを活かしてみた話 / Shirokane Kougyou vol 19
sansan_randd
1
270
Featured
See All Featured
Adopting Sorbet at Scale
ufuk
77
9.4k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
657
60k
RailsConf 2023
tenderlove
30
1.1k
Designing Experiences People Love
moore
142
24k
jQuery: Nuts, Bolts and Bling
dougneiner
63
7.8k
YesSQL, Process and Tooling at Scale
rocio
172
14k
The Pragmatic Product Professional
lauravandoore
35
6.7k
Building Adaptive Systems
keathley
43
2.6k
Embracing the Ebb and Flow
colly
86
4.7k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
29
1.8k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
26k
[RailsConf 2023] Rails as a piece of cake
palkan
55
5.6k
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