Slide 1

Slide 1 text

Ίͤ͟ʂWKWebViewϚελʔʂ @marcy731 iOSDC Japan 2024 2024/08/24 13:00ʙ Track D

Slide 2

Slide 2 text

1. ͸͡Ίʹ 2. WKWebViewͷجૅ஌ࣝ 3. ΠϯλϥΫςΟϒͳػೳͷ࣮૷ 4. ϓϥΠόγʔอޢͱηΩϡϦςΟ 5. ߴ౓ͳઃఆͱΧελϚΠζ 6. σόοά 7. Ԡ༻ͱ࣮ફྫ 8. ·ͱΊ Agenda

Slide 3

Slide 3 text

1. ͸͡Ίʹ 2. WKWebViewͷجૅ஌ࣝ 3. ΠϯλϥΫςΟϒͳػೳͷ࣮૷ 4. ϓϥΠόγʔอޢͱηΩϡϦςΟ 5. ߴ౓ͳઃఆͱΧελϚΠζ 6. σόοά 7. Ԡ༻ͱ࣮ફྫ 8. ·ͱΊ Agenda

Slide 4

Slide 4 text

͸͡Ίʹ

Slide 5

Slide 5 text

ࣗݾ঺հ • ໊લɿ௕୩઒ ক࢘ʢ͸͕ͤΘ ·͞͠ʣ • ݺͼ໊ɿ·ʔ͠ʔ • IDɿ @marcy731 • ॴଐɿSTORESגࣜձࣾ

Slide 6

Slide 6 text

ຊτʔΫͷ໨తɾλʔήοτ • ໨తɿ • WKWebViewͷجຊ͔ΒԠ༻·ͰΛΧόʔ͠ɺॳ৺ऀ͔Βதڃऀ·Ͱͷ։ൃ ऀ͕WKWebViewΛ࢖͍͜ͳͤΔΑ͏ʹͳΔ͜ͱΛ໨ࢦ͠·͢ • → WKWebViewϚελʔ • λʔήοτɿ • ॳ৺ऀɿWKWebViewΛ͸͡Ίͯ৮Δਓ • தڃऀɿWKWebViewΛ࢖͍ͬͯΔ͚ͲɺͳΜͱͳ͘Ͱ࢖ͬͯΔਓ

Slide 7

Slide 7 text

஫ҙࣄ߲ • ຊτʔΫͰ͸ WKWebView ࣗମͷઆ໌ʹऴ͍࢝ͯ͠Δ • SwiftUI Ͱ WKWebView Λ͏·͘࢖͏ํ๏͸ର৅֎ • ैͬͯɺαϯϓϧίʔυ΋ UIKit Λར༻͢Δ

Slide 8

Slide 8 text

1. ͸͡Ίʹ 2. WKWebViewͷجૅ஌ࣝ 3. ΠϯλϥΫςΟϒͳػೳͷ࣮૷ 4. ϓϥΠόγʔอޢͱηΩϡϦςΟ 5. ߴ౓ͳઃఆͱΧελϚΠζ 6. σόοά 7. Ԡ༻ͱ࣮ફྫ 8. ·ͱΊ Agenda

Slide 9

Slide 9 text

WKWebViewͷجૅ஌ࣝ

Slide 10

Slide 10 text

WebKitͱ͸ʁ • WebKit͸ɺAppleͷΦʔϓϯιʔεͷWeb Browser Engine • HTMLɺCSSɺJavaScriptͳͲͷ΢Σϒඪ४Λαϙʔτ • iOS΍macOSͷWebίϯςϯπΛදࣔ͢ΔͨΊʹ࢖༻ • Safari Browser΋͜ͷΤϯδϯΛར༻ https://webkit.org/

Slide 11

Slide 11 text

WKWebViewͱ͸ʁ • WKWebView ͸ɺiOSΞϓϦͰWebίϯςϯπΛදࣔ͢ΔͨΊͷClass • WebKitϑϨʔϜϫʔΫ Λར༻ͯ͠ɺWebϖʔδ΍HTMLίϯςϯπΛϨϯμ Ϧϯά͍ͯ͠Δ

Slide 12

Slide 12 text

WKWebViewͷओͳಛ௃ • ߴ଎Ͱޮ཰తͳύϑΥʔϚϯεɿ • WebKitΤϯδϯΛ࢖༻ͯ͠ɺߴ଎ͳϖʔδϨϯμϦϯάͱޮ཰తͳϦιʔ ε؅ཧΛ࣮ݱ • ϓϩηε෼཭ͱηΩϡϦςΟɿ • ΢ΣϒίϯςϯπͱΞϓϦͷϓϩηεΛ෼཭͠ɺΞϓϦͷ҆ఆੑͱηΩϡ ϦςΟΛ޲্ • ߴ౓ͳΧελϚΠζͱ੍ޚɿ • JavaScriptͱͷ࿈ܞ΍ίϯςϯπͷ੍ޚ͕ॊೈʹߦ͑Δ

Slide 13

Slide 13 text

WKWebViewͱଞͷWebViewͷҧ͍ • UIWebView (Deprecated) • In-App Browser • SFSafariViewController • ASWebAuthenticationSession

Slide 14

Slide 14 text

WKWebViewͱSFSafariViewControllerͷҧ͍ https://developer.apple.com/jp/news/?id=trjs0tcd

Slide 15

Slide 15 text

WKWebViewͱSFSafariViewControllerͷҧ͍ • WKWebView • WebKitϑϨʔϜϫʔΫ • දࣔΛΧελϚΠζ·ͨ͸ίϯτϩʔϧ͍ͨ࣌͠ʹ࠷ద • WebίϯςϯπΛAppͷUIʹγʔϜϨεʹ౷߹Ͱ͖Δ • SFSafariViewController • SafariServicesϑϨʔϜϫʔΫ • ؅ཧ֎ͷWebίϯςϯπΛදࣔͨ͠ΓɺΧελϚΠζ΍ίϯτϩʔϧ͕ෆཁͳ࣌ʹ࠷ద • ύεϫʔυͷࣗಈೖྗɺϦʔμʔɺηΩϡΞϒϥ΢δϯάͳͲͷػೳΛؚΉSafariͱಉ ༷ͷWebϒϥ΢δϯάମݧ͕Ͱ͖Δ https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller

Slide 16

Slide 16 text

SFSafariViewControllerͱASWebAuthenticationSession • SFSafariViewController • SafariServicesϑϨʔϜϫʔΫ • ؅ཧ֎ͷWebίϯςϯπΛදࣔͨ͠ΓɺΧελϚΠζ΍ίϯτϩʔϧ͕ෆཁͳ࣌ʹ࠷ద • ύεϫʔυͷࣗಈೖྗɺϦʔμʔɺηΩϡΞϒϥ΢δϯάͳͲͷػೳΛؚΉSafariͱಉ༷ͷWeb ϒϥ΢δϯάମݧ͕Ͱ͖Δ • ASWebAuthenticationSession • SafariServicesϑϨʔϜϫʔΫ • αʔυύʔςΟͷೝূʢϩάΠϯͳͲʣ͕ඞཁͳ৔߹ʹ࠷ద • αʔυύʔςΟೝূϓϩόΠμͱͷηΩϡΞͳೝূηογϣϯΛఏڙ͠ɺOAuth΍OpenID ConnectͳͲͷೝূΛαϙʔτ

Slide 17

Slide 17 text

WKWebViewͷجຊతͳ࢖͍ํ import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } }

Slide 18

Slide 18 text

WKWebViewͷجຊతͳ࢖͍ํ import UIKit import WebKit class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() // WKWebViewͷΠϯελϯεΛ࡞੒ webView = WKWebView(frame: view.bounds) } }

Slide 19

Slide 19 text

WKWebViewͷجຊతͳ࢖͍ํ import UIKit import WebKit class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() // WKWebViewͷΠϯελϯεΛ࡞੒ webView = WKWebView(frame: view.bounds) // ϏϡʔʹWKWebViewΛ௥Ճ view.addSubview(webView) } }

Slide 20

Slide 20 text

WKWebViewͷجຊతͳ࢖͍ํ import UIKit import WebKit class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() // WKWebViewͷΠϯελϯεΛ࡞੒ webView = WKWebView(frame: view.bounds) // ϏϡʔʹWKWebViewΛ௥Ճ view.addSubview(webView) // WebϖʔδΛಡΈࠐΉ if let url = URL(string: "https://www.apple.com") { let request = URLRequest(url: url) webView.load(request) } } }

Slide 21

Slide 21 text

WKWebViewͷجຊతͳ࢖͍ํ import UIKit import WebKit class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() // WKWebViewͷΠϯελϯεΛ࡞੒ webView = WKWebView(frame: view.bounds) // ϏϡʔʹWKWebViewΛ௥Ճ view.addSubview(webView) // WebϖʔδΛಡΈࠐΉ if let url = URL(string: "https://www.apple.com") { let request = URLRequest(url: url) webView.load(request) } } }

Slide 22

Slide 22 text

WKWebViewͷجຊతͳ࢖͍ํ import UIKit import WebKit class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() // WKWebViewͷΠϯελϯεΛ࡞੒ webView = WKWebView(frame: view.bounds) // ϏϡʔʹWKWebViewΛ௥Ճ view.addSubview(webView) // ϩʔΧϧHTMLϑΝΠϧͷಡΈࠐΈ if let localHtmlPath = Bundle.main.path(forResource: "index", ofType: "html") { let localHtmlUrl = URL(fileURLWithPath: localHtmlPath) let request = URLRequest(url: localHtmlUrl) webView.load(request) } } }

Slide 23

Slide 23 text

WKWebViewͷجຊతͳ࢖͍ํ import UIKit import WebKit class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() // WKWebViewͷΠϯελϯεΛ࡞੒ webView = WKWebView(frame: view.bounds) // ϏϡʔʹWKWebViewΛ௥Ճ view.addSubview(webView) // HTMLจࣈྻͷ௚઀ಡΈࠐΈ let htmlString = "

Hello, World!

" webView.loadHTMLString(htmlString, baseURL: nil) } }

Slide 24

Slide 24 text

WKWebViewͷओཁͳίϯϙʔωϯτ • WKWebViewɿ Web ίϯςϯπΛදࣔ͢ΔView • WKWebViewCon f igurationɿ WKWebView ͷઃఆΛΧελϚΠζ͢ΔClass • WKUserContentControllerɿ JavaScript ͔ΒMessageΛड৴͢ΔͨΊͷClass • WKScriptMessageHandlerɿJavaScript ͔ΒMessageΛॲཧ͢ΔProtcol • WKNavigationDelegateɿ φϏήʔγϣϯʢϖʔδಡΈࠐΈɺભҠɺΤϥʔͳͲʣΛ؂ࢹ͢ΔProtcol • WKUIDelegateɿ UIؔ࿈ͷΠϕϯτʢΞϥʔτɺϙοϓΞοϓɺೖྗͳͲʣΛॲཧ͢ΔProtcol • WKNavigationɿ Web ϖʔδͷφϏήʔγϣϯͷঢ়ଶΛද͢Class • WKWebsiteDataStoreɿWKWebView ͷCacheɺCookieɺLocal StrageͳͲΛ؅ཧ͢ΔClass • WKProcessPoolɿWKWebView ΠϯελϯεؒͰϓϩηεΛڞ༗͢ΔͨΊͷClass • Etc…

Slide 25

Slide 25 text

1. ͸͡Ίʹ 2. WKWebViewͷجૅ஌ࣝ 3. ΠϯλϥΫςΟϒͳػೳͷ࣮૷ 4. ϓϥΠόγʔอޢͱηΩϡϦςΟ 5. ߴ౓ͳઃఆͱΧελϚΠζ 6. σόοά 7. Ԡ༻ͱ࣮ફྫ 8. ·ͱΊ Agenda

Slide 26

Slide 26 text

ΠϯλϥΫςΟϒͳػೳͷ࣮૷

Slide 27

Slide 27 text

ΠϯλϥΫςΟϒͳػೳͷ࣮૷ • JavaScriptͱͷ࿈ܞ • JavaScript͔ΒωΠςΟϒίʔυͷݺͼग़͠ • ωΠςΟϒίʔυ͔ΒJavaScriptͷ࣮ߦ • ϢʔβʔΠϯλϥΫγϣϯͷΧελϚΠζ • ζʔϜͷ੍ޚ

Slide 28

Slide 28 text

ΠϯλϥΫςΟϒͳػೳͷ࣮૷ • JavaScriptͱͷ࿈ܞ • JavaScript͔ΒωΠςΟϒίʔυͷݺͼग़͠ • ωΠςΟϒίʔυ͔ΒJavaScriptͷ࣮ߦ • ϢʔβʔΠϯλϥΫγϣϯͷΧελϚΠζ • ζʔϜͷ੍ޚ

Slide 29

Slide 29 text

JavaScript͔ΒωΠςΟϒίʔυͷݺͼग़͠ • WKScriptMessageHandler • JavaScript ͔ΒͷϝοηʔδΛड৴͢ΔͨΊͷϓϩτίϧ • WKScriptMessageHandlerWithReply • JavaScript ͔Βͷϝοηʔδʹରͯ͠ඇಉظతʹฦ৴͢ΔͨΊͷϓϩτίϧ

Slide 30

Slide 30 text

JavaScript͔ΒωΠςΟϒίʔυͷݺͼग़͠ • WKScriptMessageHandler • JavaScript ͔ΒͷϝοηʔδΛड৴͢ΔͨΊͷϓϩτίϧ • WKScriptMessageHandlerWithReply • JavaScript ͔Βͷϝοηʔδʹରͯ͠ඇಉظతʹฦ৴͢ΔͨΊͷϓϩτίϧ

Slide 31

Slide 31 text

WKScriptMessageHandler Λར༻ͨ͠ྫ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() webView = WKWebView(frame: view.bounds) view.addSubview(webView) if let url = URL(string: "https://www.apple.com") { let request = URLRequest(url: url) webView.load(request) } } }

Slide 32

Slide 32 text

WKScriptMessageHandler Λར༻ͨ͠ྫ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() // WKUserContentControllerɿJavaScript ͔ΒͷϝοηʔδΛड৴͢ΔͨΊͷClass // “myMessageHandler” ͱ͍͏໊લͰड͚औΔ४උ let contentController = WKUserContentController() contentController.add(self, name: "myMessageHandler") webView = WKWebView(frame: view.bounds) view.addSubview(webView) if let url = URL(string: "https://www.apple.com") { let request = URLRequest(url: url) webView.load(request) } } }

Slide 33

Slide 33 text

WKScriptMessageHandler Λར༻ͨ͠ྫ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() let contentController = WKUserContentController() contentController.add(self, name: "myMessageHandler") // WKWebViewConfigurationɿઃఆΛΧελϚΠζ͢ΔͨΊͷClass // WKWebViewConfigurationʹWKUserContentControllerΛ௥Ճ͠ɺWKWebViewͷΠχγϟϥΠβͰઃఆ let config = WKWebViewConfiguration() config.userContentController = contentController webView = WKWebView(frame: view.bounds, configuration: config) view.addSubview(webView) if let url = URL(string: "https://www.apple.com") { let request = URLRequest(url: url) webView.load(request) } } }

Slide 34

Slide 34 text

WKScriptMessageHandler Λར༻ͨ͠ྫ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() … contentController.add(self, name: "myMessageHandler") … } } // WKScriptMessageHandlerɿJavaScript ͔ΒͷϝοηʔδΛॲཧ͢Δϓϩτίϧ extension ViewController: WKScriptMessageHandler { }

Slide 35

Slide 35 text

WKScriptMessageHandler Λར༻ͨ͠ྫ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() … contentController.add(self, name: "myMessageHandler") … } } // WKScriptMessageHandlerɿJavaScript ͔ΒͷϝοηʔδΛॲཧ͢Δϓϩτίϧ extension ViewController: WKScriptMessageHandler { func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { } }

Slide 36

Slide 36 text

class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() … contentController.add(self, name: "myMessageHandler") … } } // WKScriptMessageHandlerɿJavaScript ͔ΒͷϝοηʔδΛॲཧ͢Δϓϩτίϧ extension ViewController: WKScriptMessageHandler { func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { if message.name == "myMessageHandler" { print("Received message from JavaScript: \(message.body)") } } } WKScriptMessageHandler Λར༻ͨ͠ྫ

Slide 37

Slide 37 text

WKScriptMessageHandler Λར༻ͨ͠ྫ myMessageHandler.js ɹ // JavaScript ଆͰ͸ webkit.messageHandlers..postMessage Λར༻ window.webkit.messageHandlers.myMessageHandler.postMessage("Hello from JavaScript"); https://developer.apple.com/documentation/webkitjs/usermessagehandler/1631164-postmessage

Slide 38

Slide 38 text

WKScriptMessageHandler Λར༻ͨ͠ྫ // 3. JavaScript ଆͰ͸͋Β͔͡ΊܾΊͨϝιου໊ͰɹpostMessage Λୟ͍ͯωΠςΟϒଆʹMessageΛૹΔ window.webkit.messageHandlers.myMessageHandler.postMessage("Hello from JavaScript"); // 2. WKScriptMessageHandler Ͱ JavaScript ͔ΒͷϝοηʔδΛॲཧ͢Δ extension ViewController: WKScriptMessageHandler { func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { if message.name == "myMessageHandler" { print("Received message from JavaScript: \(message.body)") } } } // 1. WKUserContentController ΁ొ࿥ contentController.add(self, name: "myMessageHandler")

