Upgrade to Pro — share decks privately, control downloads, hide ads and more …

iOSDC 2021 Restore

C58d9fc209cfdc0b822f851911110fa6?s=47 coe
September 19, 2021

iOSDC 2021 Restore

iOSDC 2021 バックグラウンドでアプリがキルされても怖くない!アプリの状態を元に戻すリストア機能の全て

C58d9fc209cfdc0b822f851911110fa6?s=128

coe

September 19, 2021
Tweet

Transcript

  1. όοΫάϥ΢ϯυͰΞϓϦ͕Ωϧ͞Εͯ΋ ා͘ͳ͍ʂ ΞϓϦͷঢ়ଶΛݩʹ໭͢ϦετΞػೳͷશͯ J04%$5TVZPTIJIZVHB

  2. όοΫάϥ΢ϯυͰΞϓϦ͕Ω ϧ͞Εͯ΋ා͘ͳ͍

  3. όοΫάϥ΢ϯυͰΞϓϦ͕Ωϧ͞Εͯ΋ා͘ͳ͍ w J04%$ w ৄղ4UPSZCPBSE಺Ͱগ͠ղઆ

  4. .PSF*OGPSNBUJPO IUUQTXXXZPVUVCFDPNXBUDI WV'I6DIC0@TUT ৄղ4UPSZCPBSE 

  5. όοΫάϥ΢ϯυͰΞϓϦ͕Ω ϧ͞Εͯ΋ා͘ͳ͍

  6. "QQ" -BVODI4DSFFO ΞϓϦ"Λىಈ

  7. ΞϓϦ"Λར༻த

  8. ΞϓϦ"Ͱɺ͍͍͘͢͝৔໘·ͰਐΜͰ͍Δ

  9. ΞϓϦ# ΞϓϦ#͔Β௨஌

  10. ΞϓϦ#ʹભҠͯ͠ɺ৭ʑ΍Δ "QQ#

  11. ΞϓϦ"ʹ໭Δ "QQ" -BVODI4DSFFO

  12. ΞϓϦ"͸࠷ॳ͔Βʹͳ͍ͬͯΔ

  13. ΞϓϦར༻Λ΍ΊΔ

  14. ΞϓϦ͕ऴྃͨ࣌͠ͷରࡦ

  15. ͦΕɺ4UPSZCPBSEͰ ରࡦͰ͖·͢Α

  16. 3FTUPSBUJPO

  17. 3FTUPSBUJPO w 6*"QQMJDBUJPO%FMFHBUFͰঢ়ଶͷอଘͱ෮ݩΛ༗ޮʹ͢Δ w ෮ݩ͍ͨ͠7JFX$POUSPMMFSʹରͯ͠3FTUPSBUJPO*%Λઃఆ͢Δ

  18. 6*"QQMJDBUJPO%FMFHBUFͰ ঢ়ଶͷอଘͱ෮ݩΛ༗ޮʹ͢Δ

  19. ঢ়ଶͷอଘͱ෮ݩΛ༗ޮʹ͢Δ w 6*"QQMJDBUJPO%FMFHBUFͷ3FTUPSFܥϝιουΛUSVFʹ͢Δ w BQQMJDBUJPO @TIPVME4BWF4FDVSF"QQMJDBUJPO4UBUF  w BQQMJDBUJPO @TIPVME3FTUPSF4FDVSF"QQMJDBUJPO4UBUF

     w BQQMJDBUJPO @TIPVME4BWF"QQMJDBUJPO4UBUF  w BQQMJDBUJPO @TIPVME3FTUPSF"QQMJDBUJPO4UBUF
  20. 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 } }
  21. ෮ݩ͍ͨ͠7JFX$POUSPMMFSʹରͯ͠ 3FTUPSBUJPO*%Λઃఆ͢Δ

  22. 3FTUPSBUJPO*%ͷઃఆ ෮ݩ͍ͨ͠ର৅ͷ7JFX$POUSPMMFSશͯʹϢχʔΫͳ3FTUPSBUJPO*%ΛׂΓ౰ͯΔ

  23. ϦετΞͷςετ

  24. ϦετΞͷςετ ໨తͷը໘·ͰભҠ͢Δ ྫͱͯ͠.PEBMભҠΛߦ͏ ʢ/BWJHBUJPOભҠͰ΋Մʣ

  25. ϦετΞͷςετ Ұ౓ϗʔϜը໘ʹ໭Δ

  26. ϦετΞͷςετ 9DPEF͔Β3VOΛߦ͏

  27. ϦετΞͷςετ ௨ৗͰ͋Ε͹ॳظը໘͔Β࢝· Δͱ͜Ζɺઌఔͷଓ͖ͷϞʔμ ϧը໘͔ΒΞϓϦ͕࢝·Δ

  28. ͔͠͠

  29. ໊લ೔޲ڧ

  30. ॴଐגࣜձࣾ"NB[JB

  31. w #-&ͰJ04"OESPJEؒͰͦͦ͜͜ େ͖ͳαΠζͷσʔλ௨৴Λ࣮ݱ͢Δ -$"1΋͋ΔΑ  w ͋ͳͨͷ஌Βͳ͍࿈བྷઌͷੈք w ৄղ4UPSZCPBSE

  32. झຯύϯ԰८Γ

  33. ϦετΞͷςετ ϗʔϜը໘ʹ໭ͬͨޙɺ࠶౓9DPEF3VO

  34. ը໘ભҠ͸อͨΕ͍ͯΔ͕ɺೖ ྗͨ͠σʔλ͕ফ͍͑ͯΔ

  35. /4$PEFS

  36. 7JFX$POUSPMMFSͷσʔλ෮ݩͷ࢓૊Έ w ϦετΞͰ෮ݩ͞ΕΔͷ͸4UPSZCPBSE্ʹઃఆͯ͋͠ΔσʔλͷΈ w ͭ·Γɺ4UPSZCPBSEͷσʔλҎ֎͸͍࣋ͬͯͳ͍ͷͰɺޙ͔Βೖྗ ͨ͠ςΩετσʔλ͸͍࣋ͬͯͳ͍ w 4UPSZCPBSEҎ֎ͷσʔλΛ࣋ͨͤΔ͜ͱ͕ඞཁ w ͦͷσʔλͷอ࣋ઌ͕/4$PEFS

  37. $PEFS΁ͷσʔλ௥Ճ w 6*7JFX$POUSPMMFSFODPEF3FTUPSBCMF4UBUF w ϗʔϜը໘ʹભҠ͢Δͱ͖ͳͲʹݺ͹ΕΔ w $PEFSʹର͠ɺ֤छFODPEF ͰύϥϝʔλΛอଘ͓ͯ͘͠ w 6*7JFX$POUSPMMFSEFDPEF3FTUPSBCMF4UBUF

    w ֤छEFDPEF ͰɺFODPEF3FTUPSBCMF4UBUFͰอଘ͓͍ͯͨ͠σʔλΛ औಘ͢Δ
  38. $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 } }
  39. ࠶౓֬ೝ

  40. ໊લ೔޲ڧ

  41. ॴଐגࣜձࣾ"NB[JB

  42. w #-&ͰJ04"OESPJEؒͰͦͦ͜͜ େ͖ͳαΠζͷσʔλ௨৴Λ࣮ݱ͢Δ -$"1΋͋ΔΑ  w ͋ͳͨͷ஌Βͳ͍࿈བྷઌͷੈք w ৄղ4UPSZCPBSE

  43. झຯύϯ԰८Γ

  44. ϦετΞͷςετ ϗʔϜը໘ʹ໭ͬͨޙɺ࠶౓9DPEF3VO

  45. w #-&ͰJ04"OESPJEؒͰͦͦ͜͜ େ͖ͳαΠζͷσʔλ௨৴Λ࣮ݱ͢Δ -$"1΋͋ΔΑ  w ͋ͳͨͷ஌Βͳ͍࿈བྷઌͷੈք w ৄղ4UPSZCPBSE

  46. ·ͱΊ w 6*"QQMJDBUJPO%FMFHBUFͰঢ়ଶͷอଘͱ෮ݩΛ༗ޮʹ͢Δ w ෮ݩ͍ͨ͠7JFX$POUSPMMFSʹରͯ͠3FTUPSBUJPO*%Λઃఆ͢Δ w /4$PEFSʹɺඞཁͳσʔλΛอଘ͠ɺ෮ݩͷλΠϛϯάͰ/4$PEFS͔Β σʔλΛऔಘ͢Δ

  47. ࠓ·Ͱͷ͜ͱ͸๨Ε͍ͯͩ͘͞

  48. 4UPSZCPBSEͷϦετΞ w ΞϓϦ͕4DFOF%FMFHBUFʹରԠ͍ͯ͠Δ৔߹͸͜ͷϦετΞ͕ಈ͔ͳ͍ w ผͳରԠΛ͢Δඞཁ͕͋Δ

  49. /46TFS"DUJWJUZ

  50. ΞϓϦͷঢ়ଶͷอଘͱ෮ݩ w /46TFS"DUJWJUZΛอଘ͢Δ w ىಈ࣌ɺ/46TFS"DUJWJUZ͔ΒσʔλΛ෮ݩ͢Δ

  51. /46TFS"DUJWJUZͷอଘ

  52. *OGPQMJTUͷฤू w *OGPQMJTUʹ/46TFS"DUJWJUZ5ZQFTΛ௥Ճ͢Δ w "SSBZ4USJOH

  53. /46TFS"DUJWJUZͷอଘ w /46TFS"DUJWJUZͷอଘͷλΠϛϯά͸ɺ7JFX$POUSPMMFSͷ WJFX%JE"QQFBSҎ߱ʹߦ͏

  54. 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 }
  55. /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Λฦ ͢
  56. /46TFS"DUJWJUZʹઃఆ͢Δ৘ใ w UJUMF w UBSHFU$POUFOU*EFOUJ fi FS w VTFS*OGP BEE6TFS*OGP&OUSJFT

     w ෮ݩʹඞཁͳ஋Λ٧ΊΔ
  57. /46TFS"DUJWJUZΛอଘ͢ΔλΠϛϯά VQEBUF6TFS"DUJWJUZ Λ࣮ߦ͢ΔλΠϛϯάྫ w WJFX%JE"QQFBS w ֤छσʔλ͕มΘͬͨͱ͖ w ฤू͞ΕͨςΩετ w

    Ϣʔβʔ͕બ୒தͷΞΠςϜͷมߋ
  58. ϗʔϜը໘ભҠ࣌ʹ/46TFS"DUJWJUZΛอଘ w 4DFOF%FMFHBUFʹTUBUF3FTUPSBUJPO"DUJWJUZ GPS Λ࣮૷͢Δ w 7JFX$POUSPMMFSͰઃఆ͍ͯͨ͠6TFS"DUJWJUZΛฦ͢ w લड़ͷॲཧΛߦͳ͍ͬͯΕ͹ɺTDFOFVTFS"DUJWJUZΛฦ͚ͩ͢Ͱ0,

  59. func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? { scene.userActivity }

  60. ΞϓϦͷ෮ݩ

  61. ΞϓϦͷ෮ݩ w TDFOF @TDFOF6*4DFOF XJMM$POOFDU5PTFTTJPO6*4DFOF4FTTJPO  PQUJPOTDPOOFDUJPO0QUJPOT6*4DFOF$POOFDUJPO0QUJPOT  w ΞϓϦىಈͷλΠϛϯάͰɺTFTTJPOTUBUF3FTUPSBUJPO"DUJWJUZΛ֬ೝ͢

    Δ w TUBUF3FTUPSBUJPO"DUJWJUZͰอଘ͍ͯͨ͠/46TFS"DUJWJUZ͕औಘͰ͖Δ w ը໘ભҠͳͲ͸ࣗ෼Ͱߏங͢Δඞཁ͕͋Δ w 6TFS*OGPͷઃܭ͕ඞཁ
  62. 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) } } }
  63. 4XJGU6*ͰͷϦετΞ

  64. 4DFOF4UPSBHF

  65. 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ঢ়ଶΛอͭʹ͸ʁ
  66. 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ʹม͑Δͱɺ࣍ճىಈ࣌΋஋͕อͨΕΔ Ωʔ໊͸ϢχʔΫʹ͢Δ
  67. 4XJGU6* /46TFS"DUJWJUZ

  68. 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͕࡞ಈ͢Δ
  69. 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͕ىಈͰ͖ΔΑ͏ʹ͓ͯ͘͠
  70. 4XJGU6*  /46TFS"DUJWJUZ εϙοτϥΠτݕࡧͰ௚઀ɺຊ Λ։͘͜ͱ͕ՄೳʹͳΔ

  71. ࢀߟϦϯΫ 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