Slide 1

Slide 1 text

ංେԽ͕ͪ͠ͳ Daiki Matsudate (@d_date) ΞϓϦͷىಈܦ࿏Λ੔ཧ͢Δ iOSDC Japan 2018

Slide 2

Slide 2 text

Daiki Matsudate @d_date FOLIO Co., Ltd.

Slide 3

Slide 3 text

ຊ೔ͷςʔϚ ΞϓϦΛىಈͯ͠΋Β͏

Slide 4

Slide 4 text

ຊ೔ͷςʔϚ ΞϓϦΛىಈͯ͠΋Β͏

Slide 5

Slide 5 text

ຊ೔ͷςʔϚ ͨ͘͞Μͷىಈܦ࿏ΛͲ͏ࡹ͔͘

Slide 6

Slide 6 text

໔੹ࣄ߲

Slide 7

Slide 7 text

ࠇຐज़΋৽͍͠ઃܭ֓೦΋ ొ৔͠·ͤΜ

Slide 8

Slide 8 text

Q.ΞϓϦͷىಈܦ࿏ ͍ͭ͘ݴ͑·͔͢ʁ Ϣʔβʔ͔Βݟͯ۠ผͰ͖Δ΋ͷΛΧ΢ϯτ

Slide 9

Slide 9 text

A. 6Ҏ্͸͋Δ

Slide 10

Slide 10 text

1. ௨ৗىಈ

Slide 11

Slide 11 text

ಛʹͳ͠ AppDelegateͷdidFinishLaunchingΛ௨ΔΑ

Slide 12

Slide 12 text

2. ϓογϡ௨஌

Slide 13

Slide 13 text

ϓογϡ௨஌ • Local Push Notification • Remote Push Notification

Slide 14

Slide 14 text

ϓογϡ௨஌ • Local Push Notification • Remote Push Notification ~ iOS 9: AppDelegate iOS 10 ~: UserNotifiaction.framework

Slide 15

Slide 15 text

3. Universal Links

Slide 16

Slide 16 text

Universal Links (Deep Link) • ΞϓϦΠϯετʔϧࡁΈͷ৔߹ • Web → App • User Activityѻ͍(URL SchemeʹΑΔભҠͱಉ͡ʣ • activityType͸NSUserActivityTypeBrowsingWeb

Slide 17

Slide 17 text

Universal Links (Deep Link) • ΞϓϦະΠϯετʔϧͷ৔߹ • Web → App Store → App • didFinishLaunching͕ݺ͹ΕΔʢ௨ৗىಈʣ • URLΛ։͍ͯ೚ҙͷϖʔδ΁ʢopenURL)

Slide 18

Slide 18 text

Universal Links (Deep Link) • ΞϓϦΠϯετʔϧࡁΈͷ৔߹ • ΞϓϦະΠϯετʔϧͷ৔߹ Ϣʔβʔ͔ΒΈΔͱ1ͭͳͷʹɺ࣮૷͸2ύλʔϯ

Slide 19

Slide 19 text

4. Spotlight

Slide 20

Slide 20 text

Spotlight • ݕࡧ͔Βͷىಈ • ݕࡧ݁Ռ (CSSearchableItemActionType) • ΞϓϦ಺ݕࡧʢiOS 10 ~ʣ (CSQueryContinuationActionType) • ͲͪΒ΋User Activityѻ͍ʹͳΔ

Slide 21

Slide 21 text

5. 3D Touch

Slide 22

Slide 22 text

3D Touch ( Home Screen Quick Action ) • AppDelegateͷperformShortcutItem • ఆٛͨ͠ϝχϡʔͷ਺͚ͩભҠઌ͕͋Δ

Slide 23

Slide 23 text

6.Widget

Slide 24

Slide 24 text

Widget ( Today Extension) • Widget͔ΒͷΞϓϦຊମͷىಈ • Widget͔ΒopenAppURLͰURL SchemeΛݺΜͰىಈ͢Δ • ΞϓϦଆͰ͸openURL͕ݺ͹ΕΔ

Slide 25

Slide 25 text

ΞϓϦͷىಈܦ࿏ • ௨ৗىಈ • ϓογϡ௨஌ʢLocal / Remoteʣ • σΟʔϓϦϯΫ • Core Spotlight (Result Action / Query ) • Home Screen Quick Action • Widget

Slide 26

Slide 26 text

7. Siri Shortcut

Slide 27

Slide 27 text

NDA

Slide 28

Slide 28 text