Slide 39

Slide 39 text

WKScriptMessageHandler Λར༻ͨ͠ྫ // 3. JavaScript ଆͰ͸͋Β͔͡ΊܾΊͨϝιου໊ͰɹpostMessage Λୟ͍ͯωΠςΟϒଆʹMessageΛૹΔ window.webkit.messageHandlers.myMessageHandler.postMessage("Hello from JavaScript"); // 2. WKScriptMessageHandler Ͱ JavaScript ͔ΒͷϝοηʔδΛॲཧ͢Δ extension ViewController: WKScriptMessageHandler { func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { if message.name == "myMessageHandler" { print("Received message from JavaScript: \(message.body)") } } } // 1. WKUserContentController ΁ొ࿥ contentController.add(self, name: "myMessageHandler")

Slide 40

Slide 40 text

WKScriptMessageHandler Λར༻ͨ͠ྫ // 3. JavaScript ଆͰ͸͋Β͔͡ΊܾΊͨϝιου໊ͰɹpostMessage Λୟ͍ͯωΠςΟϒଆʹMessageΛૹΔ window.webkit.messageHandlers.myMessageHandler.postMessage("Hello from JavaScript"); // 2. WKScriptMessageHandler Ͱ JavaScript ͔ΒͷϝοηʔδΛॲཧ͢Δ extension ViewController: WKScriptMessageHandler { func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { if message.name == "myMessageHandler" { print("Received message from JavaScript: \(message.body)") } } } // 1. WKUserContentController ΁ొ࿥ contentController.add(self, name: "myMessageHandler")

Slide 41

Slide 41 text

// 3. JavaScript ଆͰ͸͋Β͔͡ΊܾΊͨϝιου໊ͰɹpostMessage Λୟ͍ͯωΠςΟϒଆʹMessageΛૹΔ window.webkit.messageHandlers.myMessageHandler.postMessage("Hello from JavaScript"); // 2. WKScriptMessageHandler Ͱ JavaScript ͔ΒͷϝοηʔδΛॲཧ͢Δ extension ViewController: WKScriptMessageHandler { func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { if message.name == "myMessageHandler" { print("Received message from JavaScript: \(message.body)") } } } // 1. WKUserContentController ΁ొ࿥ contentController.add(self, name: "myMessageHandler") WKScriptMessageHandler Λར༻ͨ͠ྫ

Slide 42

Slide 42 text

JavaScript͔ΒωΠςΟϒίʔυͷݺͼग़͠ • WKScriptMessageHandler • JavaScript ͔ΒͷϝοηʔδΛड৴͢ΔͨΊͷϓϩτίϧ • WKScriptMessageHandlerWithReply • JavaScript ͔Βͷϝοηʔδʹରͯ͠ඇಉظతʹฦ৴͢ΔͨΊͷϓϩτίϧ

Slide 43

Slide 43 text

WKScriptMessageHandlerWithReply Λར༻ͨ͠ྫ // 3. JavaScriptଆͰ͸ postMessage Λୟ͍ͯωΠςΟϒଆʹMessageΛૹΓɺϨεϙϯεΛPromiseͰॲཧ͢Δ try { const result = await window.webkit.messageHandlers.myMessageHandler.postMessage("Hello"); console.log(result); // ੒ޭͨ݁͠Ռ: Received message from JavaScript: Hello } catch (err) { console.log(err); // Τϥʔ಺༰: Error } // 2. JavaScript ͔ΒͷϝοηʔδΛॲཧ͠ɺJavaScriptଆ΁ฦ৴͢Δ extension ViewController: WKScriptMessageHandlerWithReply { func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) async -> (Any?, String?) { if message.name == "myMessageHandler" { return ("Received message from JavaScript: \(message.body)", nil) } else { return (nil, "Error") } } } // 1. WKScriptMessageHandlerWithReplyͷhandler௥Ճ༻ͷϝιου΁௥Ճ contentController.addScriptMessageHandler(self, contentWorld: .page, name: "myMessageHandler")

Slide 44

Slide 44 text

WKScriptMessageHandlerWithReply Λར༻ͨ͠ྫ // 3. JavaScriptଆͰ͸ postMessage Λୟ͍ͯωΠςΟϒଆʹMessageΛૹΓɺϨεϙϯεΛPromiseͰॲཧ͢Δ try { const result = await window.webkit.messageHandlers.myMessageHandler.postMessage("Hello"); console.log(result); // ੒ޭͨ݁͠Ռ: Received message from JavaScript: Hello } catch (err) { console.log(err); // Τϥʔ಺༰: Error } // 2. JavaScript ͔ΒͷϝοηʔδΛॲཧ͠ɺJavaScriptଆ΁ฦ৴͢Δ extension ViewController: WKScriptMessageHandlerWithReply { func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) async -> (Any?, String?) { if message.name == "myMessageHandler" { return ("Received message from JavaScript: \(message.body)", nil) } else { return (nil, "Error") } } } // 1. WKScriptMessageHandlerWithReplyͷhandler௥Ճ༻ͷϝιου΁௥Ճ contentController.addScriptMessageHandler(self, contentWorld: .page, name: "myMessageHandler")

Slide 45

Slide 45 text

WKScriptMessageHandlerWithReply Λར༻ͨ͠ྫ // 3. JavaScriptଆͰ͸ postMessage Λୟ͍ͯωΠςΟϒଆʹMessageΛૹΓɺϨεϙϯεΛPromiseͰॲཧ͢Δ try { const result = await window.webkit.messageHandlers.myMessageHandler.postMessage("Hello"); console.log(result); // ੒ޭͨ݁͠Ռ: Received message from JavaScript: Hello } catch (err) { console.log(err); // Τϥʔ಺༰: Error } // 2. JavaScript ͔ΒͷϝοηʔδΛॲཧ͠ɺJavaScriptଆ΁ฦ৴͢Δ extension ViewController: WKScriptMessageHandlerWithReply { func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) async -> (Any?, String?) { if message.name == "myMessageHandler" { return ("Received message from JavaScript: \(message.body)", nil) } else { return (nil, "Error") } } } // 1. WKScriptMessageHandlerWithReplyͷhandler௥Ճ༻ͷϝιου΁௥Ճ contentController.addScriptMessageHandler(self, contentWorld: .page, name: "myMessageHandler")

Slide 46

Slide 46 text

WKScriptMessageHandlerWithReply Λར༻ͨ͠ྫ // 3. JavaScriptଆͰ͸ postMessage Λୟ͍ͯωΠςΟϒଆʹMessageΛૹΓɺϨεϙϯεΛPromiseͰॲཧ͢Δ try { const result = await window.webkit.messageHandlers.myMessageHandler.postMessage("Hello"); console.log(result); // ੒ޭͨ݁͠Ռ: Received message from JavaScript: Hello } catch (err) { console.log(err); // Τϥʔ಺༰: Error } // 2. JavaScript ͔ΒͷϝοηʔδΛॲཧ͠ɺJavaScriptଆ΁ฦ৴͢Δ extension ViewController: WKScriptMessageHandlerWithReply { func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) async -> (Any?, String?) { if message.name == "myMessageHandler" { return ("Received message from JavaScript: \(message.body)", nil) } else { return (nil, "Error") } } } // 1. WKScriptMessageHandlerWithReplyͷhandler௥Ճ༻ͷϝιου΁௥Ճ contentController.addScriptMessageHandler(self, contentWorld: .page, name: "myMessageHandler")

Slide 47

Slide 47 text

WKScriptMessageHandlerWithReply Λར༻ͨ͠ྫ // 3. JavaScriptଆͰ͸ postMessage Λୟ͍ͯωΠςΟϒଆʹMessageΛૹΓɺϨεϙϯεΛPromiseͰॲཧ͢Δ try { const result = await window.webkit.messageHandlers.myMessageHandler.postMessage("Hello"); console.log(result); // ੒ޭͨ݁͠Ռ: Received message from JavaScript: Hello } catch (err) { console.log(err); // Τϥʔ಺༰: Error } // 2. JavaScript ͔ΒͷϝοηʔδΛॲཧ͠ɺJavaScriptଆ΁ฦ৴͢Δ extension ViewController: WKScriptMessageHandlerWithReply { func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) async -> (Any?, String?) { if message.name == "myMessageHandler" { return ("Received message from JavaScript: \(message.body)", nil) } else { return (nil, "Error") } } } // 1. WKScriptMessageHandlerWithReplyͷhandler௥Ճ༻ͷϝιου΁௥Ճ contentController.addScriptMessageHandler(self, contentWorld: .page, name: "myMessageHandler")

Slide 48

Slide 48 text

WKScriptMessageHandlerWithReply Λར༻ͨ͠ྫ // 3. JavaScriptଆͰ͸ postMessage Λୟ͍ͯωΠςΟϒଆʹMessageΛૹΓɺϨεϙϯεΛPromiseͰॲཧ͢Δ try { const result = await window.webkit.messageHandlers.myMessageHandler.postMessage("Hello"); console.log(result); // ੒ޭͨ݁͠Ռ: Received message from JavaScript: Hello } catch (err) { console.log(err); // Τϥʔ಺༰: Error } // 2. JavaScript ͔ΒͷϝοηʔδΛॲཧ͠ɺJavaScriptଆ΁ฦ৴͢Δ extension ViewController: WKScriptMessageHandlerWithReply { func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) async -> (Any?, String?) { if message.name == "myMessageHandler" { return ("Received message from JavaScript: \(message.body)", nil) } else { return (nil, "Error") } } } // 1. WKScriptMessageHandlerWithReplyͷhandler௥Ճ༻ͷϝιου΁௥Ճ contentController.addScriptMessageHandler(self, contentWorld: .page, name: "myMessageHandler")

Slide 49

Slide 49 text

JavaScript͔ΒωΠςΟϒίʔυͷݺͼग़͠ (࠶ܝ) • WKScriptMessageHandler • JavaScript ͔ΒͷϝοηʔδΛड৴͢ΔͨΊͷϓϩτίϧ • WKScriptMessageHandlerWithReply • JavaScript ͔Βͷϝοηʔδʹରͯ͠ඇಉظతʹฦ৴͢ΔͨΊͷϓϩτίϧ

Slide 50

Slide 50 text

ΠϯλϥΫςΟϒͳػೳͷ࣮૷ • JavaScriptͱͷ࿈ܞ • JavaScript͔ΒωΠςΟϒίʔυͷݺͼग़͠ • ωΠςΟϒίʔυ͔ΒJavaScriptͷ࣮ߦ • ϢʔβʔΠϯλϥΫγϣϯͷΧελϚΠζ • ζʔϜͷ੍ޚ

Slide 51

Slide 51 text

ωΠςΟϒίʔυ͔ΒJavaScriptͷ࣮ߦ • WKUserScript ͷར༻ • WebίϯςϯπʹJavaScriptΛૠೖ͢Δ • evaluateJavaScript(_:completionHandler:)ϝιουͷར༻ • ೚ҙͷλΠϛϯάͰJavaScriptΛ࣮ߦ͢ΔAPI

Slide 52

Slide 52 text

ωΠςΟϒίʔυ͔ΒJavaScriptͷ࣮ߦ • WKUserScript ͷར༻ • WebίϯςϯπʹJavaScriptΛૠೖ͢Δ • evaluateJavaScript(_:completionHandler:)ϝιουͷར༻ • ೚ҙͷλΠϛϯάͰJavaScriptΛ࣮ߦ͢ΔAPI

Slide 53

Slide 53 text

WKUserScript ͷར༻ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() let contentController = WKUserContentController() let config = WKWebViewConfiguration() config.userContentController = contentController webView = WKWebView(frame: view.bounds, configuration: config) view.addSubview(webView) if let url = URL(string: "https://www.apple.com") { webView.load(URLRequest(url: url)) } } }

Slide 54

Slide 54 text

WKUserScript ͷར༻ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() let contentController = WKUserContentController() // JavaScriptจࣈྻΛWKUserScriptʹม׵͠ɺWKUserContentControllerʹ௥Ճ͢Δ let script = "document.querySelector('#header').style.display = 'none'" let userScript = WKUserScript( source: script, injectionTime: .atDocumentEnd,ɹ forMainFrameOnly: true ) contentController.addUserScript(userScript) let config = WKWebViewConfiguration() config.userContentController = contentController webView = WKWebView(frame: view.bounds, configuration: config) view.addSubview(webView) if let url = URL(string: "https://www.apple.com") { webView.load(URLRequest(url: url)) }

Slide 55

Slide 55 text

WKUserScript ͷར༻ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() let contentController = WKUserContentController() // JavaScriptจࣈྻΛWKUserScriptʹม׵͠ɺWKUserContentControllerʹ௥Ճ͢Δ let script = "document.querySelector('#header').style.display = 'none'" let userScript = WKUserScript( source: script, // atDocumentStart: ϖʔδͷϩʔυલʹεΫϦϓτΛ஫ೖ // atDocumentEnd: ϖʔδͷશମ͕ಡΈࠐ·ΕͨޙʹεΫϦϓτΛ஫ೖ injectionTime: .atDocumentEnd,ɹ forMainFrameOnly: true ) contentController.addUserScript(userScript) let config = WKWebViewConfiguration() config.userContentController = contentController webView = WKWebView(frame: view.bounds, configuration: config) view.addSubview(webView)

Slide 56

Slide 56 text

ωΠςΟϒίʔυ͔ΒJavaScriptͷ࣮ߦ • WKUserScript ͷར༻ • WebίϯςϯπʹJavaScriptΛૠೖ͢Δ • evaluateJavaScript(_:completionHandler:)ϝιουͷར༻ • ೚ҙͷλΠϛϯάͰJavaScriptΛ࣮ߦ͢ΔAPI

Slide 57

Slide 57 text

evaluateJavaScript(_:completionHandler:)ϝιουͷར༻ import UIKit import WebKit extension ViewController: WKNavigationDelegate { // WebίϯςϯπͷಡΈࠐΈ׬ྃΛϋϯυϦϯά͢ΔWKNavigationDelegateϝιου // WebίϯςϯπͷಡΈࠐΈ׬ྃ࣌ʹJavaScriptΛ࣮ߦ͢Δྫ func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) { } }

Slide 58

Slide 58 text

evaluateJavaScript(_:completionHandler:)ϝιουͷར༻ import UIKit import WebKit extension ViewController: WKNavigationDelegate { // WebίϯςϯπͷಡΈࠐΈ׬ྃΛϋϯυϦϯά͢ΔWKNavigationDelegateϝιου // WebίϯςϯπͷಡΈࠐΈ׬ྃ࣌ʹJavaScriptΛ࣮ߦ͢Δྫ func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) { // JavaScriptͷεΫϦϓτΛ४උ let javaScriptString = "document.body.style.backgroundColor = ‘red';" } }

Slide 59

Slide 59 text

evaluateJavaScript(_:completionHandler:)ϝιουͷར༻ import UIKit import WebKit extension ViewController: WKNavigationDelegate { // WebίϯςϯπͷಡΈࠐΈ׬ྃΛϋϯυϦϯά͢ΔWKNavigationDelegateϝιου // WebίϯςϯπͷಡΈࠐΈ׬ྃ࣌ʹJavaScriptΛ࣮ߦ͢Δྫ func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) { // JavaScriptͷεΫϦϓτΛ४උ let javaScriptString = "document.body.style.backgroundColor = ‘red’;" // evaluateJavaScript(_:completionHandler:)ϝιουΛ࢖༻ͯ͠ɺJavaScriptίʔυΛ࣮ߦ webView.evaluateJavaScript(javaScriptString) { (result, error) in if let error = error { print("JavaScript execution error: \(error.localizedDescription)") } else if let result = result { print("JavaScript execution result: \(result)") } } }

Slide 60

Slide 60 text

ωΠςΟϒίʔυ͔ΒJavaScriptͷ࣮ߦ (࠶ܝ) • WKUserScript ͷར༻ • WebίϯςϯπʹJavaScriptΛૠೖ͢Δ • evaluateJavaScript(_:completionHandler:)ϝιουͷར༻ • ೚ҙͷλΠϛϯάͰJavaScriptΛ࣮ߦ͢ΔAPI

Slide 61

Slide 61 text

ΠϯλϥΫςΟϒͳػೳͷ࣮૷ • JavaScriptͱͷ࿈ܞ • JavaScript͔ΒωΠςΟϒίʔυͷݺͼग़͠ • ωΠςΟϒίʔυ͔ΒJavaScriptͷ࣮ߦ • ϢʔβʔΠϯλϥΫγϣϯͷΧελϚΠζ • ζʔϜͷ੍ޚ

Slide 62

Slide 62 text

ζʔϜͷ੍ޚ • WKWebViewͷSubViewʹ͸UIScrollView͕͋Γɺ͜ͷUIScrollViewΛ੍ޚ ͢Δ͜ͱͰζʔϜͷ੍ޚΛߦ͏

Slide 63

Slide 63 text

WebView ಺ͷ Zooming Λແޮʹ͢Δ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() webView = WKWebView(frame: view.bounds) view.addSubview(webView) if let url = URL(string: "https://www.apple.com") { webView.load(URLRequest(url: url)) } } }

Slide 64

Slide 64 text

WebView ಺ͷ Zooming Λແޮʹ͢Δ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() webView = WKWebView(frame: view.bounds) view.addSubview(webView) webView.scrollView.delegate = self if let url = URL(string: "https://www.apple.com") { webView.load(URLRequest(url: url)) } } } // MARK: - UIScrollViewDelegate extension ViewController: UIScrollViewDelegate { }

Slide 65

Slide 65 text

WebView ಺ͷ Zooming Λແޮʹ͢Δ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() webView = WKWebView(frame: view.bounds) view.addSubview(webView) webView.scrollView.delegate = self if let url = URL(string: "https://www.apple.com") { webView.load(URLRequest(url: url)) } } } // MARK: - UIScrollViewDelegate extension ViewController: UIScrollViewDelegate { func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) { } }

Slide 66

Slide 66 text

WebView ಺ͷ Zooming Λແޮʹ͢Δ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() webView = WKWebView(frame: view.bounds) view.addSubview(webView) webView.scrollView.delegate = self if let url = URL(string: "https://www.apple.com") { webView.load(URLRequest(url: url)) } } } // MARK: - UIScrollViewDelegate extension ViewController: UIScrollViewDelegate { func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) { scrollView.pinchGestureRecognizer?.isEnabled = false }

Slide 67

Slide 67 text

1. ͸͡Ίʹ 2. WKWebViewͷجૅ஌ࣝ 3. ΠϯλϥΫςΟϒͳػೳͷ࣮૷ 4. ϓϥΠόγʔอޢͱηΩϡϦςΟ 5. ߴ౓ͳઃఆͱΧελϚΠζ 6. σόοά 7. Ԡ༻ͱ࣮ફྫ 8. ·ͱΊ Agenda

Slide 68

Slide 68 text

ϓϥΠόγʔอޢͱηΩϡϦςΟ

Slide 69

Slide 69 text

ϓϥΠόγʔอޢͱηΩϡϦςΟ • ϓϥΠόγʔͷอޢ • WebαΠτͷσʔλ؅ཧ • ϩʔΧϧετϨʔδͷ؅ཧ • ηΩϡϦςΟ • JavaScriptͷ੍ݶͱ؅ཧ • App Transport Security (ATS) ͷઃఆ

Slide 70

Slide 70 text

ϓϥΠόγʔอޢͱηΩϡϦςΟ • ϓϥΠόγʔͷอޢ • WebαΠτͷσʔλ؅ཧ • ϩʔΧϧετϨʔδͷ؅ཧ • ηΩϡϦςΟ • JavaScriptͷ੍ݶͱ؅ཧ • App Transport Security (ATS) ͷઃఆ

Slide 71

Slide 71 text

WebαΠτͷσʔλ؅ཧ • WKWebsiteDataStore • WebαΠτ͔ΒͷσʔλʢΫοΩʔɺΩϟογϡͳͲʣΛอଘ͠ɺ؅ཧ͢ ΔͨΊͷΫϥε • WKWebViewͷΠϯελϯεؒͰσʔλΛڞ༗ͨ͠ΓɺಛఆͷWebίϯς ϯπʹؔ࿈͢ΔσʔλͷΫϦʔϯΞοϓΛߦ͏͜ͱ͕Ͱ͖Δ • ϓϥΠόγʔอޢͷͨΊɺ͜ΕΒͷετϨʔδΛద੾ʹ؅ཧ͢Δ͜ͱ͕ॏཁ

Slide 72

Slide 72 text

WebαΠτͷσʔλ؅ཧ • WKWebsiteDataStore ͷ࣮૷͸ʮߴ౓ͳઃఆͱΧελϚΠζ > σʔλετ ΞͷΧελϚΠζʯͰઆ໌͢ΔͷͰ͜͜Ͱ͸লུ

Slide 73

Slide 73 text

ϓϥΠόγʔอޢͱηΩϡϦςΟ • ϓϥΠόγʔͷอޢ • WebαΠτͷσʔλ؅ཧ • ϩʔΧϧετϨʔδͷ؅ཧ • ηΩϡϦςΟ • JavaScriptͷ੍ݶͱ؅ཧ • App Transport Security (ATS) ͷઃఆ

Slide 74

Slide 74 text

ϩʔΧϧετϨʔδͷ؅ཧ • ϩʔΧϧετϨʔδ΁͸JavaScriptܦ༝ͰΞΫηε͢Δ͜ͱ͕Ͱ͖Δ

Slide 75

Slide 75 text

ϩʔΧϧετϨʔδͷ؅ཧ // ϩʔΧϧετϨʔδʹσʔλΛอଘ webView.evaluateJavaScript("localStorage.setItem('key', 'value');") {}

Slide 76

Slide 76 text

ϩʔΧϧετϨʔδͷ؅ཧ // ϩʔΧϧετϨʔδʹσʔλΛอଘ webView.evaluateJavaScript("localStorage.setItem('key', 'value');") {} // ϩʔΧϧετϨʔδ͔ΒσʔλΛऔಘ webView.evaluateJavaScript("localStorage.getItem(‘key’);”) {}

Slide 77

Slide 77 text

ϩʔΧϧετϨʔδͷ؅ཧ // ϩʔΧϧετϨʔδʹσʔλΛอଘ webView.evaluateJavaScript("localStorage.setItem('key', 'value');") {} // ϩʔΧϧετϨʔδ͔ΒσʔλΛऔಘ webView.evaluateJavaScript("localStorage.getItem(‘key’);”) {} // ϩʔΧϧετϨʔδ͔ΒσʔλΛ࡟আ webView.evaluateJavaScript("localStorage.removeItem('key');") {} https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage

Slide 78

Slide 78 text

ϩʔΧϧετϨʔδͷ؅ཧ • ஫ҙ఺ • ηΩϡϦςΟ: • localStorage ͸ΫϥΠΞϯταΠυͰσʔλΛอଘ͢ΔͨΊɺػີੑͷߴ͍৘ใ Λอଘ͢Δͷ͸ආ͚Δ΂͖ • σʔλ͕ϒϥ΢βͷετϨʔδʹอଘ͞ΕΔͨΊɺଞͷϖʔδ΍ηογϣϯͰ΋ ΞΫηεͰ͖ΔՄೳੑ͕͋Δ • ΫϩεΦϦδϯ: • WKWebView ͷ localStorage ͸ಉҰΦϦδϯϙϦγʔʹै͏ͨΊɺҟͳΔΦϦδ ϯͷϖʔδ͔Β͸ΞΫηεͰ͖ͳ͍

Slide 79

Slide 79 text

ϓϥΠόγʔอޢͱηΩϡϦςΟ • ϓϥΠόγʔͷอޢ • WebαΠτͷσʔλ؅ཧ • ϩʔΧϧετϨʔδͷ؅ཧ • ηΩϡϦςΟ • JavaScriptͷ੍ݶͱ؅ཧ • App Transport Security (ATS) ͷઃఆ

Slide 80

Slide 80 text

JavaScriptͷ੍ݶͱ؅ཧ • JavaScriptͷ࣮ߦ͕ෆཁͳͷͰ͋Ε͹໌ࣔతʹແޮʹͯ͋͛͠Δ͜ͱ΋ݕ౼

Slide 81

Slide 81 text

JavaScriptͷ੍ݶͱ؅ཧ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() webView = WKWebView(frame: view.bounds) view.addSubview(webView) if let url = URL(string: "https://www.apple.com") { webView.load(URLRequest(url: url)) } } }

Slide 82

Slide 82 text

JavaScriptͷ੍ݶͱ؅ཧ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() // WKWebpagePreferences: ϖʔδίϯςϯπͷಡΈࠐΈͱϨϯμϦϯά࣌ͷৼΔ෣͍Λࢦఆ // allowsContentJavaScript = false ͱ͢Δ͜ͱͰJavaScriptΛແޮʹ͢Δ let webpagePreferences = WKWebpagePreferences() webpagePreferences.allowsContentJavaScript = false webView = WKWebView(frame: view.bounds) view.addSubview(webView) if let url = URL(string: "https://www.apple.com") { webView.load(URLRequest(url: url)) } } }

Slide 83

Slide 83 text

JavaScriptͷ੍ݶͱ؅ཧ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() // WKWebpagePreferences: ϖʔδίϯςϯπͷಡΈࠐΈͱϨϯμϦϯά࣌ͷৼΔ෣͍Λࢦఆ // allowsContentJavaScript = false ͱ͢Δ͜ͱͰJavaScriptΛແޮʹ͢Δ let webpagePreferences = WKWebpagePreferences() webpagePreferences.allowsContentJavaScript = false let config = WKWebViewConfiguration() config.defaultWebpagePreferences = webpagePreferences webView = WKWebView(frame: view.bounds, configuration: config) view.addSubview(webView) if let url = URL(string: "https://www.apple.com") { webView.load(URLRequest(url: url)) } } }

Slide 84

Slide 84 text

JavaScriptͷ੍ݶͱ؅ཧ • ͲͷΑ͏ͳϦεΫ͕͋Δ͔ • ѱҙͷ͋Δίʔυ࣮ߦ • ΫϩεαΠτεΫϦϓςΟϯά (XSS) ߈ܸͳͲͷϦεΫ͕ߴ·Δ • ηΩϡϦςΟϙϦγʔ΍ݕࠪΛ௨ͯ͡ɺ҆શͰ͋Δ͜ͱ͕֬ೝ͞Εͨ ίʔυͷΈΛڐՄ͢Δ͜ͱ͕ॏཁ • ৘ใ࿙Ӯͷ๷ࢭ • ֎෦ͷεΫϦϓτ͕ϢʔβʔͷσʔλʹΞΫηε͢ΔͷΛ๷͙͜ͱ͕Ͱ͖ Δ • ಛʹɺϢʔβʔͷϓϥΠόγʔʹؔΘΔ৘ใؚ͕·ΕΔ৔߹ɺॏཁ

Slide 85

Slide 85 text

ϓϥΠόγʔอޢͱηΩϡϦςΟ • ϓϥΠόγʔͷอޢ • WebαΠτͷσʔλ؅ཧ • ϩʔΧϧετϨʔδͷ؅ཧ • ηΩϡϦςΟ • JavaScriptͷ੍ݶͱ؅ཧ • App Transport Security (ATS) ͷઃఆ

Slide 86

Slide 86 text

App Transport Security (ATS) ͷઃఆ • App Transport Security (ATS) ͱ͸ • iOSΞϓϦέʔγϣϯ͕ηΩϡΞͳ௨৴Λߦ͏ͨΊͷηΩϡϦςΟػೳ • ATS͸ɺHTTP௨৴ͷηΩϡϦςΟΛ޲্ͤ͞ΔͨΊʹɺHTTPSΛڧ੍͢Δ • = HTTPʹ͸ΞΫηεͰ͖ͳ͍ • ಛఆͷHTTPΛར༻͍ͯ͠ΔυϝΠϯʹରͯ͠ATSͷ੍ݶΛ؇࿨͢Δʹ͸ɺ Info.plistʹྫ֎ͷઃఆ͕ඞཁ

Slide 87

Slide 87 text

App Transport Security (ATS) ͷઃఆ • App Transport Security (ATS) ͷྫ֎ͷઃఆ

Slide 88

Slide 88 text

App Transport Security (ATS) ͷઃఆ NSAppTransportSecurity NSExceptionDomains example.com NSIncludesSubdomains NSExceptionAllowsInsecureHTTPLoads • App Transport Security (ATS) ͷྫ֎ͷઃఆ

Slide 89

Slide 89 text

App Transport Security (ATS) ͷઃఆ NSAppTransportSecurity NSExceptionDomains example.com NSIncludesSubdomains NSExceptionAllowsInsecureHTTPLoads • App Transport Security (ATS) ͷྫ֎ͷઃఆ

Slide 90

Slide 90 text

App Transport Security (ATS) ͷઃఆ NSAppTransportSecurity NSExceptionDomains example.com NSIncludesSubdomains NSExceptionAllowsInsecureHTTPLoads • App Transport Security (ATS) ͷྫ֎ͷઃఆ

Slide 91

Slide 91 text

NSAppTransportSecurity NSExceptionDomains example.com NSIncludesSubdomains NSExceptionAllowsInsecureHTTPLoads App Transport Security (ATS) ͷઃఆ • App Transport Security (ATS) ͷྫ֎ͷઃఆ

Slide 92

Slide 92 text

ϓϥΠόγʔอޢͱηΩϡϦςΟ • ϓϥΠόγʔͷอޢ • WebαΠτͷσʔλ؅ཧ • ϩʔΧϧετϨʔδͷ؅ཧ • ηΩϡϦςΟ • JavaScriptͷ੍ݶͱ؅ཧ • App Transport Security (ATS) ͷઃఆ

Slide 93

Slide 93 text

1. ͸͡Ίʹ 2. WKWebViewͷجૅ஌ࣝ 3. ΠϯλϥΫςΟϒͳػೳͷ࣮૷ 4. ϓϥΠόγʔอޢͱηΩϡϦςΟ 5. ߴ౓ͳઃఆͱΧελϚΠζ 6. σόοά 7. Ԡ༻ͱ࣮ફྫ 8. ·ͱΊ Agenda

Slide 94

Slide 94 text

ߴ౓ͳઃఆͱΧελϚΠζ

Slide 95

Slide 95 text

ߴ౓ͳઃఆͱΧελϚΠζ • WKWebViewCon f igurationͷΧελϚΠζ • ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • σʔλετΞͷΧελϚΠζ • ϓϦϑΝϨϯεͷઃఆ • UIΧελϚΠζͱφϏήʔγϣϯ؅ཧ • WKUIDelegate ͷ࣮૷ • WKNavigationDelegate ͷ࣮૷ • ΧελϜφϏήʔγϣϯόʔͷ౷߹ • ͦͷଞͷ֦ு • ΧελϜURLεΩʔϚͷରԠ • ίϯςϯπϑΟϧλϦϯά

Slide 96

Slide 96 text

ߴ౓ͳઃఆͱΧελϚΠζ • WKWebViewCon f igurationͷΧελϚΠζ • ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • σʔλετΞͷΧελϚΠζ • ϓϦϑΝϨϯεͷઃఆ • UIΧελϚΠζͱφϏήʔγϣϯ؅ཧ • WKUIDelegate ͷ࣮૷ • WKNavigationDelegate ͷ࣮૷ • ΧελϜφϏήʔγϣϯόʔͷ౷߹ • ͦͷଞͷ֦ு • ΧελϜURLεΩʔϚͷରԠ • ίϯςϯπϑΟϧλϦϯά

Slide 97

Slide 97 text

ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • ϓϩηεϓʔϧ • WKWebViewΠϯελϯεؒͰڞ༗͢ΔWebίϯςϯπͷϓϩηεΛ؅ཧ ͢ΔͨΊͷ࢓૊Έ • WebίϯςϯπΛϨϯμϦϯά͢ΔͨΊʹɺΞϓϦέʔγϣϯͷϝΠϯ ϓϩηεͱ͸ผͷϓϩηεʢαϯυϘοΫεԽ͞ΕͨWebίϯςϯπϓ ϩηεʣΛ࢖༻ • ϓϩηεϓʔϧΛڞ༗͢Δ͔෼཭͢Δ͔͸ɺηΩϡϦςΟ΍ύϑΥʔϚϯε ʹେ͖ͳӨڹΛ༩͑Δॏཁͳઃఆ

Slide 98

Slide 98 text

ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • ڞ༗ • Ωϟογϡ΍CookieͳͲͷσʔλ͕ෳ਺ͷWKWebViewؒͰڞ༗ • ϝϞϦ࢖༻ྔ͕ݮΓɺύϑΥʔϚϯε͕޲্

Slide 99

Slide 99 text

ϓϩηεϓʔϧͷڞ༗ͱ෼཭ // 1. ڞ௨Ͱར༻͢ΔϓϩηεϓʔϧΠϯελϯεΛ४උ let sharedProcessPool = WKProcessPool() • ڞ༗ • Ωϟογϡ΍CookieͳͲͷσʔλ͕ෳ਺ͷWKWebViewؒͰڞ༗ • ϝϞϦ࢖༻ྔ͕ݮΓɺύϑΥʔϚϯε͕޲্

Slide 100

Slide 100 text

ϓϩηεϓʔϧͷڞ༗ͱ෼཭ // 1. ڞ௨Ͱར༻͢ΔϓϩηεϓʔϧΠϯελϯεΛ४උ let sharedProcessPool = WKProcessPool() // 2. ڞ௨ͷϓϩηεϓʔϧΛ࢖༻ͯ͠ WKWebViewConfiguration Λઃఆ let configuration = WKWebViewConfiguration() configuration.processPool = sharedProcessPool • ڞ༗ • Ωϟογϡ΍CookieͳͲͷσʔλ͕ෳ਺ͷWKWebViewؒͰڞ༗ • ϝϞϦ࢖༻ྔ͕ݮΓɺύϑΥʔϚϯε͕޲্

Slide 101

Slide 101 text

ϓϩηεϓʔϧͷڞ༗ͱ෼཭ // 1. ڞ௨Ͱར༻͢ΔϓϩηεϓʔϧΠϯελϯεΛ४උ let sharedProcessPool = WKProcessPool() // 2. ڞ௨ͷϓϩηεϓʔϧΛ࢖༻ͯ͠ WKWebViewConfiguration Λઃఆ let configuration = WKWebViewConfiguration() configuration.processPool = sharedProcessPool // 3. ڞ௨ͷϓϩηεϓʔϧΛ࣋ͭ WKWebView ΠϯελϯεΛͦΕͧΕ࡞੒ let webView1 = WKWebView(frame: .zero, configuration: configuration) let webView2 = WKWebView(frame: .zero, configuration: configuration) • ڞ༗ • Ωϟογϡ΍CookieͳͲͷσʔλ͕ෳ਺ͷWKWebViewؒͰڞ༗ • ϝϞϦ࢖༻ྔ͕ݮΓɺύϑΥʔϚϯε͕޲্