Siri Shortcutͷىಈ൑ఆ • User Activityѻ͍ • actionType͸ઃఆ࣌ʹఆٛ • “com.d-date.hogehoge.app” Έ͍ͨͳ΍ͭ(ఆ਺ͳ͠)

Slide 29

Slide 29 text

ΞϓϦͷىಈܦ࿏ • ௨ৗىಈ • ϓογϡ௨஌ʢLocal / Remoteʣ • σΟʔϓϦϯΫ • Core Spotlight (Result Action / Query ) • Home Screen Quick Action • Siri Shortcut • etc…

Slide 30

Slide 30 text

ΞϓϦͷىಈܦ࿏ App Delegate ௨ৗىಈ ϓογϡ௨஌ʢ ~ iOS 9 ) σΟʔϓϦϯΫ Core Spotlight Home Screen Quick Action Siri Shortcut (Intents) User Notification ϓογϡ௨஌ʢiOS 10 ~) etc…

Slide 31

Slide 31 text

ىಈܦ࿏͸͜ͷ̐ͭͷϝιου ( AppDelegate ) • application:didFinishLaunching • application:openURL • application:continueUserActivity • application:performAction

Slide 32

Slide 32 text

Կ΋ߟ͑ͣʹAppDelegate ʹભҠॲཧΛॻ͍ͯΈΔ

Slide 33

Slide 33 text