Slide 102

Slide 102 text

ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • ෼཭ • ҟͳΔWKWebViewؒͰΩϟογϡ΍CookieͳͲͷσʔλΛִ཭Ͱ͖Δ • ηΩϡϦςΟ͕޲্

Slide 103

Slide 103 text

ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • ෼཭ • ҟͳΔWKWebViewؒͰΩϟογϡ΍CookieͳͲͷσʔλΛִ཭Ͱ͖Δ • ηΩϡϦςΟ͕޲্ // 1. ϓϩηεϓʔϧΛҟͳΔΠϯελϯεͰ࡞੒ let processPool1 = WKProcessPool() let processPool2 = WKProcessPool()

Slide 104

Slide 104 text

ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • ෼཭ • ҟͳΔWKWebViewؒͰΩϟογϡ΍CookieͳͲͷσʔλΛִ཭Ͱ͖Δ • ηΩϡϦςΟ͕޲্ // 1. ϓϩηεϓʔϧΛҟͳΔΠϯελϯεͰ࡞੒ let processPool1 = WKProcessPool() let processPool2 = WKProcessPool() // 2. ֤ϓϩηεϓʔϧΛ࢖༻ͯ͠ WKWebViewConfiguration Λઃఆ let configuration1 = WKWebViewConfiguration() configuration1.processPool = processPool1 let configuration2 = WKWebViewConfiguration() configuration2.processPool = processPool2

Slide 105

Slide 105 text

ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • ෼཭ • ҟͳΔWKWebViewؒͰΩϟογϡ΍CookieͳͲͷσʔλΛִ཭Ͱ͖Δ • ηΩϡϦςΟ͕޲্ // 1. ϓϩηεϓʔϧΛҟͳΔΠϯελϯεͰ࡞੒ let processPool1 = WKProcessPool() let processPool2 = WKProcessPool() // 2. ֤ϓϩηεϓʔϧΛ࢖༻ͯ͠ WKWebViewConfiguration Λઃఆ let configuration1 = WKWebViewConfiguration() configuration1.processPool = processPool1 let configuration2 = WKWebViewConfiguration() configuration2.processPool = processPool2 // 3. ҟͳΔϓϩηεϓʔϧΛ࣋ͭ WKWebView ΠϯελϯεΛ࡞੒ let webView1 = WKWebView(frame: .zero, configuration: configuration1) let webView2 = WKWebView(frame: .zero, configuration: configuration2)

Slide 106

Slide 106 text

ߴ౓ͳઃఆͱΧελϚΠζ • WKWebViewCon f igurationͷΧελϚΠζ • ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • σʔλετΞͷΧελϚΠζ • ϓϦϑΝϨϯεͷઃఆ • UIΧελϚΠζͱφϏήʔγϣϯ؅ཧ • WKUIDelegate ͷ࣮૷ • WKNavigationDelegate ͷ࣮૷ • ΧελϜφϏήʔγϣϯόʔͷ౷߹ • ͦͷଞͷ֦ு • ΧελϜURLεΩʔϚͷରԠ • ίϯςϯπϑΟϧλϦϯά

Slide 107

Slide 107 text

σʔλετΞͷΧελϚΠζ • WKWebViewCon f igurationͷwebsiteDataStoreϓϩύςΟΛར༻ • WKWebsiteDataStore • WebαΠτ͔ΒͷσʔλʢΫοΩʔɺΩϟογϡͳͲʣΛอଘ͠ɺ؅ཧ͢ ΔͨΊͷΫϥε • WKWebViewͷΠϯελϯεؒͰσʔλΛڞ༗ͨ͠ΓɺಛఆͷWebίϯς ϯπʹؔ࿈͢ΔσʔλͷΫϦʔϯΞοϓΛߦ͏͜ͱ͕Ͱ͖Δ

Slide 108

Slide 108 text

WKWebsiteDataStore • σϑΥϧτετϨʔδ • WKWebsiteDataStoreͷσϑΥϧτͷΠϯελϯεΛऔಘͰ͖Δ • ΞϓϦશମͰڞ௨ͷWebσʔλΛ؅ཧ // WKWebViewConfigurationΛ࡞੒͠ɺσϑΥϧτͷσʔλετϨʔδΛઃఆ͢Δ let configuration = WKWebViewConfiguration() configuration.websiteDataStore = WKWebsiteDataStore.default() // ઃఆΛద༻ͯ͠WKWebViewͷΠϯελϯεΛੜ੒͢Δ let webView = WKWebView(frame: .zero, configuration: configuration)

Slide 109

Slide 109 text

WKWebsiteDataStore • σϑΥϧτετϨʔδ • WKWebsiteDataStoreͷσϑΥϧτͷΠϯελϯεΛऔಘͰ͖Δ • ΞϓϦશମͰڞ௨ͷWebσʔλΛ؅ཧ // WKWebViewConfigurationΛ࡞੒͠ɺσϑΥϧτͷσʔλετϨʔδΛઃఆ͢Δ let configuration = WKWebViewConfiguration() configuration.websiteDataStore = WKWebsiteDataStore.default() // ઃఆΛద༻ͯ͠WKWebViewͷΠϯελϯεΛੜ੒͢Δ let webView = WKWebView(frame: .zero, configuration: configuration)

Slide 110

Slide 110 text

WKWebsiteDataStore • ඇӬଓతͳσʔλετϨʔδʢ㲈 ϓϥΠϕʔτϒϥ΢ζϞʔυʣ • Ұ࣌తͳσʔλͷอଘ͕ඞཁͳ৔߹΍ɺηογϣϯதͷΈσʔλΛอ࣋ ͠ɺΞϓϦ͕ऴྃ͢ΔͱσʔλΛ࡟আ͍ͨ͠৔߹ʹ࢖༻ // ඇӬଓతͳσʔλετϨʔδɿ let configuration = WKWebViewConfiguration() configuration.websiteDataStore = WKWebsiteDataStore.nonPersistent() // ઃఆΛద༻ͯ͠WKWebViewͷΠϯελϯεΛੜ੒͢Δ let webView = WKWebView(frame: .zero, configuration: configuration)

Slide 111

Slide 111 text

WKWebsiteDataStore • ඇӬଓతͳσʔλετϨʔδʢ㲈 ϓϥΠϕʔτϒϥ΢ζϞʔυʣ • Ұ࣌తͳσʔλͷอଘ͕ඞཁͳ৔߹΍ɺηογϣϯதͷΈσʔλΛอ࣋ ͠ɺΞϓϦ͕ऴྃ͢ΔͱσʔλΛ࡟আ͍ͨ͠৔߹ʹ࢖༻ // ඇӬଓతͳσʔλετϨʔδɿ let configuration = WKWebViewConfiguration() configuration.websiteDataStore = WKWebsiteDataStore.nonPersistent() // ઃఆΛద༻ͯ͠WKWebViewͷΠϯελϯεΛੜ੒͢Δ let webView = WKWebView(frame: .zero, configuration: configuration)

Slide 112

Slide 112 text

WKWebsiteDataStore • ΧελϜετϨʔδ • ΞϓϦέʔγϣϯ಺ͰҟͳΔWKWebViewΠϯελϯεؒͰσʔλΛڞ༗͠ ͍ͨ৔߹ʹ࢖༻ // ΧελϜετϨʔδɿ let identifier = UUID() let customDataStore = WKWebsiteDataStore(forIdentifier: identifier) configuration.websiteDataStore = customDataStore // ઃఆΛద༻ͯ͠WKWebViewͷΠϯελϯεΛੜ੒͢Δ let webView = WKWebView(frame: .zero, configuration: configuration)

Slide 113

Slide 113 text

WKWebsiteDataStore • ΧελϜετϨʔδ • ΞϓϦέʔγϣϯ಺ͰҟͳΔWKWebViewΠϯελϯεؒͰσʔλΛڞ༗͠ ͍ͨ৔߹ʹ࢖༻ // ΧελϜετϨʔδɿ let identifier = UUID() let customDataStore = WKWebsiteDataStore(forIdentifier: identifier) configuration.websiteDataStore = customDataStore // ઃఆΛద༻ͯ͠WKWebViewͷΠϯελϯεΛੜ੒͢Δ let webView = WKWebView(frame: .zero, configuration: configuration)

Slide 114

Slide 114 text

WKWebsiteDataStore • ΫοΩʔͷ؅ཧ • WKHTTPCookieStore Λར༻ • `dataStore.httpCookieStore` ͷΑ͏ʹΞΫηε

Slide 115

Slide 115 text

ΫοΩʔͷऔಘ // WKWebsiteDataStore ͷΠϯελϯεΛऔಘ let dataStore = WKWebsiteDataStore.default()

Slide 116

Slide 116 text

ΫοΩʔͷऔಘ // WKWebsiteDataStore ͷΠϯελϯεΛऔಘ let dataStore = WKWebsiteDataStore.default() // શͯͷΫοΩʔΛऔಘ let cookies = await dataStore.httpCookieStore.allCookies() for cookie in cookies { print("Fetched Cookie: \(cookie)") }

Slide 117

Slide 117 text

ΫοΩʔͷ࡟আ // WKWebsiteDataStore ͷΠϯελϯεΛऔಘ let dataStore = WKWebsiteDataStore.default() // શͯͷΫοΩʔΛ࡟আ let cookies = await dataStore.httpCookieStore.allCookies() for cookie in cookies { await dataStore.httpCookieStore.deleteCookie(cookie) print("Cookie deleted: \(cookie)") }

Slide 118

Slide 118 text

৽͍͠ΫοΩʔͷઃఆ // WKWebsiteDataStore ͷΠϯελϯεΛऔಘ let dataStore = WKWebsiteDataStore.default() // ৽͍͠ΫοΩʔΛઃఆ let cookie = HTTPCookie(properties: [ .domain: "example.com", .path: "/", .name: "sessionCookie", .value: "12345", .secure: "TRUE", .expires: NSDate(timeIntervalSinceNow: 3600) ]) if let cookie { await dataStore.httpCookieStore.setCookie(cookie) print("Cookie set: \(cookie)") }

Slide 119

Slide 119 text

৽͍͠ΫοΩʔͷઃఆ // WKWebsiteDataStore ͷΠϯελϯεΛऔಘ let dataStore = WKWebsiteDataStore.default() // ৽͍͠ΫοΩʔΛઃఆ let cookie = HTTPCookie(properties: [ .domain: "example.com", .path: "/", .name: "sessionCookie", .value: "12345", .secure: "TRUE", .expires: NSDate(timeIntervalSinceNow: 3600) ]) if let cookie { await dataStore.httpCookieStore.setCookie(cookie) print("Cookie set: \(cookie)") }

Slide 120

Slide 120 text

ΫοΩʔϙϦγʔͷઃఆ (iOS17) // WKWebsiteDataStore ͷΠϯελϯεΛऔಘ let dataStore = WKWebsiteDataStore.default() // ΫοΩʔϙϦγʔͷઃఆ // - .allow: // - .disallow: await dataStore.httpCookieStore.setCookiePolicy(.allow)

Slide 121

Slide 121 text

ΫοΩʔมߋͷ؂ࢹ // WKHTTPCookieStoreObserver: ΢ΣϒϖʔδͷΫοΩʔͷมߋΛ؂ࢹ͢ΔͨΊͷDelagate // WKHTTPCookieStoreObserver ϓϩτίϧΛ࣮૷͢ΔΫϥεΛ४උ class CookieObserver: NSObject, WKHTTPCookieStoreObserver { }

Slide 122

Slide 122 text

ΫοΩʔมߋͷ؂ࢹ // WKHTTPCookieStoreObserver: ΢ΣϒϖʔδͷΫοΩʔͷมߋΛ؂ࢹ͢ΔͨΊͷDelagate // WKHTTPCookieStoreObserver ϓϩτίϧΛ࣮૷͢ΔΫϥεΛ४උ class CookieObserver: NSObject, WKHTTPCookieStoreObserver { // ΫοΩʔ͕௥Ճ͞Εͨͱ͖ʹݺ͹ΕΔϝιου func cookieStore(_ cookieStore: WKHTTPCookieStore, didAddCookies cookies: [HTTPCookie]) { for cookie in cookies { print("Added cookie: \(cookie)") } } // ΫοΩʔ͕࡟আ͞Εͨͱ͖ʹݺ͹ΕΔϝιου func cookieStore(_ cookieStore: WKHTTPCookieStore, didRemoveCookies cookies: [HTTPCookie]) { for cookie in cookies { print("Removed cookie: \(cookie)") } } }

Slide 123

Slide 123 text

ΫοΩʔมߋͷ؂ࢹ // WKHTTPCookieStoreObserver: ΢ΣϒϖʔδͷΫοΩʔͷมߋΛ؂ࢹ͢ΔͨΊͷDelagate // WKHTTPCookieStoreObserver ϓϩτίϧΛ࣮૷͢ΔΫϥεΛ४උ class CookieObserver: NSObject, WKHTTPCookieStoreObserver { // ΫοΩʔ͕௥Ճ͞Εͨͱ͖ʹݺ͹ΕΔϝιου func cookieStore(_ cookieStore: WKHTTPCookieStore, didAddCookies cookies: [HTTPCookie]) { for cookie in cookies { print("Added cookie: \(cookie)") } } // ΫοΩʔ͕࡟আ͞Εͨͱ͖ʹݺ͹ΕΔϝιου func cookieStore(_ cookieStore: WKHTTPCookieStore, didRemoveCookies cookies: [HTTPCookie]) { for cookie in cookies { print("Removed cookie: \(cookie)") } } }

Slide 124

Slide 124 text

ΫοΩʔมߋͷ؂ࢹ class CookieObserver: NSObject, WKHTTPCookieStoreObserver { func cookieStore(_ cookieStore: WKHTTPCookieStore, didAddCookies cookies: [HTTPCookie]) { for cookie in cookies { print("Added cookie: \(cookie)") } } func cookieStore(_ cookieStore: WKHTTPCookieStore, didRemoveCookies cookies: [HTTPCookie]) { for cookie in cookies { print("Removed cookie: \(cookie)") } } } // WKWebsiteDataStore ͷΠϯελϯεΛऔಘ let dataStore = WKWebsiteDataStore.default() let cookieStore = dataStore.httpCookieStore // CookieObserver Λ WKHTTPCookieStore ʹ௥Ճ let observer = CookieObserver() cookieStore.add(observer)

Slide 125

Slide 125 text

WKWebsiteDataStore • Ωϟογϡͷ؅ཧ • WKWebsiteDataStoreΛར༻

Slide 126

Slide 126 text

Ωϟογϡͷ࡟আ // WKWebsiteDataStore ͷΠϯελϯεΛऔಘ let dataStore = WKWebsiteDataStore.default()

Slide 127

Slide 127 text

Ωϟογϡͷ࡟আ // WKWebsiteDataStore ͷΠϯελϯεΛऔಘ let dataStore = WKWebsiteDataStore.default() // ࡟আର৅ͷσʔλλΠϓΛࢦఆ(Ұྫ) let dataTypes: Set = [ WKWebsiteDataTypeMemoryCache, // ϝϞϦΩϟογϡ WKWebsiteDataTypeDiskCache, // σΟεΫΩϟογϡ WKWebsiteDataTypeOfflineWebApplicationCache // ΦϑϥΠϯWebΞϓϦέʔγϣϯΩϟογϡ ]

Slide 128

Slide 128 text

Ωϟογϡͷ࡟আ // WKWebsiteDataStore ͷΠϯελϯεΛऔಘ let dataStore = WKWebsiteDataStore.default() // ࡟আର৅ͷσʔλλΠϓΛࢦఆ(Ұྫ) let dataTypes: Set = [ WKWebsiteDataTypeMemoryCache, // ϝϞϦΩϟογϡ WKWebsiteDataTypeDiskCache, // σΟεΫΩϟογϡ WKWebsiteDataTypeOfflineWebApplicationCache // ΦϑϥΠϯWebΞϓϦέʔγϣϯΩϟογϡ ]) // σʔλϨίʔυΛऔಘ let records = await dataStore.dataRecords(ofTypes: dataTypes)

Slide 129

Slide 129 text

Ωϟογϡͷ࡟আ // WKWebsiteDataStore ͷΠϯελϯεΛऔಘ let dataStore = WKWebsiteDataStore.default() // ࡟আର৅ͷσʔλλΠϓΛࢦఆ(Ұྫ) let dataTypes: Set = [ WKWebsiteDataTypeMemoryCache, // ϝϞϦΩϟογϡ WKWebsiteDataTypeDiskCache, // σΟεΫΩϟογϡ WKWebsiteDataTypeOfflineWebApplicationCache // ΦϑϥΠϯWebΞϓϦέʔγϣϯΩϟογϡ ]) // σʔλϨίʔυΛऔಘ let records = await dataStore.dataRecords(ofTypes: dataTypes) // ֤σʔλϨίʔυΛ࡟আ for record in records { await dataStore.removeData(ofTypes: dataTypes, for: [record]) print("Ωϟογϡ͕࡟আ͞Ε·ͨ͠ɻ") }

Slide 130

Slide 130 text

WKWebsiteDataRecordҰཡ • Cookie Types • WKWebsiteDataTypeCookies: ΫοΩʔ • Cache Types • WKWebsiteDataTypeMemoryCache: ϝϞϦ಺Ωϟογϡ • WKWebsiteDataTypeDiskCache: σΟεΫ্ͷΩϟογϡ • WKWebsiteDataTypeO ff lineWebApplicationCache: HTMLΦϑϥΠϯWebΞϓϦέʔγϣϯΩϟογϡ • Storage Types • WKWebsiteDataTypeLocalStorage: HTML ϩʔΧϧετϨʔδ • WKWebsiteDataTypeSessionStorage: HTML ηογϣϯετϨʔδ • Database Types • WKWebsiteDataTypeWebSQLDatabases: WebSQL σʔλϕʔε • WKWebsiteDataTypeIndexedDBDatabases: IndexedDB σʔλϕʔε

Slide 131

Slide 131 text

HTTPϦΫΤετ͝ͱʹΩϟογϡϙϦγʔΛઃఆ • WKWebView ͷσϑΥϧτͷΩϟογϡϙϦγʔ͸มߋͰ͖ͳ͍ • ҰํͰɺϦΫΤετ͝ͱʹΩϟογϡϙϦγʔ͸ઃఆͰ͖Δ

Slide 132

Slide 132 text

HTTPϦΫΤετ͝ͱʹΩϟογϡϙϦγʔΛઃఆ let webView = WKWebView(frame: .zero) let urlRequest = URLRequest( url: URL(string: “https://www.apple.com")! ) webView.load(urlRequest)

Slide 133

Slide 133 text

HTTPϦΫΤετ͝ͱʹΩϟογϡϙϦγʔΛઃఆ let webView = WKWebView(frame: .zero) let urlRequest = URLRequest( url: URL(string: “https://www.apple.com")!, cachePolicy: .reloadIgnoringLocalCacheData, // ϦΫΤετ͝ͱͷΩϟογϡϙϦγʔͷઃఆ ) webView.load(urlRequest)

Slide 134

Slide 134 text

ΩϟογϡϙϦγʔҰཡ • useProtocolCachePolicy: ॳظ஋ / αʔόʔ͕ఏڙ͢ΔΩϟογϡ੍ޚϔομʔʹج͍ͮͯద༻ • reloadIgnoringLocalCacheData: ϩʔΧϧΩϟογϡΛແࢹ • reloadIgnoringLocalAndRemoteCacheData: ϩʔΧϧ͓ΑͼϦϞʔτΩϟογϡΛແࢹ • returnCacheDataElseLoad: Ωϟογϡ͕͋Ε͹ͦΕΛ࢖༻͠ɺͳ͚Ε͹ωοτϫʔΫ͔Βऔಘ • returnCacheDataDontLoad: Ωϟογϡ͕͋Ε͹ͦΕΛ࢖༻͠ɺͳ͚Ε͹ϦΫΤετ͠ͳ͍ • reloadRevalidatingCacheData: ΩϟογϡΛ࠶ݕূ͠ɺඞཁʹԠͯ͡ωοτϫʔΫ͔Βऔಘ

Slide 135

Slide 135 text

ΫοΩʔͱΩϟογϡͷؔ܎ • ΫοΩʔ • ΢ΣϒαΠτͷઃఆ΍ηογϣϯ৘ใΛอଘ͢ΔͨΊͷ΋ͷ • WKHTTPCookieStore Ͱ؅ཧ • Ωϟογϡ • ΢ΣϒαΠτͷσʔλΛอଘͯ͠ύϑΥʔϚϯεΛ޲্ͤ͞Δ΋ͷ WKWebsiteDataStore Ͱ؅ཧ • ΫοΩʔΛ࡟আͯ͠΋Ωϟογϡ΍ϩʔΧϧετϨʔδ͸ӨڹΛड͚ͳ͍ • ΩϟογϡΛ࡟আͯ͠΋ΫοΩʔ΍ϩʔΧϧετϨʔδ͸ӨڹΛड͚ͳ͍

Slide 136

Slide 136 text

ߴ౓ͳઃఆͱΧελϚΠζ • WKWebViewCon f igurationͷΧελϚΠζ • ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • σʔλετΞͷΧελϚΠζ • ϓϦϑΝϨϯεͷઃఆ • UIΧελϚΠζͱφϏήʔγϣϯ؅ཧ • WKUIDelegate ͷ࣮૷ • WKNavigationDelegate ͷ࣮૷ • ΧελϜφϏήʔγϣϯόʔͷ౷߹ • ͦͷଞͷ֦ு • ΧελϜURLεΩʔϚͷରԠ • ίϯςϯπϑΟϧλϦϯά

Slide 137

Slide 137 text

ϓϦϑΝϨϯεͷઃఆ • WKWebpagePreferences • WKWebView ͷઃఆΦϓγϣϯΛ؅ཧ͢ΔͨΊͷΫϥε

Slide 138

Slide 138 text

WKWebpagePreferences Ұཡᶃ let preferences = WKPreferences() // ΢ΣϒίϯςϯπͰ࢖༻͞ΕΔ࠷খϑΥϯταΠζΛϙΠϯτ୯ҐͰࢦఆɻσϑΥϧτ஋͸ 0 preferences.minimumFontSize = 16.0 // JavaScript ͕Ϣʔβʔͷૢ࡞ͳ͠ʹ৽͍͠΢Οϯυ΢Λ։͚Δ͔Ͳ͏͔ΛࢦఆɻσϑΥϧτ஋͸ iOS Ͱ͸ false preferences.javaScriptCanOpenWindowsAutomatically = true // ࠮ٗతͳ΢ΣϒαΠτʹରͯ͠ܯࠂΛදࣔ͢Δ͔Ͳ͏͔Λࢦఆ preferences.isFraudulentWebsiteWarningEnabled = true // ΢ΣϒϖʔδΛҹ࡮͢ΔࡍʹഎܠΛؚΊΔ͔Ͳ͏͔Λࢦఆ preferences.shouldPrintBackgrounds = true

Slide 139

Slide 139 text

WKWebpagePreferences Ұཡᶃ let preferences = WKPreferences() // ΢ΣϒίϯςϯπͰ࢖༻͞ΕΔ࠷খϑΥϯταΠζΛϙΠϯτ୯ҐͰࢦఆɻσϑΥϧτ஋͸ 0 preferences.minimumFontSize = 16.0 // JavaScript ͕Ϣʔβʔͷૢ࡞ͳ͠ʹ৽͍͠΢Οϯυ΢Λ։͚Δ͔Ͳ͏͔ΛࢦఆɻσϑΥϧτ஋͸ iOS Ͱ͸ false preferences.javaScriptCanOpenWindowsAutomatically = true // ࠮ٗతͳ΢ΣϒαΠτʹରͯ͠ܯࠂΛදࣔ͢Δ͔Ͳ͏͔Λࢦఆ preferences.isFraudulentWebsiteWarningEnabled = true // ΢ΣϒϖʔδΛҹ࡮͢ΔࡍʹഎܠΛؚΊΔ͔Ͳ͏͔Λࢦఆ preferences.shouldPrintBackgrounds = true

Slide 140

Slide 140 text

WKWebpagePreferences Ұཡᶄ let preferences = WKPreferences() // ΢Σϒίϯςϯπ಺ͰͷςΩετΠϯλϥΫγϣϯʢબ୒΍ίϐʔͳͲʣ͕༗ޮ͔Ͳ͏͔Λࢦఆ preferences.isTextInteractionEnabled = false // ಛఆͷ΢ΣϒαΠτʹର͢Δޓ׵ੑ޲্ͷͨΊͷΫΠʔΫΛద༻͢Δ͔Ͳ͏͔Λࢦఆ // ΫΠʔΫʢQuirksʣɿ // ಛఆͷ΢ΣϒαΠτ΍΢ΣϒΞϓϦέʔγϣϯ͕ਖ਼ৗʹಈ࡞͢ΔΑ͏ʹɺϒϥ΢β͕ಠࣗʹద༻͢Δಈ࡞ͷௐ੔΍ಛྫͷ͜ͱ preferences.isSiteSpecificQuirksModeEnabled = true // ΢Σϒϖʔδ಺ͷཁૉΛશը໘දࣔ͢Δ͔Ͳ͏͔Λࢦఆ preferences.isElementFullscreenEnabled = true // WKWebView ͕ඇΞΫςΟϒͰϏϡʔ֊૚͔Β֎Εͨͱ͖ͷεέδϡʔϦϯάϙϦγʔΛࢦఆ // - suspend: JavaScript ͷ࣮ߦ΍ϖʔδͷϨΠΞ΢τ͕ఀࢭ // - throttle: CPU ͷ࢖༻Λ੍ݶ͠·͕͢ɺ׬શʹ͸ఀࢭ͠ͳ͍ // - none: ಛผͳεέδϡʔϦϯάಈ࡞Λద༻ͤͣɺ௨ৗ௨Γಈ࡞ preferences.inactiveSchedulingPolicy = .none

Slide 141

Slide 141 text

WKWebpagePreferences Ұཡᶄ let preferences = WKPreferences() // ΢Σϒίϯςϯπ಺ͰͷςΩετΠϯλϥΫγϣϯʢબ୒΍ίϐʔͳͲʣ͕༗ޮ͔Ͳ͏͔Λࢦఆ preferences.isTextInteractionEnabled = false // ಛఆͷ΢ΣϒαΠτʹର͢Δޓ׵ੑ޲্ͷͨΊͷΫΠʔΫΛద༻͢Δ͔Ͳ͏͔Λࢦఆ // ΫΠʔΫʢQuirksʣɿ // ಛఆͷ΢ΣϒαΠτ΍΢ΣϒΞϓϦέʔγϣϯ͕ਖ਼ৗʹಈ࡞͢ΔΑ͏ʹɺϒϥ΢β͕ಠࣗʹద༻͢Δಈ࡞ͷௐ੔΍ಛྫͷ͜ͱ preferences.isSiteSpecificQuirksModeEnabled = true // ΢Σϒϖʔδ಺ͷཁૉΛશը໘දࣔ͢Δ͔Ͳ͏͔Λࢦఆ preferences.isElementFullscreenEnabled = true // WKWebView ͕ඇΞΫςΟϒͰϏϡʔ֊૚͔Β֎Εͨͱ͖ͷεέδϡʔϦϯάϙϦγʔΛࢦఆ // - suspend: JavaScript ͷ࣮ߦ΍ϖʔδͷϨΠΞ΢τ͕ఀࢭ // - throttle: CPU ͷ࢖༻Λ੍ݶ͠·͕͢ɺ׬શʹ͸ఀࢭ͠ͳ͍ // - none: ಛผͳεέδϡʔϦϯάಈ࡞Λద༻ͤͣɺ௨ৗ௨Γಈ࡞ preferences.inactiveSchedulingPolicy = .none

Slide 142

Slide 142 text

ߴ౓ͳઃఆͱΧελϚΠζ • WKWebViewCon f igurationͷΧελϚΠζ • ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • σʔλετΞͷΧελϚΠζ • ϓϦϑΝϨϯεͷઃఆ • UIΧελϚΠζͱφϏήʔγϣϯ؅ཧ • WKUIDelegate ͷ࣮૷ • WKNavigationDelegate ͷ࣮૷ • ΧελϜφϏήʔγϣϯόʔͷ౷߹ • ͦͷଞͷ֦ு • ΧελϜURLεΩʔϚͷରԠ • ίϯςϯπϑΟϧλϦϯά

Slide 143

Slide 143 text

WKUIDelegate ͷ࣮૷ • WKUIDelegate • WKWebView ͷ UIૢ࡞ʹؔ͢ΔσϦήʔτϝιουΛఏڙ͢Δϓϩτίϧ • ΞϥʔτͳͲͷϢʔβʔΠϯλϥΫγϣϯʹؔ͢Δಛఆͷಈ࡞ΛΧελϚ ΠζͰ͖Δ • Ξϥʔτ͸WKUIDelegateͰϋϯυϦϯά͠ͳ͍ͱɺWKWebView্Ͱ͸ ͳʹ΋ಈ࡞͠ͳ͍

Slide 144

Slide 144 text

WKUIDelegate ͷ࣮૷ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() webView = WKWebView(frame: view.bounds) view.addSubview(webView) if let url = URL(string: "https://www.apple.com") { webView.load(URLRequest(url: url)) } } }

Slide 145

Slide 145 text

WKUIDelegate ͷ࣮૷ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() webView = WKWebView(frame: view.bounds) webView.uiDelegate = self view.addSubview(webView) if let url = URL(string: "https://www.apple.com") { webView.load(URLRequest(url: url)) } } } // MARK: - WKUIDelegate extension ViewController: WKUIDelegate { }

Slide 146

Slide 146 text

ओͳ WKUIDelegate ϝιου • ΞϥʔτμΠΞϩά • webView(_:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:) • ֬ೝμΠΞϩά • webView(_:runJavaScriptCon f irmPanelWithMessage:initiatedByFrame:completionHandler:) • ೖྗμΠΞϩά • webView(_:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:) • ৽͍͠΢Οϯυ΢/λϒ • webView(_:createWebViewWith:for:windowFeatures:) • ΢Οϯυ΢Ϋϩʔζ • webViewDidClose(_:)

Slide 147

Slide 147 text

ओͳ WKUIDelegate ϝιου • ΞϥʔτμΠΞϩά • webView(_:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:) • ֬ೝμΠΞϩά • webView(_:runJavaScriptCon f irmPanelWithMessage:initiatedByFrame:completionHandler:) • ೖྗμΠΞϩά • webView(_:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:) • ৽͍͠΢Οϯυ΢/λϒ • webView(_:createWebViewWith:for:windowFeatures:) • ΢Οϯυ΢Ϋϩʔζ • webViewDidClose(_:)

Slide 148

Slide 148 text

ΞϥʔτμΠΞϩά - runJavaScriptAlertPanelWithMessage - // MARK: - WKUIDelegate extension ViewController: WKUIDelegate { // λΠϛϯά: JavaScript͔Βalert()͕ݺ͹Εͨͱ͖ func webView( _ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void ) { let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in completionHandler() }) webView.window?.rootViewController?.present(alert, animated: true, completion: nil) } }

Slide 149

Slide 149 text

ΞϥʔτμΠΞϩά - runJavaScriptAlertPanelWithMessage - // MARK: - WKUIDelegate extension ViewController: WKUIDelegate { // λΠϛϯά: JavaScript͔Βalert()͕ݺ͹Εͨͱ͖ func webView( _ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void ) { let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in completionHandler() }) webView.window?.rootViewController?.present(alert, animated: true, completion: nil) } }

Slide 150

Slide 150 text

ΞϥʔτμΠΞϩά - runJavaScriptAlertPanelWithMessage - // MARK: - WKUIDelegate extension ViewController: WKUIDelegate { // λΠϛϯά: JavaScript͔Βalert()͕ݺ͹Εͨͱ͖ func webView( _ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void ) { let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in completionHandler() }) webView.window?.rootViewController?.present(alert, animated: true, completion: nil) } }

Slide 151

Slide 151 text

ओͳ WKUIDelegate ϝιου • ΞϥʔτμΠΞϩά • webView(_:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:) • ֬ೝμΠΞϩά • webView(_:runJavaScriptCon f irmPanelWithMessage:initiatedByFrame:completionHandler:) • ೖྗμΠΞϩά • webView(_:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:) • ৽͍͠΢Οϯυ΢/λϒ • webView(_:createWebViewWith:for:windowFeatures:) • ΢Οϯυ΢Ϋϩʔζ • webViewDidClose(_:)

Slide 152

Slide 152 text

֬ೝμΠΞϩά - runJavaScriptCon f irmPanelWithMessage - // MARK: - WKUIDelegate extension ViewController: WKUIDelegate { // λΠϛϯά: JavaScript͔Βconfirm()͕ݺ͹Εͨͱ͖ func webView( _ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void ) { let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in completionHandler(false) }) alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in completionHandler(true) }) webView.window?.rootViewController?.present(alert, animated: true, completion: nil) } }

Slide 153

Slide 153 text

֬ೝμΠΞϩά - runJavaScriptCon f irmPanelWithMessage - // MARK: - WKUIDelegate extension ViewController: WKUIDelegate { // λΠϛϯά: JavaScript͔Βconfirm()͕ݺ͹Εͨͱ͖ func webView( _ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void ) { let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in completionHandler(false) }) alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in completionHandler(true) }) webView.window?.rootViewController?.present(alert, animated: true, completion: nil) } }

Slide 154

Slide 154 text

֬ೝμΠΞϩά - runJavaScriptCon f irmPanelWithMessage - // MARK: - WKUIDelegate extension ViewController: WKUIDelegate { // λΠϛϯά: JavaScript͔Βconfirm()͕ݺ͹Εͨͱ͖ func webView( _ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void ) { let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in completionHandler(false) }) alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in completionHandler(true) }) webView.window?.rootViewController?.present(alert, animated: true, completion: nil) } }

Slide 155

Slide 155 text

ओͳ WKUIDelegate ϝιου • ΞϥʔτμΠΞϩά • webView(_:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:) • ֬ೝμΠΞϩά • webView(_:runJavaScriptCon f irmPanelWithMessage:initiatedByFrame:completionHandler:) • ೖྗμΠΞϩά • webView(_:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:) • ৽͍͠΢Οϯυ΢/λϒ • webView(_:createWebViewWith:for:windowFeatures:) • ΢Οϯυ΢Ϋϩʔζ • webViewDidClose(_:)