App Delegate import UIKit import UserNotifications import CoreSpotlight @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? var tabBarController: UITabBarController? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { self.window = UIWindow(frame: UIScreen.main.bounds) self.tabBarController = UITabBarController() tabBarController.viewControllers = [FirstViewController(), SecondViewController(), ThirdViewController()] window?.rootViewController = tabBarController return true } func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { switch userActivity.activityType { case NSUserActivityTypeBrowsingWeb: guard let url = userActivity.webpageURL else { fatalError("unreachable") } let detailViewController = DetailViewController() detailViewController.identifier = url.lastPathComponent (tabBarController?.viewControllers?[0] as? UINavigationController)?.pushViewController(detailViewController, animated: true) case CSSearchableItemActionType: guard let identifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String else { fatalError("unreachable")} let detailViewController = DetailViewController() detailViewController.identifier = identifier (tabBarController?.viewControllers?[0] as? UINavigationController)?.pushViewController(detailViewController, animated: true) case CSQueryContinuationActionType: guard let query = userActivity.userInfo?[CSSearchQueryString] as? String else { fatalError("unreachable") } let detailViewController = DetailViewController() detailViewController.identifier = query (tabBarController?.viewControllers?[0] as? UINavigationController)?.pushViewController(detailViewController, animated: true) case "com.d-date.CoordinatorExample.intent": // Siri shortcut let identifier = userActivity.userInfo?["signiture"] as? String let detailViewController = DetailViewController() detailViewController.identifier = identifier (tabBarController?.viewControllers?[0] as? UINavigationController)?.pushViewController(detailViewController, animated: true) default: break } return true } func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { if url.scheme == "coordinator-example-widget" { let identifier = url.lastPathComponent let detailViewController = DetailViewController() detailViewController.identifier = identifier (tabBarController?.viewControllers?[0] as? UINavigationController)?.pushViewController(detailViewController, animated: true) } else if url.scheme == "adjustSchemeExample" { let detailViewController = DetailViewController() detailViewController.identifier = identifier (tabBarController?.viewControllers?[0] as? UINavigationController)?.pushViewController(detailViewController, animated: true) } else if url.scheme == "FirebaseDynamicLinksExmaple" { let detailViewController = DetailViewController() detailViewController.identifier = identifier (tabBarController?.viewControllers?[0] as? UINavigationController)?.pushViewController(detailViewController, animated: true) } } func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { let text = shortcutItem.type.replacingOccurrences(of: bundleIdentifier + ".", with: "").lowercased() switch text { case "a": let detailViewController = DetailViewController() detailViewController.identifier = identifier (tabBarController?.viewControllers?[0] as? UINavigationController)?.pushViewController(detailViewController, animated: true) case "b": let detailViewController = DetailViewController() detailViewController.identifier = identifier (tabBarController?.viewControllers?[0] as? UINavigationController)?.pushViewController(detailViewController, animated: true) case "c": let detailViewController = DetailViewController() detailViewController.identifier = identifier (tabBarController?.viewControllers?[0] as? UINavigationController)?.pushViewController(detailViewController, animated: true) default: break } } } extension AppDelegate: UNUserNotificationCenterDelegate { func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { let detailViewController = DetailViewController() detailViewController.identifier = identifier (tabBarController?.viewControllers?[0] as? UINavigationController)?.pushViewController(detailViewController, animated: true) completionHandler() } } • ※ιʔείʔυ͸ΠϝʔδͰ͢

Slide 34

Slide 34 text

έʔεελσΟ • ىಈॲཧ͕ͻͱͭ૿͑ͨͷͰɺ࣮૷͠·ͨ͠ɻɹɹɹɹɹ खಈͰಈ࡞֬ೝͯ͠΋໰୊͋Γ·ͤΜͰͨ͠ • ςετʁॻ͍ͯ·ͤΜ͜͜͠͸ॻ͚ͳ͍Ͱ͢Ͷ • ະϩάΠϯ࣌ʹUniversal Link͔Β։͍ͨ࣌͸ɺͬͪ͜ ͷը໘ʹඈ͹͞ͳ͍ͱɻAppDelegateʹॲཧೖΕ͓ͯ ͖·͢Ͷ

Slide 35

Slide 35 text

͜͏ͯ͠Fat App Delegate͕ ஀ੜͨ͠ͷͩͬͨ

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

Fat App Delegateͷ໰୊ ίʔυྔ͕ଟ͍

Slide 38

Slide 38 text

Fat App Delegateͷ໰୊ ίʔυྔ͕ଟ͍

Slide 39

Slide 39 text

Fat App Delegateͷ໰୊ खಈͰςετ͢Δඞཁ͕͋Δ σάϨ͕ݕ஌Ͱ͖ͳ͍

Slide 40

Slide 40 text

Q.ίʔυྔ͕ଟ͍͜ͱ͸໰୊ͳͷ͔ʁ A.Ωνϯͱ੹຿ʹ෼ׂ͞Ε͍ͯΕ͹໰୊ͳ͍

Slide 41

Slide 41 text

Q. खಈͰςετ͢Ε͹ͦΕͰ͍͍͡Όͳ͍Ͱ͔͢ A. ࠷ऴతʹ͸͢Δ͕ɺ͠ͳ͍͍ͯ͘ͱ͜Ζ͸ ͨ͘͠ͳ͍

Slide 42

Slide 42 text

Q. ࣗಈςετΛ͢ΔΜͰ͔͢ʁ A. ίετ͕ߴ͍ͷͰɺͰ͖Ε͹ͨ͋͘͠Γ· ͤΜ

Slide 43

Slide 43 text

Q. σάϨ͸ݕ஌Ͱ͖·͔͢ʁ A. ϢχοτςετͰ໢ཏͰ͖͍ͯΕ͹ՄೳͰ͢ɻ

Slide 44

Slide 44 text

ςετΛॻ͔ͳ͍ཧ༝͕ͳ͘ͳͬͨ

Slide 45

Slide 45 text

ςετͰ͖Δઃܭ΁

Slide 46

Slide 46 text

AppDelegateʹ͓͚Δىಈॲཧʹؔ͢Δ੹຿ ભҠઌͷܾఆ τϥοΩϯά ىಈΠϕϯτͷड͚औΓ ભҠ

Slide 47

Slide 47 text

AppDelegateʹ͓͚Δىಈॲཧʹؔ͢Δ੹຿ ભҠઌͷܾఆ τϥοΩϯά ىಈΠϕϯτͷड͚औΓ ભҠ ͜͜͸ςετՄೳ

Slide 48

Slide 48 text

AppDelegateʹ͓͚Δىಈॲཧʹؔ͢Δ੹຿ ભҠઌͷܾఆ τϥοΩϯά ىಈΠϕϯτͷड͚औΓ ભҠ ͜ͷ2ͭ͸෼཭͍ͨ͠

Slide 49

Slide 49 text

ೖྗͱग़ྗͲ͏͢Δʁ

Slide 50

Slide 50 text

ભҠઌͷܾఆ ೖྗ = ىಈ ग़ྗ = ભҠ

Slide 51

Slide 51 text

ભҠઌͷܾఆ ೖྗ = ىಈ ग़ྗ = ભҠ

Slide 52

Slide 52 text

ભҠઌͷܾఆ ೖྗ = ىಈ ग़ྗ = ભҠ ىಈ͕Θ͔ΔԿ͔ ભҠઌ͕Θ͔ΔԿ͔

Slide 53

Slide 53 text

τϥοΩϯά ೖྗ = ىಈ ग़ྗ = ૹ৴ ىಈ͕Θ͔ΔԿ͔ Πϕϯτ͕Θ͔ΔԿ͔

Slide 54

Slide 54 text

Ͳ͏΍ͬͯޮ཰Α͘ςετ͢Δʁ • ܕʹམͱ͠ࠐΉ / EnumͰύλʔϯ໢ཏ • ੹຿Λ෼͚Δ • ೖྗ x ग़ྗͷ૊Έ߹ΘͤΛݶఆ͢Δ ͜ΕΒ͸͢΂ͯڞ௨ͨ͠ಉ͡࿩

Slide 55

Slide 55 text

ೖग़ྗͷ૊Έ߹ΘͤΛ੍ ݶ͢Δ

Slide 56

Slide 56 text

ىಈܦ࿏ΛEnumͰఆٛ import UIKit import CoreSpotlight import UserNotifications enum LaunchType { case normal case notification(_ notification: UNNotificationRequest) case userActivity(_ userActivity: NSUserActivity) case openURL(_ url: URL) case shortcutItem(_ shortcutItem: UIApplicationShortcutItem) } ܕͰྲྀೖݩΛ͢΂ͯ໢ཏ

Slide 57

Slide 57 text

τϥοΩϯάΠϕϯτΛEnumͰఆٛ struct LaunchTracker { enum Event: Equatable { case normal case localNotification(identifier: String) case remoteNotification(identifier: String) case deepLink(url: URL) case intent(word: String) case spotlight(resultIdentifier: String) case spotlight(query: String) case widget(identifier: String) case homeScreen(type: String) }

Slide 58

Slide 58 text

τϥοΩϯάΠϕϯτΛEnumͰఆٛ struct LaunchTracker { enum Event: Equatable { case normal case localNotification(identifier: String) case remoteNotification(identifier: String) case deepLink(url: URL) case intent(word: String) case spotlight(resultIdentifier: String) case spotlight(query: String) case widget(identifier: String) case homeScreen(type: String) } ςετ͢Δ͜ͱ͕લఏͷ࣮૷

Slide 59

Slide 59 text

IN OUT Normal Notification UserActivity shortcutItem URL normal localNotification remoteNotification) deepLink intent spotlight(result: String) spotlight(query: String) widget homeScreen

Slide 60

Slide 60 text

launchType -> Event @discardableResult static func track(launchType: LaunchType) -> Event? { switch launchType { case .normal: return send(event: .normal) case .notification(let request): if request.trigger is UNPushNotificationTrigger { let event: Event = .remoteNotification(identifier: request.identifier) return send(event: event) } else if request.trigger is UNTimeIntervalNotificationTrigger { let event: Event = send(event: .localNotification(identifier: request.identifier)) return event }

Slide 61

Slide 61 text

launchType -> Event case .userActivity(let activity): switch activity.activityType { case NSUserActivityTypeBrowsingWeb: guard let url = activity.webpageURL else { fatalError("unreachable") } return send(event: .deepLink(url: url)) case CSSearchableItemActionType: guard let identifier = activity.userInfo?[CSSearchableItemActivityIdentifier] as? String else { fatalError("unreachable") } return send(event: .spotlight(resultIdentifier: identifier)) case CSQueryContinuationActionType: guard let query = activity.userInfo?[CSSearchQueryString] as? String else { fatalError("unreachable") } return send(event: .spotlight(query: query)) case "com.d-date.CoordinatorExample.intent": // Siri shortcut return send(event: .intent(word: "word")) }

Slide 62

Slide 62 text

launchType -> Event case .openURL(let url): if url.scheme == "coordinator-example-widget" { let identifier = url.lastPathComponent return send(event: .widget(identifier: identifier)) } else if url.scheme == "adjustSchemeExample" { //TODO: replace your adjust url scheme return send(event: .deepLink(url: url)) } else if url.scheme == "FirebaseDynamicLinksExmaple" { //TODO: handle your FDL return send(event: .deepLink(url: url)) } return nil // untracked any other urls case .shortcutItem(let item): return send(event: .homeScreen(type: item.type)) } return nil }

Slide 63

Slide 63 text

ૹ৴ϝιουΛఆٛ @discardableResult private static func send(event: Event) -> Event { // TODO: send event to your analytics return event }

Slide 64

Slide 64 text

τϥοΩϯάͷॲཧΛ෼཭Ͱ͖ͨ func start() { LaunchTracker.track(launchType: launchType) window.makeKeyAndVisible() }

Slide 65

Slide 65 text

͜͜·ͰͰͰ͖ͨ͜ͱ • ىಈܦ࿏͔ΒɺςετՄೳͳ΋ͷΛ੾Γग़ͨ͠ • ςετՄೳͳ΋ͷ͔Β੹຿ΛτϥοΩϯάͱભҠઌͷ ܾఆʹΘ͚ͨ • ىಈܦ࿏ͱτϥοΩϯάͷ૊Έ߹ΘͤΛ཈͑ͨ

Slide 66

Slide 66 text

ςετ͸؆୯

Slide 67

Slide 67 text

IN OUT Normal Notification UserActivity shortcutItem URL normal localNotification remoteNotification) deepLink intent spotlight(result: String) spotlight(query: String) widget homeScreen

Slide 68

Slide 68 text

IN OUT Normal Notification UserActivity shortcutItem URL normal localNotification remoteNotification) deepLink intent spotlight(result: String) spotlight(query: String) widget homeScreen ͜͜Λςετ͢Δ

Slide 69

Slide 69 text

ςετͯ͠ΈΑ͏ func testLaunchLocalPushNotification() { let identifier = "testLocalNotification" let request = UNNotificationRequest( identifier: identifier, content: .init(), trigger: UNTimeIntervalNotificationTrigger.init(timeInterval: 30, repeats: false) ) guard let event = LaunchTracker.track(launchType: .notification(request)) else { XCTFail() return } XCTAssertEqual(event, LaunchTracker.Event.localNotification(identifier: identifier)) }

Slide 70

Slide 70 text

ςετͯ͠ΈΑ͏ func testLaunchLocalPushNotification() { let identifier = "testLocalNotification" let request = UNNotificationRequest( identifier: identifier, content: .init(), trigger: UNTimeIntervalNotificationTrigger.init(timeInterval: 30, repeats: false) ) guard let event = LaunchTracker.track(launchType: .notification(request)) else { XCTFail() return } XCTAssertEqual(event, LaunchTracker.Event.localNotification(identifier: identifier)) } μϛʔΛ࡞Δ

Slide 71

Slide 71 text

ςετͯ͠ΈΑ͏ func testLaunchLocalPushNotification() { let identifier = "testLocalNotification" let request = UNNotificationRequest( identifier: identifier, content: .init(), trigger: UNTimeIntervalNotificationTrigger.init(timeInterval: 30, repeats: false) ) guard let event = LaunchTracker.track(launchType: .notification(request)) else { XCTFail() return } XCTAssertEqual(event, LaunchTracker.Event.localNotification(identifier: identifier)) } Notification͔Βىಈͨ͠ͱ͖ͷEventΛऔಘ

Slide 72

Slide 72 text

ςετͯ͠ΈΑ͏ func testLaunchLocalPushNotification() { let identifier = "testLocalNotification" let request = UNNotificationRequest( identifier: identifier, content: .init(), trigger: UNTimeIntervalNotificationTrigger.init(timeInterval: 30, repeats: false) ) guard let event = LaunchTracker.track(launchType: .notification(request)) else { XCTFail() return } XCTAssertEqual(event, LaunchTracker.Event.localNotification(identifier: identifier)) } औಘͨ͠Πϕϯτͱɺਖ਼ղΛൺֱ

Slide 73

Slide 73 text

ςετͯ͠ΈΑ͏ func testLaunchDeepLink() { let userActivity = NSUserActivity(activityType: NSUserActivityTypeBrowsingWeb) let url = URL(string: "https://example.coordinator.com/123456")! userActivity.webpageURL = url guard let event = LaunchTracker.track(launchType: .userActivity(userActivity)) else { XCTFail() return } XCTAssertEqual(event, .deepLink(url: url)) }

Slide 74

Slide 74 text

ςετͯ͠ΈΑ͏ func testLaunchSpotlightClickResult() { let userActivity = NSUserActivity(activityType: CSSearchableItemActionType) userActivity.userInfo = [CSSearchableItemActivityIdentifier: "123456"] guard let event = LaunchTracker.track(launchType: .userActivity(userActivity)) else { XCTFail() return } XCTAssertEqual(event, .spotlight(resultIdentifier: "123456")) }

Slide 75

Slide 75 text

ςετͯ͠ΈΑ͏ func testLaunchRemotePushNotification() { let identifier = "testLocalNotification" let request = UNNotificationRequest( identifier: identifier, content: .init(), trigger: UNPushNotificationTrigger.init() ) guard let event = LaunchTracker.track(launchType: .notification(request)) else { XCTFail() return } XCTAssertEqual(event, LaunchTracker.Event.localNotification(identifier: identifier)) }

Slide 76

Slide 76 text

ςετͯ͠ΈΑ͏ func testLaunchRemotePushNotification() { let identifier = "testLocalNotification" let request = UNNotificationRequest( identifier: identifier, content: .init(), trigger: UNPushNotificationTrigger.init() ) guard let event = LaunchTracker.track(launchType: .notification(request)) else { XCTFail() return } XCTAssertEqual(event, LaunchTracker.Event.localNotification(identifier: identifier)) }

Slide 77

Slide 77 text

Initializer͕ެ։͞Ε͍ͯͳ͍ @available(iOS 10.0, *) open class UNNotificationTrigger : NSObject, NSCopying, NSSecureCoding { open var repeats: Bool { get } } @available(iOS 10.0, *) open class UNPushNotificationTrigger : UNNotificationTrigger { }

Slide 78

Slide 78 text

Q. Ͳ͏͢Δʁ

Slide 79

Slide 79 text

Q. Ͳ͏͢Δʁ A. ఘΊΔʂ

Slide 80

Slide 80 text

ఘΊΔʁ • ؒ઀తʹςετͰ͖ͳ͍͔Λߟ͑Δ • ϩʔΧϧϓογϡ͡Όͳ͍ˠϦϞʔτϓογϡ • ແཧ͠ͳ͍ʢϢχοτςετ͸͋͘·Ͱखஈʣ

Slide 81

Slide 81 text

ࠓճ࿩ͨ͜͠ͱ • ىಈܦ࿏Λ୊ࡐʹͨ͠Ϣχοτςετͷ࿩ • ςετͰ͖ͳ͍΋ͷΛςετՄೳʹͳΔΑ͏ʹ෼ׂ͢Δ • ೖྗ x ग़ྗͷ૊Έ߹Θ͕ͤগͳ͘ͳΔΑ͏ʹɺ੍໿Λ ͚ͭΔ • ͦΕͰ΋μϝͳΒແཧ͸͠ͳ͍

Slide 82

Slide 82 text

ࠓճ࿩ͤͳ͔ͬͨ͜ͱ • ઃܭύλʔϯͷ࿩ʢCoordinator, RouterͳͲ) • ձࣾͷϓϩμΫτͰͲ͏͍ͯ͠Δ͔

Slide 83

Slide 83 text

ӶҙࣥචதͰ͢

Slide 84

Slide 84 text

import UIKit import CoreSpotlight import UserNotifications @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? private var appCoordinator: AppCoordinator? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { self.window = UIWindow(frame: UIScreen.main.bounds) self.appCoordinator = AppCoordinator(window: window!, launchType: .normal) appCoordinator!.start() return true } func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { self.window = UIWindow(frame: UIScreen.main.bounds) let type: LaunchType = .userActivity(userActivity) self.appCoordinator = AppCoordinator(window: window!, launchType: type) appCoordinator!.start() return true } func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { self.window = UIWindow(frame: UIScreen.main.bounds) let type: LaunchType = .openURL(url) self.appCoordinator = AppCoordinator(window: window!, launchType: type) appCoordinator!.start() return true } func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { self.window = UIWindow(frame: UIScreen.main.bounds) let type: LaunchType = .shortcutItem(shortcutItem) self.appCoordinator = AppCoordinator(window: window!, launchType: type) appCoordinator!.start() completionHandler(true) } } extension AppDelegate: UNUserNotificationCenterDelegate { func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { self.window = UIWindow(frame: UIScreen.main.bounds) let request = response.notification.request let type: LaunchType = .notification(request) self.appCoordinator = AppCoordinator(window: window!, launchType: type) appCoordinator!.start() completionHandler() } } ※ιʔείʔυ͸ΠϝʔδͰ͢

Slide 85

Slide 85 text

͜ͷ͙Β͍εοΩϦ͠·͢ ※͋͘·Ͱىಈॲཧͷ࿩Ͱ͢

Slide 86

Slide 86 text

ձࣾͷࣄྫ

Slide 87

Slide 87 text

ձࣾͷࣄྫ ͪ͜Β·Ͱ

Slide 88

Slide 88 text

9/12ɾ13 @DeNA

Slide 89

Slide 89 text

Thank you!