Slide 156

Slide 156 text

ೖྗμΠΞϩά - runJavaScriptTextInputPanelWithPrompt - // MARK: - WKUIDelegate extension ViewController: WKUIDelegate { // λΠϛϯά: JavaScript͔Βprompt()͕ݺ͹Εͨͱ͖ func webView( _ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void ) { let alert = UIAlertController(title: prompt, message: nil, preferredStyle: .alert) alert.addTextField { textField in textField.text = defaultText } alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in completionHandler(nil) }) alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in completionHandler(alert.textFields?.first?.text) }) webView.window?.rootViewController?.present(alert, animated: true, completion: nil) }

Slide 157

Slide 157 text

ೖྗμΠΞϩά - runJavaScriptTextInputPanelWithPrompt - // MARK: - WKUIDelegate extension ViewController: WKUIDelegate { // λΠϛϯά: JavaScript͔Βprompt()͕ݺ͹Εͨͱ͖ func webView( _ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void ) { let alert = UIAlertController(title: prompt, message: nil, preferredStyle: .alert) alert.addTextField { textField in textField.text = defaultText } alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in completionHandler(nil) }) alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in completionHandler(alert.textFields?.first?.text) }) webView.window?.rootViewController?.present(alert, animated: true, completion: nil) }

Slide 158

Slide 158 text

ೖྗμΠΞϩά - runJavaScriptTextInputPanelWithPrompt - // MARK: - WKUIDelegate extension ViewController: WKUIDelegate { // λΠϛϯά: JavaScript͔Βprompt()͕ݺ͹Εͨͱ͖ func webView( _ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void ) { let alert = UIAlertController(title: prompt, message: nil, preferredStyle: .alert) alert.addTextField { textField in textField.text = defaultText } alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in completionHandler(nil) }) alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in completionHandler(alert.textFields?.first?.text) }) webView.window?.rootViewController?.present(alert, animated: true, completion: nil) }

Slide 159

Slide 159 text

ೖྗμΠΞϩά - runJavaScriptTextInputPanelWithPrompt - // MARK: - WKUIDelegate extension ViewController: WKUIDelegate { // λΠϛϯά: JavaScript͔Βprompt()͕ݺ͹Εͨͱ͖ func webView( _ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void ) { let alert = UIAlertController(title: prompt, message: nil, preferredStyle: .alert) alert.addTextField { textField in textField.text = defaultText } alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in completionHandler(nil) }) alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in completionHandler(alert.textFields?.first?.text) }) webView.window?.rootViewController?.present(alert, animated: true, completion: nil) }

Slide 160

Slide 160 text

ओͳ WKUIDelegate ϝιου • ΞϥʔτμΠΞϩά • webView(_:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:) • ֬ೝμΠΞϩά • webView(_:runJavaScriptCon f irmPanelWithMessage:initiatedByFrame:completionHandler:) • ೖྗμΠΞϩά • webView(_:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:) • ৽͍͠΢Οϯυ΢/λϒ • webView(_:createWebViewWith:for:windowFeatures:) • ΢Οϯυ΢Ϋϩʔζ • webViewDidClose(_:)

Slide 161

Slide 161 text

৽͍͠΢Οϯυ΢/λϒ - createWebViewWith - // MARK: - WKUIDelegate extension ViewController: WKUIDelegate { // λΠϛϯά: ΢Σϒϖʔδ͔Β৽͍͠΢Οϯυ΢΍λϒΛ։ͨ͘Ίͷཁٻ͕͋ͬͨͱ͖ func webView( _ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures ) -> WKWebView? { return nil } }

Slide 162

Slide 162 text

৽͍͠΢Οϯυ΢/λϒ - createWebViewWith - // MARK: - WKUIDelegate extension ViewController: WKUIDelegate { // λΠϛϯά: ΢Σϒϖʔδ͔Β৽͍͠΢Οϯυ΢΍λϒΛ։ͨ͘Ίͷཁٻ͕͋ͬͨͱ͖ func webView( _ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures ) -> WKWebView? { // WKWebViewͰ͸target="_blank"͕ઃఆ͞Ε͍ͯΔϦϯΫλοϓ࣌ɺͦͷ··Ͱ͸ભҠ͠ͳ͍ return nil } }

Slide 163

Slide 163 text

৽͍͠΢Οϯυ΢/λϒ - createWebViewWith - // MARK: - WKUIDelegate extension ViewController: WKUIDelegate { // λΠϛϯά: ΢Σϒϖʔδ͔Β৽͍͠΢Οϯυ΢΍λϒΛ։ͨ͘Ίͷཁٻ͕͋ͬͨͱ͖ func webView( _ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures ) -> WKWebView? { // WKWebViewͰ͸target=“_blank"͕ઃఆ͞Ε͍ͯΔϦϯΫλοϓ࣌ɺͦͷ··Ͱ͸ભҠ͠ͳ͍ // target=“_blank” ͷͱ͖ͷॲཧΛ໌ࣔతʹهड़͢Δඞཁ͕͋Δ if navigationAction.targetFrame == nil { webView.load(navigationAction.request) } return nil } }

Slide 164

Slide 164 text

ओͳ WKUIDelegate ϝιου • ΞϥʔτμΠΞϩά • webView(_:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:) • ֬ೝμΠΞϩά • webView(_:runJavaScriptCon f irmPanelWithMessage:initiatedByFrame:completionHandler:) • ೖྗμΠΞϩά • webView(_:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:) • ৽͍͠΢Οϯυ΢/λϒ • webView(_:createWebViewWith:for:windowFeatures:) • ΢Οϯυ΢Ϋϩʔζ • webViewDidClose(_:)

Slide 165

Slide 165 text

΢Οϯυ΢Ϋϩʔζ - webViewDidClose - // MARK: - WKUIDelegate extension ViewController: WKUIDelegate { // λΠϛϯά: window.close()͕ݺ͹Εͨͱ͖ʹWebView͕ด͡ΒΕͨͱ͖ func webViewDidClose(_ webView: WKWebView) { // Handle the WebView closure, e.g., remove it from the UI } }

Slide 166

Slide 166 text

ߴ౓ͳઃఆͱΧελϚΠζ • WKWebViewCon f igurationͷΧελϚΠζ • ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • σʔλετΞͷΧελϚΠζ • ϓϦϑΝϨϯεͷઃఆ • UIΧελϚΠζͱφϏήʔγϣϯ؅ཧ • WKUIDelegate ͷ࣮૷ • WKNavigationDelegate ͷ࣮૷ • ΧελϜφϏήʔγϣϯόʔͷ౷߹ • ͦͷଞͷ֦ு • ΧελϜURLεΩʔϚͷରԠ • ίϯςϯπϑΟϧλϦϯά

Slide 167

Slide 167 text

WKNavigationDelegate ͷ࣮૷ • WKNavigationDelegate • φϏήʔγϣϯͷঢ়ଶΛ؂ࢹ͠ɺϦιʔεͷಡΈࠐΈΛ੍ޚ

Slide 168

Slide 168 text

WKNavigationDelegate ͷ࣮૷ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() webView = WKWebView(frame: view.bounds) view.addSubview(webView) if let url = URL(string: "https://www.apple.com") { webView.load(URLRequest(url: url)) } } }

Slide 169

Slide 169 text

WKNavigationDelegate ͷ࣮૷ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() webView = WKWebView(frame: view.bounds) webView.navigationDelegate = self view.addSubview(webView) if let url = URL(string: "https://www.apple.com") { webView.load(URLRequest(url: url)) } } } // MARK: - WKNavigationDelegate extension ViewController: WKNavigationDelegate { }

Slide 170

Slide 170 text

WKNavigationDelegate ϝιουҰཡ • φϏήʔγϣϯϦΫΤετͷڐՄ͓Αͼڋ൱ • webView(_:decidePolicyFor navigationAction:preferences:decisionHandler:) • webView(_:decidePolicyFor navigationResponse:decisionHandler:) // MARK: - WKNavigationDelegate extension ViewController: WKNavigationDelegate { }

Slide 171

Slide 171 text

WKNavigationDelegate ϝιουҰཡ • φϏήʔγϣϯϦΫΤετͷڐՄ͓Αͼڋ൱ • webView(_:decidePolicyFor navigationAction:preferences:decisionHandler:) • webView(_:decidePolicyFor navigationResponse:decisionHandler:) // MARK: - WKNavigationDelegate extension ViewController: WKNavigationDelegate { // φϏήʔγϣϯϦΫΤετ͕preferencesʹج͍ͮͯڐՄ·ͨ͸Ωϟϯηϧ͞ΕΔ΂͖͔Λܾఆ func webView( _ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void ) { decisionHandler(.allow, preferences) }

Slide 172

Slide 172 text

WKNavigationDelegate ϝιουҰཡ • φϏήʔγϣϯϦΫΤετͷڐՄ͓Αͼڋ൱ • webView(_:decidePolicyFor navigationAction:preferences:decisionHandler:) • webView(_:decidePolicyFor navigationResponse:decisionHandler:) // MARK: - WKNavigationDelegate extension ViewController: WKNavigationDelegate { // αʔόʔ͔ΒͷϨεϙϯεΛड͚औͬͨޙɺͦͷϨεϙϯε͕΢ΣϒϏϡʔͰͷදࣔʹద͍ͯ͠Δ͔Ͳ͏͔Λܾఆ͢Δ func webView( _ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void ) { decisionHandler(.allow) } }

Slide 173

Slide 173 text

WKNavigationDelegate ϝιουҰཡ • ίϯςϯπͷಡΈࠐΈঢ়گͷ௥੻ • webView(_:didStartProvisionalNavigation:) • webView(_:didReceiveServerRedirectForProvisionalNavigation:) • webView(_:didCommit:) • webView(_:didFinish:)

Slide 174

Slide 174 text

WKNavigationDelegate ϝιουҰཡ • φϏήʔγϣϯɾΤϥʔ΁ͷରԠ • webView(_:didFail:withError:) • webView(_:didFailProvisionalNavigation:withError:) • webViewWebContentProcessDidTerminate(_:)

Slide 175

Slide 175 text

WKNavigationDelegate ϝιουҰཡ • ೝূνϟϨϯδ΁ͷରԠ • webView(_:didReceive:completionHandler:) • webView(_:authenticationChallenge:shouldAllowDeprecatedTLS:)

Slide 176

Slide 176 text

WKNavigationDelegate ϝιουҰཡ • μ΢ϯϩʔυͷਐߦঢ়گ • webView(_:navigationAction:didBecome:) • webView(_:navigationAction:didBecome:)

Slide 177

Slide 177 text

URLRequestΛloadͨ͠ޙͷϝιουݺͼग़͠ͷॱংʢ௨ৗ࣌ʣ 1. load(_:) 2. decidePolicyFor navigationAction: φϏήʔγϣϯϑϩʔͷ։࢝ɻφϏήʔτΛڐՄ 3. didStartProvisionalNavigation: φϏήʔγϣϯ։࢝Λݕ஌ 4. αʔόʔ͔ΒͷϨεϙϯε଴ػ 5. decidePolicyFor navigationResponse: αʔόʔ͔ΒͷϨεϙϯεΛड৴ɻίϯςϯπͷಡΈࠐΈΛڐՄ 6. didReceiveServerRedirectForProvisionalNavigation:ʢϦμΠϨΫτ͕͋Ε͹ʣ 7. didCommit: ίϯςϯπͷಡΈࠐΈ։࢝ 8. didFinish: φϏήʔγϣϯ͕׬ྃ͠ɺϖʔδͷಡΈࠐΈ͕ऴྃ

Slide 178

Slide 178 text

URLRequestΛloadͨ͠ޙͷϝιουݺͼग़͠ͷॱংʢ௨ৗ࣌ʣ 1. load(_:) 2. decidePolicyFor navigationAction: φϏήʔγϣϯϑϩʔͷ։࢝ɻφϏήʔτΛڐՄ 3. didStartProvisionalNavigation: φϏήʔγϣϯ։࢝Λݕ஌ 4. αʔόʔ͔ΒͷϨεϙϯε଴ػ 5. decidePolicyFor navigationResponse: αʔόʔ͔ΒͷϨεϙϯεΛड৴ɻίϯςϯπͷಡΈࠐΈΛڐՄ 6. didReceiveServerRedirectForProvisionalNavigation:ʢϦμΠϨΫτ͕͋Ε͹ʣ 7. didCommit: ίϯςϯπͷಡΈࠐΈ։࢝ 8. didFinish: φϏήʔγϣϯ͕׬ྃ͠ɺϖʔδͷಡΈࠐΈ͕ऴྃ

Slide 179

Slide 179 text

URLRequestΛloadͨ͠ޙͷϝιουݺͼग़͠ͷॱংʢ௨ৗ࣌ʣ 1. load(_:) 2. decidePolicyFor navigationAction: φϏήʔγϣϯϑϩʔͷ։࢝ɻφϏήʔτΛڐՄ 3. didStartProvisionalNavigation: φϏήʔγϣϯ։࢝Λݕ஌ 4. αʔόʔ͔ΒͷϨεϙϯε଴ػ 5. decidePolicyFor navigationResponse: αʔόʔ͔ΒͷϨεϙϯεΛड৴ɻίϯςϯπͷಡΈࠐΈΛڐՄ 6. didReceiveServerRedirectForProvisionalNavigation:ʢϦμΠϨΫτ͕͋Ε͹ʣ 7. didCommit: ίϯςϯπͷಡΈࠐΈ։࢝ 8. didFinish: φϏήʔγϣϯ͕׬ྃ͠ɺϖʔδͷಡΈࠐΈ͕ऴྃ

Slide 180

Slide 180 text

URLRequestΛloadͨ͠ޙͷϝιουݺͼग़͠ͷॱংʢ௨ৗ࣌ʣ 1. load(_:) 2. decidePolicyFor navigationAction: φϏήʔγϣϯϑϩʔͷ։࢝ɻφϏήʔτΛڐՄ 3. didStartProvisionalNavigation: φϏήʔγϣϯ։࢝Λݕ஌ 4. αʔόʔ͔ΒͷϨεϙϯε଴ػ 5. decidePolicyFor navigationResponse: αʔόʔ͔ΒͷϨεϙϯεΛड৴ɻίϯςϯπͷಡΈࠐΈΛڐՄ 6. didReceiveServerRedirectForProvisionalNavigation:ʢϦμΠϨΫτ͕͋Ε͹ʣ 7. didCommit: ίϯςϯπͷಡΈࠐΈ։࢝ 8. didFinish: φϏήʔγϣϯ͕׬ྃ͠ɺϖʔδͷಡΈࠐΈ͕ऴྃ

Slide 181

Slide 181 text

URLRequestΛloadͨ͠ޙͷϝιουݺͼग़͠ͷॱংʢ௨ৗ࣌ʣ 1. load(_:) 2. decidePolicyFor navigationAction: φϏήʔγϣϯϑϩʔͷ։࢝ɻφϏήʔτΛڐՄ 3. didStartProvisionalNavigation: φϏήʔγϣϯ։࢝Λݕ஌ 4. αʔόʔ͔ΒͷϨεϙϯε଴ػ 5. decidePolicyFor navigationResponse: αʔόʔ͔ΒͷϨεϙϯεΛड৴ɻίϯςϯπͷಡΈࠐΈΛڐՄ 6. didReceiveServerRedirectForProvisionalNavigation:ʢϦμΠϨΫτ͕͋Ε͹ʣ 7. didCommit: ίϯςϯπͷಡΈࠐΈ։࢝ 8. didFinish: φϏήʔγϣϯ͕׬ྃ͠ɺϖʔδͷಡΈࠐΈ͕ऴྃ

Slide 182

Slide 182 text

URLRequestΛloadͨ͠ޙͷϝιουݺͼग़͠ͷॱংʢ௨ৗ࣌ʣ 1. load(_:) 2. decidePolicyFor navigationAction: φϏήʔγϣϯϑϩʔͷ։࢝ɻφϏήʔτΛڐՄ 3. didStartProvisionalNavigation: φϏήʔγϣϯ։࢝Λݕ஌ 4. αʔόʔ͔ΒͷϨεϙϯε଴ػ 5. decidePolicyFor navigationResponse: αʔόʔ͔ΒͷϨεϙϯεΛड৴ɻίϯςϯπͷಡΈࠐΈΛڐՄ 6. didReceiveServerRedirectForProvisionalNavigation:ʢϦμΠϨΫτ͕͋Ε͹ʣ 7. didCommit: ίϯςϯπͷಡΈࠐΈ։࢝ 8. didFinish: φϏήʔγϣϯ͕׬ྃ͠ɺϖʔδͷಡΈࠐΈ͕ऴྃ

Slide 183

Slide 183 text

URLRequestΛloadͨ͠ޙͷϝιουݺͼग़͠ͷॱংʢ௨ৗ࣌ʣ 1. load(_:) 2. decidePolicyFor navigationAction: φϏήʔγϣϯϑϩʔͷ։࢝ɻφϏήʔτΛڐՄ 3. didStartProvisionalNavigation: φϏήʔγϣϯ։࢝Λݕ஌ 4. αʔόʔ͔ΒͷϨεϙϯε଴ػ 5. decidePolicyFor navigationResponse: αʔόʔ͔ΒͷϨεϙϯεΛड৴ɻίϯςϯπͷಡΈࠐΈΛڐՄ 6. didReceiveServerRedirectForProvisionalNavigation:ʢϦμΠϨΫτ͕͋Ε͹ʣ 7. didCommit: ίϯςϯπͷಡΈࠐΈ։࢝ 8. didFinish: φϏήʔγϣϯ͕׬ྃ͠ɺϖʔδͷಡΈࠐΈ͕ऴྃ

Slide 184

Slide 184 text

URLRequestΛloadͨ͠ޙͷϝιουݺͼग़͠ͷॱংʢ௨ৗ࣌ʣ 1. load(_:) 2. decidePolicyFor navigationAction: φϏήʔγϣϯϑϩʔͷ։࢝ɻφϏήʔτΛڐՄ 3. didStartProvisionalNavigation: φϏήʔγϣϯ։࢝Λݕ஌ 4. αʔόʔ͔ΒͷϨεϙϯε଴ػ 5. decidePolicyFor navigationResponse: αʔόʔ͔ΒͷϨεϙϯεΛड৴ɻίϯςϯπͷಡΈࠐΈΛڐՄ 6. didReceiveServerRedirectForProvisionalNavigation:ʢϦμΠϨΫτ͕͋Ε͹ʣ 7. didCommit: ίϯςϯπͷಡΈࠐΈ։࢝ 8. didFinish: φϏήʔγϣϯ͕׬ྃ͠ɺϖʔδͷಡΈࠐΈ͕ऴྃ

Slide 185

Slide 185 text

URLRequestΛloadͨ͠ޙͷϝιουݺͼग़͠ͷॱংʢ Τϥʔ࣌ᶃʣ 1. load(_:) 2. decidePolicyFor navigationAction: φϏήʔγϣϯϑϩʔͷ։࢝ɻφϏήʔτΛڐՄ 3. didStartProvisionalNavigation: φϏήʔγϣϯ։࢝Λݕ஌ 4. αʔόʔ͔ΒͷϨεϙϯε଴ػ 5. didFailProvisionalNavigation:withError: ProvisionalNavigationதͷΤϥʔ 6. didCommit: ίϯςϯπ͸ಡΈࠐ·Εͳ͍

Slide 186

Slide 186 text

URLRequestΛloadͨ͠ޙͷϝιουݺͼग़͠ͷॱংʢ Τϥʔ࣌ᶃʣ 1. load(_:) 2. decidePolicyFor navigationAction: φϏήʔγϣϯϑϩʔͷ։࢝ɻφϏήʔτΛڐՄ 3. didStartProvisionalNavigation: φϏήʔγϣϯ։࢝Λݕ஌ 4. αʔόʔ͔ΒͷϨεϙϯε଴ػ 5. didFailProvisionalNavigation:withError: ProvisionalNavigationதͷΤϥʔ 6. didCommit: ίϯςϯπ͸ಡΈࠐ·Εͳ͍

Slide 187

Slide 187 text

URLRequestΛloadͨ͠ޙͷϝιουݺͼग़͠ͷॱংʢ Τϥʔ࣌ᶄʣ 1. load(_:) 2. decidePolicyFor navigationAction: φϏήʔγϣϯϑϩʔͷ։࢝ɻφϏήʔτΛڐՄ 3. didStartProvisionalNavigation: φϏήʔγϣϯ։࢝Λݕ஌ 4. αʔόʔ͔ΒͷϨεϙϯε଴ػ 5. didCommit: ίϯςϯπͷಡΈࠐΈ։࢝ 6. didFail:withError: ίϯςϯπͷಡΈࠐΈதͷΤϥʔɻλΠϛϯάʹΑͬͯ͸ didCommit ͕ݺ͹Εͳ͍ɻ

Slide 188

Slide 188 text

URLRequestΛloadͨ͠ޙͷϝιουݺͼग़͠ͷॱংʢ Τϥʔ࣌ᶄʣ 1. load(_:) 2. decidePolicyFor navigationAction: φϏήʔγϣϯϑϩʔͷ։࢝ɻφϏήʔτΛڐՄ 3. didStartProvisionalNavigation: φϏήʔγϣϯ։࢝Λݕ஌ 4. αʔόʔ͔ΒͷϨεϙϯε଴ػ 5. didCommit: ίϯςϯπͷಡΈࠐΈ։࢝ 6. didFail:withError: ίϯςϯπͷಡΈࠐΈதͷΤϥʔɻλΠϛϯάʹΑͬͯ͸ didCommit ͕ݺ͹Εͳ͍ɻ

Slide 189

Slide 189 text

ߴ౓ͳઃఆͱΧελϚΠζ • WKWebViewCon f igurationͷΧελϚΠζ • ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • σʔλετΞͷΧελϚΠζ • ϓϦϑΝϨϯεͷઃఆ • UIΧελϚΠζͱφϏήʔγϣϯ؅ཧ • WKUIDelegate ͷ࣮૷ • WKNavigationDelegate ͷ࣮૷ • ΧελϜφϏήʔγϣϯόʔͷ౷߹ • ͦͷଞͷ֦ு • ΧελϜURLεΩʔϚͷରԠ • ίϯςϯπϑΟϧλϦϯά

Slide 190

Slide 190 text

ΧελϜφϏήʔγϣϯόʔͷ౷߹ • WKWebView Λ࢖༻͢Δ৔߹ɺඪ४ͷφϏήʔγϣϯػ ೳʢʮਐΉʯʮ໭ΔʯʮϦϩʔυʯͳͲʣ͸ࣗಈతʹ͸ ఏڙ͞Εͳ͍ • ͜ΕΒͷػೳΛ࣮૷͢ΔͨΊʹ͸ɺWKWebView ͷΠϯ ελϯεΛૢ࡞͢ΔͨΊͷΧελϜφϏήʔγϣϯϘλ ϯΛ࡞੒͢Δඞཁ͕͋Δ

Slide 191

Slide 191 text

ΧελϜφϏήʔγϣϯόʔͷ౷߹ • WKWebView Λ࢖༻͢Δ৔߹ɺඪ४ͷφϏήʔγϣϯػ ೳʢʮਐΉʯʮ໭ΔʯʮϦϩʔυʯͳͲʣ͸ࣗಈతʹ͸ ఏڙ͞Εͳ͍ • ͜ΕΒͷػೳΛ࣮૷͢ΔͨΊʹ͸ɺWKWebView ͷΠϯ ελϯεΛૢ࡞͢ΔͨΊͷΧελϜφϏήʔγϣϯϘλ ϯΛ࡞੒͢Δඞཁ͕͋Δ

Slide 192

Slide 192 text

goBack() • webView.canGoBack • ʮ໭Δʯ͜ͱ͕Ͱ͖Δ͔Ͳ͏͔ • webView.goBack() • ʮ໭Δʯૢ࡞Λ࣮ߦ @objc private func goBack() { if webView.canGoBack { webView.goBack() } }

Slide 193

Slide 193 text

goForward() • webView.canGoForward • ʮਐΉʯ͜ͱ͕Ͱ͖Δ͔Ͳ͏͔ • webView.goForward() • ʮਐΉʯૢ࡞Λ࣮ߦ @objc private func goBack() { if webView.canGoForward { webView.goForward() } }

Slide 194

Slide 194 text

reload() • webView.reload() • ʮ࠶ಡΈࠐΈʯૢ࡞Λ࣮ߦ @objc private func reload() { webView.reload() }

Slide 195

Slide 195 text

ΧελϜφϏήʔγϣϯόʔͷ౷߹ • ࠓճ͸WebView༻ͷΧελϜφϏσʔγϣϯόʔΛ࡞੒͢Δྫ͕ͩͬͨɺω ΠςΟϒଆͰར༻͍ͯ͠ΔφϏήʔγϣϯόʔΛͦͷ··ར༻͢Δͷ΋ྑ͍ • Ex: • φϏήʔγϣϯόοΫ -> webView.goBack() • Pull to Refresh -> webView.reload() • ͦͷଞɺWKWebView ͸ΧελϜͷ෯͕޿͍΋ͷͷɺجຊతʹ͸ಠࣗʹ࣮૷ Λߦ͏ඞཁ͕͋Δ

Slide 196

Slide 196 text

ߴ౓ͳઃఆͱΧελϚΠζ • WKWebViewCon f igurationͷΧελϚΠζ • ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • σʔλετΞͷΧελϚΠζ • ϓϦϑΝϨϯεͷઃఆ • UIΧελϚΠζͱφϏήʔγϣϯ؅ཧ • WKUIDelegate ͷ࣮૷ • WKNavigationDelegate ͷ࣮૷ • ΧελϜφϏήʔγϣϯόʔͷ౷߹ • ͦͷଞͷ֦ு • ΧελϜURLεΩʔϚͷରԠ • ίϯςϯπϑΟϧλϦϯά

Slide 197

Slide 197 text

ΧελϜURLεΩʔϚͷରԠ • WKURLSchemeHandler • ΞϓϦ಺Ͱಛఆͷ URL εΩʔϚΛΧελϜॲཧ͢ΔͨΊͷϓϩτίϧ

Slide 198

Slide 198 text

WKURLSchemeHandler ͷར༻ྫ • ΞηοτͷϩʔΧϧಡΈࠐΈ • ྫɿ`custom-scheme://images/logo.png` • ΞϓϦ಺ͷϦιʔε΍σʔλΛ WKWebView ಺Ͱ௚઀ѻ͏͜ͱ͕Ͱ͖Δ • σόοά৘ใͷૹ৴ • ྫɿ`custom-scheme://debug?info=someDebugInfo` • Web ίϯςϯπ͔ΒΞϓϦέʔγϣϯʹσόοά৘ใΛૹ৴

Slide 199

Slide 199 text

WKURLSchemeHandler class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() let config = WKWebViewConfiguration() webView = WKWebView(frame: self.view.bounds, configuration: config) self.view.addSubview(webView) // ΧελϜεΩʔϚͷϦιʔεΛϦΫΤετ͢Δྫ if let url = URL(string: “custom-scheme://exampleImage") { let request = URLRequest(url: url) webView.load(request) } } }

Slide 200

Slide 200 text

WKURLSchemeHandler class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() let config = WKWebViewConfiguration() // WKWebViewConfigurationͷsetURLSchemeHandler(_:forURLScheme:)ϝιουΛ࢖༻ͯ͠ొ࿥ // ͜͜Ͱ͸ “custom-scheme://" ͱ͍͏ URLScheme Λࢦఆ͍ͯ͠Δ let schemeHandler = CustomSchemeHandler() config.setURLSchemeHandler(schemeHandler, forURLScheme: “custom-scheme") webView = WKWebView(frame: self.view.bounds, configuration: config) self.view.addSubview(webView) // ΧελϜεΩʔϚͷϦιʔεΛϦΫΤετ͢Δྫ if let url = URL(string: “custom-scheme://exampleImage") { let request = URLRequest(url: url) webView.load(request) } } }

Slide 201

Slide 201 text

WKURLSchemeHandler class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() let config = WKWebViewConfiguration() // WKWebViewConfigurationͷsetURLSchemeHandler(_:forURLScheme:)ϝιουΛ࢖༻ͯ͠ొ࿥ // ͜͜Ͱ͸ “custom-scheme://" ͱ͍͏ URLScheme Λࢦఆ͍ͯ͠Δ let schemeHandler = CustomSchemeHandler() config.setURLSchemeHandler(schemeHandler, forURLScheme: “custom-scheme") webView = WKWebView(frame: self.view.bounds, configuration: config) self.view.addSubview(webView) // ΧελϜεΩʔϚͷϦιʔεΛϦΫΤετ͢Δྫ if let url = URL(string: “custom-scheme://exampleImage") { let request = URLRequest(url: url) webView.load(request) } } }

Slide 202

Slide 202 text

WKURLSchemeHandler // WKURLSchemeHandler: ΧελϜURLεΩʔϜͷϦιʔεΛಡΈࠐΉͨΊͷϓϩτίϧ class CustomSchemeHandler: NSObject, WKURLSchemeHandler { }

Slide 203

Slide 203 text

WKURLSchemeHandler // WKURLSchemeHandler: ΧελϜURLεΩʔϜͷϦιʔεΛಡΈࠐΉͨΊͷϓϩτίϧ class CustomSchemeHandler: NSObject, WKURLSchemeHandler { // URLSchemeϦΫΤετͷॲཧΛ։࢝͢Δϝιου // WKURLSchemeTask Λ௨ͯ͡ϦΫΤετʹԠ౴ func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) { } // ϦΫΤετॲཧΛఀࢭ͢Δϝιου // ͢΂ͯͷඇಉظϦΫΤετΛΩϟϯηϧ͢Δࡍʹ࢖༻ func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) { } }

Slide 204

Slide 204 text

WKURLSchemeHandler // WKURLSchemeHandler: ΧελϜURLεΩʔϜͷϦιʔεΛಡΈࠐΉͨΊͷϓϩτίϧ class CustomSchemeHandler: NSObject, WKURLSchemeHandler { // URLSchemeϦΫΤετͷॲཧΛ։࢝͢Δϝιου // WKURLSchemeTask Λ௨ͯ͡ϦΫΤετʹԠ౴ func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) { } // ϦΫΤετॲཧΛఀࢭ͢Δϝιου // ͢΂ͯͷඇಉظϦΫΤετΛΩϟϯηϧ͢Δࡍʹ࢖༻ func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) { } }

Slide 205

Slide 205 text

WKURLSchemeHandler // URLSchemeϦΫΤετͷॲཧΛ։࢝͢Δϝιου // WKURLSchemeTask Λ௨ͯ͡ϦΫΤετʹԠ౴ func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) { guard let url = urlSchemeTask.request.url else { return } // ΧελϜϓϩτίϧͷॲཧ if url.scheme == “custom-scheme", let response = HTTPURLResponse( url: url, statusCode: 200, httpVersion: nil, headerFields: nil ) { urlSchemeTask.didReceive(response) urlSchemeTask.didReceive(Data("Custom scheme response".utf8)) urlSchemeTask.didFinish() } else { urlSchemeTask.didFailWithError( NSError(domain: "CustomURLSchemeError", code: 404, userInfo: nil) ) } }

Slide 206

Slide 206 text

ߴ౓ͳઃఆͱΧελϚΠζ • WKWebViewCon f igurationͷΧελϚΠζ • ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • σʔλετΞͷΧελϚΠζ • ϓϦϑΝϨϯεͷઃఆ • UIΧελϚΠζͱφϏήʔγϣϯ؅ཧ • WKUIDelegate ͷ࣮૷ • WKNavigationDelegate ͷ࣮૷ • ΧελϜφϏήʔγϣϯόʔͷ౷߹ • ͦͷଞͷ֦ு • ΧελϜURLεΩʔϚͷରԠ • ίϯςϯπϑΟϧλϦϯά

Slide 207

Slide 207 text

ίϯςϯπϑΟϧλϦϯά • WKContentRuleListStore • ίϯςϯπϒϩοΩϯάϧʔϧΛ؅ཧ͢ΔͨΊͷΫϥε • ϧʔϧΛJSONܗࣜͰ࡞੒͠WKWebViewʹదԠͤ͞Δ • ϧʔϧ͸ Safari Web Extension ͷ ʮCreating a content blockerʯͱ͍͏ υΩϡϝϯτʹهड़͕͋Γɺಉ͡Α͏ʹWKWebViewͰ΋ར༻Ͱ͖Δ https://developer.apple.com/documentation/safariservices/creating-a-content-blocker

Slide 208

Slide 208 text

WKContentRuleListStore // ϧʔϧͷఆٛ let contentRules = """ [ { "trigger": { "url-filter": ".*", "resource-type": ["image"] }, "action": { "type": "block" } } ] """

Slide 209

Slide 209 text

WKContentRuleListStore // ϧʔϧͷఆٛ let contentRules = """ [ { "trigger": { "url-filter": ".*", "resource-type": ["image"] }, "action": { "type": "block" } } ] """

Slide 210

Slide 210 text

WKContentRuleListStore // ϧʔϧͷఆٛ let contentRules = “"" … """ // WKContentRuleListStore Λऔಘ let contentRuleListStore = WKContentRuleListStore.default()

Slide 211

Slide 211 text

WKContentRuleListStore // ϧʔϧͷఆٛ let contentRules = “"" … """ // WKContentRuleListStore Λऔಘ let contentRuleListStore = WKContentRuleListStore.default() // ϧʔϧϦετΛίϯύΠϧ contentRuleListStore?.compileContentRuleList( forIdentifier: "myContentRuleList", encodedContentRuleList: contentRules ) {}

Slide 212

Slide 212 text

WKContentRuleListStore // ϧʔϧϦετΛίϯύΠϧ contentRuleListStore?.compileContentRuleList( forIdentifier: "myContentRuleList", encodedContentRuleList: contentRules ) { (contentRuleList, error) in if let error = error { print("Failed to compile content rule list: \(error)") return } // ϧʔϧϦετΛWKUserContentControllerʹొ࿥ if let contentRuleList { let contentController = WKUserContentController() contentController.add(contentRuleList) let config = WKWebViewConfiguration() config.userContentController = contentController let webView = WKWebView(frame: .zero, configuration: config) // webView.load } }

Slide 213

Slide 213 text

WKContentRuleListStore // ϧʔϧϦετΛίϯύΠϧ contentRuleListStore?.compileContentRuleList( forIdentifier: "myContentRuleList", encodedContentRuleList: contentRules ) { (contentRuleList, error) in if let error = error { print("Failed to compile content rule list: \(error)") return } // ϧʔϧϦετΛWKUserContentControllerʹొ࿥ if let contentRuleList { let contentController = WKUserContentController() contentController.add(contentRuleList) let config = WKWebViewConfiguration() config.userContentController = contentController let webView = WKWebView(frame: .zero, configuration: config) // webView.load } }

Slide 214

Slide 214 text

ίϯςϯπϑΟϧλϦϯά • WKContentRuleListStore ͰͰ͖Δ͜ͱ • block: • ϦιʔεͷಡΈࠐΈΛఀࢭ • Safari ͷΩϟογϡ͕͋Δ৔߹Ͱ΋ແࢹ͞ΕΔ • block-cookies: • ϔομʔ͔ΒΫοΩʔΛ࡟আ • Safari ͷϓϥΠόγʔઃఆʹै͏ • css-display-none: • CSS ηϨΫλʹج͍ͮͯϖʔδͷཁૉΛඇදࣔʹ͢Δʢdisplay: none Λઃఆʣ • ignore-previous-rules: • Ҏલʹద༻͞ΕͨϧʔϧΛແࢹ͢Δ • make-https: • URL ͷϓϩτίϧΛ http ͔Β https ʹมߋ • ͨͩ͠ɺࢦఆ͞Εͨϙʔτ΍ଞͷϓϩτίϧʹ͸Өڹ͠ͳ͍

Slide 215

Slide 215 text

1. ͸͡Ίʹ 2. WKWebViewͷجૅ஌ࣝ 3. ΠϯλϥΫςΟϒͳػೳͷ࣮૷ 4. ϓϥΠόγʔอޢͱηΩϡϦςΟ 5. ߴ౓ͳઃఆͱΧελϚΠζ 6. σόοά 7. Ԡ༻ͱ࣮ફྫ 8. ·ͱΊ Agenda

Slide 216

Slide 216 text

σόοά

Slide 217

Slide 217 text

σόοά • Web Inspectorͷར༻

Slide 218

Slide 218 text

Web Inspectorͷར༻ • WKWebViewͷσόοάʹ͸ɺSafariͷWeb InspectorΛར༻ • Web InspectorΛ࢖͏͜ͱͰɺWebίϯςϯπͷৄࡉͳ৘ใΛ֬ೝͨ͠Γɺ σόοάΛߦͬͨΓͰ͖Δ https://developer.apple.com/documentation/safari-developer-tools/web-inspector

Slide 219

Slide 219 text

Web Inspectorͷ༗ޮԽ • WebView ͷ isInspectable Λ true ʹ͢Δ let webConfiguration = WKWebViewConfiguration() let webView = WKWebView(frame: .zero, configuration: webConfiguration) webView.isInspectable = true

Slide 220

Slide 220 text

Web Inspectorͷར༻ • Safari > ։ൃ

Slide 221

Slide 221 text

Web Inspectorͷར༻ • Safari > ։ൃ > ىಈதͷDevise

Slide 222

Slide 222 text

Web Inspectorͷར༻ • Safari > ։ൃ > ىಈதͷDevise > WebViewΠϯελϯε Λબ୒

Slide 223

Slide 223 text

Web Inspectorͷར༻

Slide 224

Slide 224 text

Web Inspectorͷར༻ • ࢖͍ํ͸௨ৗͷ Safari Web Inspector ͱಉ͡ • Web InspectorΛ࢖ͬͨجຊతͳૢ࡞ • Elements: • HTMLυΩϡϝϯτͷߏ଄Λ֬ೝ͠ɺCSSελΠϧΛมߋͰ͖Δ • Console: • JavaScriptͷΤϥʔϝοηʔδ΍ϩάΛ֬ೝͰ͖Δ • JavaScripͷଈ࣮࣌ߦ΋Մೳ • Network: • ωοτϫʔΫϦΫΤετͱϨεϙϯεͷৄࡉΛ֬ೝͰ͖Δ

Slide 225

Slide 225 text

1. ͸͡Ίʹ 2. WKWebViewͷجૅ஌ࣝ 3. ΠϯλϥΫςΟϒͳػೳͷ࣮૷ 4. ϓϥΠόγʔอޢͱηΩϡϦςΟ 5. ߴ౓ͳઃఆͱΧελϚΠζ 6. σόοά 7. Ԡ༻ͱ࣮ફྫ 8. ·ͱΊ Agenda

Slide 226

Slide 226 text

Ԡ༻ͱ࣮ફྫ

Slide 227

Slide 227 text

Ԡ༻ͱ࣮ફྫ • KVOͰ؂ࢹͰ͖ΔϓϩύςΟͱͦͷར༻ྫ • MutationObserver Λར༻ͨ͠DOM؂ࢹͷྫ

Slide 228

Slide 228 text

Ԡ༻ͱ࣮ફྫ • KVOͰ؂ࢹͰ͖ΔϓϩύςΟͱͦͷར༻ྫ • MutationObserver Λར༻ͨ͠DOM؂ࢹͷྫ

Slide 229

Slide 229 text

KVOͰ؂ࢹͰ͖ΔϓϩύςΟͱͦͷར༻ྫ • KVO ʢKey-Value Observationʣ • ର৅ͷΦϒδΣΫτΛ؂ࢹ͠ɺมߋΛ௨஌ͯ͘͠ΕΔ࢓૊Έ • CombineͰ΋؂ࢹՄೳ

Slide 230

Slide 230 text

KVOͰ؂ࢹͰ͖ΔWKWebViewͷϓϩύςΟҰཡ open var title: String? { get } open var url: URL? { get } open var isLoading: Bool { get } open var estimatedProgress: Double { get } open var hasOnlySecureContent: Bool { get } open var serverTrust: SecTrust? { get } open var canGoBack: Bool { get } open var canGoForward: Bool { get } open var cameraCaptureState: WKMediaCaptureState { get } // @available(iOS 15, *) open var microphoneCaptureState: WKMediaCaptureState { get } // @available(iOS 15, *) open var themeColor: UIColor? { get } // @available(iOS 15, *) @NSCopying open var underPageBackgroundColor: UIColor? { get } // @available(iOS 15, *) open var fullscreen: Bool { get } // @available(iOS 16, *)

Slide 231

Slide 231 text

KVO؂ࢹͷར༻ྫᶃ • estimatedProgress • WebίϯςϯπͷಡΈࠐΈঢ়گΛՄࢹԽ

Slide 232

Slide 232 text

KVO؂ࢹͷར༻ྫᶃ • estimatedProgress • WebίϯςϯπͷಡΈࠐΈঢ়گΛՄࢹԽ

Slide 233

Slide 233 text

KVO؂ࢹͷར༻ྫᶃ import WebKit class ViewController: UIViewController { private var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() webView = WKWebView(frame: self.view.bounds) self.view.addSubview(webView) if let url = URL(string: "https://www.apple.com") { let request = URLRequest(url: url) webView.load(request) } } }

Slide 234

Slide 234 text

KVO؂ࢹͷར༻ྫᶃ import WebKit class ViewController: UIViewController { private var webView: WKWebView! private var progressView: UIProgressView! override func viewDidLoad() { super.viewDidLoad() webView = WKWebView(frame: self.view.bounds) self.view.addSubview(webView) if let url = URL(string: "https://www.apple.com") { let request = URLRequest(url: url) webView.load(request) } progressView = UIProgressView(progressViewStyle: .default) progressView.frame = CGRect(x: 0, y: 88, width: self.view.bounds.width, height: 2) self.view.addSubview(progressView) } }

Slide 235

Slide 235 text

KVO؂ࢹͷར༻ྫᶃ import WebKit class ViewController: UIViewController { … override func viewDidLoad() { … } deinit { } }

Slide 236

Slide 236 text

KVO؂ࢹͷར༻ྫᶃ import WebKit class ViewController: UIViewController { … override func viewDidLoad() { … webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil) } deinit { webView.removeObserver(self, forKeyPath: "estimatedProgress") } }

Slide 237

Slide 237 text

KVO؂ࢹͷར༻ྫᶃ import WebKit class ViewController: UIViewController { … override func viewDidLoad() { … webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil) } deinit { webView.removeObserver(self, forKeyPath: "estimatedProgress") } }

Slide 238

Slide 238 text

KVO؂ࢹͷར༻ྫᶃ import WebKit class ViewController: UIViewController { … override func viewDidLoad() { … webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil) } override func observeValue( forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer? ) { if keyPath == "estimatedProgress" { progressView.progress = Float(webView.estimatedProgress) progressView.isHidden = (webView.estimatedProgress == 1.0) } } deinit { webView.removeObserver(self, forKeyPath: "estimatedProgress") }

Slide 239

Slide 239 text

KVO؂ࢹͷར༻ྫᶃ import WebKit class ViewController: UIViewController { … override func viewDidLoad() { … webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil) } override func observeValue( forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer? ) { if keyPath == "estimatedProgress" { progressView.progress = Float(webView.estimatedProgress) progressView.isHidden = (webView.estimatedProgress == 1.0) } } deinit { webView.removeObserver(self, forKeyPath: "estimatedProgress") }

Slide 240

Slide 240 text

KVO؂ࢹͷར༻ྫᶄ • url • WKWebViewΠϯελϯε্ͷݱࡏͷURLΛ؂ࢹͰ͖Δ • ௨ৗͷφϏήʔγϣϯͰ͋Ε͹WKNavigationDelegate ͰURLભҠ͸ϋϯυϦ ϯάͰ͖Δ͕… • Webίϯςϯπ͕SPA(Single Page Application) ͷ৔߹ • URL͕มԽͯ͠΋φϏήʔγϣϯʹӨڹ͸ͳ͍ͨΊɺWKNavigationDelegate ͰURLΛϋϯυϦϯά͢Δ͜ͱ͸Ͱ͖ͳ͍ • ͦͷͨΊSPAͷ৔߹ͰURLͷมԽʹ൐ͬͯԿ͔ͷΞΫγϣϯΛ͍ͨ࣌͠ɺKVO ؂ࢹΛߦ͏ඞཁ͕͋Δ

Slide 241

Slide 241 text

KVO؂ࢹͷར༻ྫᶄ • Webίϯςϯπ͕SPA(Single Page Application) ͷ৔߹ • URL͕มԽͯ͠΋φϏήʔγϣϯʹӨڹ͸ͳ͍ͨΊɺ WKNavigationDelegateͰURLΛϋϯυϦϯά͢Δ͜ͱ͸Ͱ͖ͳ͍ • ͦͷͨΊSPAͷ৔߹ͰURLͷมԽʹ൐ͬͯԿ͔ͷΞΫγϣϯΛ͍ͨ࣌͠ɺ KVO؂ࢹΛߦ͏ඞཁ͕͋Δ

Slide 242

Slide 242 text

KVOͰ؂ࢹͰ͖ΔϓϩύςΟͱͦͷར༻ྫ • ϓϩύςΟ؂ࢹͰWKWebViewͷදݱͷ෯͕޿͕Δ

Slide 243

Slide 243 text

Ԡ༻ͱ࣮ફྫ • KVOͰ؂ࢹͰ͖ΔϓϩύςΟͱͦͷར༻ྫ • MutationObserver Λར༻ͨ͠DOM؂ࢹͷྫ

Slide 244

Slide 244 text

MutationObserver Λར༻ͨ͠DOM؂ࢹͷྫ • MutationObserverͱ͸ • DOMπϦʔʹՃ͑ΒΕͨมߋΛ؂ࢹ͢ΔJavaScriptͷAPI https://developer.mozilla.org/ja/docs/Web/API/MutationObserver

Slide 245

Slide 245 text

MutationObserver Λར༻ͨ͠DOM؂ࢹͷྫ • ࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ

Slide 246

Slide 246 text

࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ

Slide 247

Slide 247 text

࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ • ΧʔτόοδͷηϨΫλʔΛ೺Ѳ

Slide 248

Slide 248 text

࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ • Χʔτόοδ͕औಘͰ͖Δ͔ίϯιʔϧͰ֬ೝ

Slide 249

Slide 249 text

࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ // MutationObserver ͷ࡞੒ const observer = new MutationObserver(() => { });

Slide 250

Slide 250 text

࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ // MutationObserver ͷ࡞੒ const observer = new MutationObserver(() => { }); // ؂ࢹͷ։࢝ observer.observe(document, { childList: true, characterData: true, subtree: true });

Slide 251

Slide 251 text

࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ // MutationObserver ͷ࡞੒ͱઃఆ const observer = new MutationObserver(() => { // Χʔτόοδͷ஋Λऔಘ const cartItemCountElement = document.querySelector('.globalnav-bag-badge-number'); if (cartItemCountElement) { const cartItemCount = cartItemCountElement.textContent.trim(); } }); // ؂ࢹͷ։࢝ observer.observe(document, { childList: true, characterData: true, subtree: true });

Slide 252

Slide 252 text

࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ // MutationObserver ͷ࡞੒ͱઃఆ const observer = new MutationObserver(() => { // Χʔτόοδͷ஋Λऔಘ const cartItemCountElement = document.querySelector('.globalnav-bag-badge-number'); if (cartItemCountElement) { const cartItemCount = cartItemCountElement.textContent.trim(); window.webkit.messageHandlers.mutationObserver.postMessage({ cart_item_count: cartItemCount }); } }); // ؂ࢹͷ։࢝ observer.observe(document, { childList: true, characterData: true, subtree: true });

Slide 253

Slide 253 text

࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() let contentController = WKUserContentController() let script = """ ... """ let userScript = WKUserScript( source: script, injectionTime: .atDocumentStart, forMainFrameOnly: true ) contentController.addUserScript(userScript) let config = WKWebViewConfiguration() config.userContentController = contentController webView = WKWebView(frame: view.bounds, configuration: config) view.addSubview(webView) if let url = URL(string: "https://www.apple.com") {

Slide 254

Slide 254 text

࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() let contentController = WKUserContentController() let script = """ ... """ let userScript = WKUserScript( source: script, injectionTime: .atDocumentStart, forMainFrameOnly: true ) contentController.addUserScript(userScript) let config = WKWebViewConfiguration() config.userContentController = contentController webView = WKWebView(frame: view.bounds, configuration: config) view.addSubview(webView) if let url = URL(string: "https://www.apple.com") { MutationObserverར༻ͷࡍ͸υΩϡϝϯτಡΈࠐΈલʹૠೖ

Slide 255

Slide 255 text

࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ class ViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() let contentController = WKUserContentController() let script = """ ... """ let userScript = WKUserScript( source: script, injectionTime: .atDocumentStart, forMainFrameOnly: true ) contentController.addUserScript(userScript) let config = WKWebViewConfiguration() config.userContentController = contentController config.userContentController.add(self, name: "mutationObserver") webView = WKWebView(frame: view.bounds, configuration: config) view.addSubview(webView)

Slide 256

Slide 256 text

࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ extension ViewController: WKScriptMessageHandler { }

Slide 257

Slide 257 text

࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ extension ViewController: WKScriptMessageHandler { func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { } }

Slide 258

Slide 258 text

࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ extension ViewController: WKScriptMessageHandler { func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { if message.name == "mutationObserver" { if let messageBody = message.body as? [String: Any] { } } } }

Slide 259

Slide 259 text

࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ extension ViewController: WKScriptMessageHandler { func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { if message.name == "mutationObserver" { if let messageBody = message.body as? [String: Any] { let cartItemCount = messageBody[“cart_item_count"] as? String ?? "0" print("Cart Item Count: \(cartItemCount)") // TODO: Cart Item Count ΛNavigationBarʹ࿈ܞ } } } }

Slide 260

Slide 260 text

MutationObserver Λར༻ͨ͠DOM؂ࢹͷྫ • MutationObserver ͕࢖͑ΔͱJS࿈ܞͷ෯͕޿͕ΔͷͰ͓͢͢Ί

Slide 261

Slide 261 text

1. ͸͡Ίʹ 2. WKWebViewͷجૅ஌ࣝ 3. ΠϯλϥΫςΟϒͳػೳͷ࣮૷ 4. ϓϥΠόγʔอޢͱηΩϡϦςΟ 5. ߴ౓ͳઃఆͱΧελϚΠζ 6. σόοά 7. Ԡ༻ͱ࣮ફྫ 8. ·ͱΊ Agenda

Slide 262

Slide 262 text

·ͱΊ

Slide 263

Slide 263 text

• ຊτʔΫͰ͸WKWebViewͷओཁͳػೳΛ෯޿͘આ໌ͨ͠ • ࠓճऔΓ্͛ΒΕͳ͔ͬͨAPI΋·ͩ·ͩଟ͋͘ΓɺऔΓ্͛ͨAPI΋ࡉ͔͍ ͱ͜Ζ·Ͱ͸આ໌͖͠Εͳ͍Օॴ΋͋Δ • ͜ͷ಺༰Λ౔୆ʹɺຊ౰ͷWKWebViewϚελʔΛ໨ࢦ͍͖ͯ͠·͠ΐ͏ ·ͱΊ

Slide 264

Slide 264 text

WKWebViewϚελʔ΁ͷಓ͸ɺ ·ͩ·ͩଓ͘… ଓͬͨ͘Β…ଓ͘…

Slide 265

Slide 265 text

એ఻

Slide 266

Slide 266 text

No content