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

めざせ!WKWebViewマスター! / WKWebView Master

marcy731
August 23, 2024

めざせ!WKWebViewマスター! / WKWebView Master

iOSDC Japan 2024
2024/08/24 13:00〜 Track D

marcy731

August 23, 2024
Tweet

More Decks by marcy731

Other Decks in Programming

Transcript

  1. WKWebViewͱSFSafariViewControllerͷҧ͍ • WKWebView • WebKitϑϨʔϜϫʔΫ • දࣔΛΧελϚΠζ·ͨ͸ίϯτϩʔϧ͍ͨ࣌͠ʹ࠷ద • WebίϯςϯπΛAppͷUIʹγʔϜϨεʹ౷߹Ͱ͖Δ •

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

    • ASWebAuthenticationSession • SafariServicesϑϨʔϜϫʔΫ • αʔυύʔςΟͷೝূʢϩάΠϯͳͲʣ͕ඞཁͳ৔߹ʹ࠷ద • αʔυύʔςΟೝূϓϩόΠμͱͷηΩϡΞͳೝূηογϣϯΛఏڙ͠ɺOAuth΍OpenID ConnectͳͲͷೝূΛαϙʔτ
  3. WKWebViewͷجຊతͳ࢖͍ํ import UIKit import WebKit class ViewController: UIViewController { var

    webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() // WKWebViewͷΠϯελϯεΛ࡞੒ webView = WKWebView(frame: view.bounds) } }
  4. 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) } }
  5. 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) } } }
  6. 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) } } }
  7. 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) } } }
  8. 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 = "<html><body><h1>Hello, World!</h1></body></html>" webView.loadHTMLString(htmlString, baseURL: nil) } }
  9. 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…
  10. 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) } } }
  11. 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) } } }
  12. 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) } } }
  13. WKScriptMessageHandler Λར༻ͨ͠ྫ class ViewController: UIViewController { var webView: WKWebView! override

    func viewDidLoad() { super.viewDidLoad() … contentController.add(self, name: "myMessageHandler") … } } // WKScriptMessageHandlerɿJavaScript ͔ΒͷϝοηʔδΛॲཧ͢Δϓϩτίϧ extension ViewController: WKScriptMessageHandler { }
  14. 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 ) { } }
  15. 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 Λར༻ͨ͠ྫ
  16. 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")
  17. 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")
  18. 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")
  19. // 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 Λར༻ͨ͠ྫ
  20. 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")
  21. 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")
  22. 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")
  23. 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")
  24. 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")
  25. 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")
  26. 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)) } } }
  27. 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)) }
  28. 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)
  29. evaluateJavaScript(_:completionHandler:)ϝιουͷར༻ import UIKit import WebKit extension ViewController: WKNavigationDelegate { //

    WebίϯςϯπͷಡΈࠐΈ׬ྃΛϋϯυϦϯά͢ΔWKNavigationDelegateϝιου // WebίϯςϯπͷಡΈࠐΈ׬ྃ࣌ʹJavaScriptΛ࣮ߦ͢Δྫ func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) { } }
  30. 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';" } }
  31. 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)") } } }
  32. 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)) } } }
  33. 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 { }
  34. 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?) { } }
  35. 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 }
  36. ϩʔΧϧετϨʔδͷ؅ཧ // ϩʔΧϧετϨʔδʹσʔλΛอଘ 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
  37. ϩʔΧϧετϨʔδͷ؅ཧ • ஫ҙ఺ • ηΩϡϦςΟ: • localStorage ͸ΫϥΠΞϯταΠυͰσʔλΛอଘ͢ΔͨΊɺػີੑͷߴ͍৘ใ Λอଘ͢Δͷ͸ආ͚Δ΂͖ •

    σʔλ͕ϒϥ΢βͷετϨʔδʹอଘ͞ΕΔͨΊɺଞͷϖʔδ΍ηογϣϯͰ΋ ΞΫηεͰ͖ΔՄೳੑ͕͋Δ • ΫϩεΦϦδϯ: • WKWebView ͷ localStorage ͸ಉҰΦϦδϯϙϦγʔʹै͏ͨΊɺҟͳΔΦϦδ ϯͷϖʔδ͔Β͸ΞΫηεͰ͖ͳ͍
  38. 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)) } } }
  39. 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)) } } }
  40. 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)) } } }
  41. JavaScriptͷ੍ݶͱ؅ཧ • ͲͷΑ͏ͳϦεΫ͕͋Δ͔ • ѱҙͷ͋Δίʔυ࣮ߦ • ΫϩεαΠτεΫϦϓςΟϯά (XSS) ߈ܸͳͲͷϦεΫ͕ߴ·Δ •

    ηΩϡϦςΟϙϦγʔ΍ݕࠪΛ௨ͯ͡ɺ҆શͰ͋Δ͜ͱ͕֬ೝ͞Εͨ ίʔυͷΈΛڐՄ͢Δ͜ͱ͕ॏཁ • ৘ใ࿙Ӯͷ๷ࢭ • ֎෦ͷεΫϦϓτ͕ϢʔβʔͷσʔλʹΞΫηε͢ΔͷΛ๷͙͜ͱ͕Ͱ͖ Δ • ಛʹɺϢʔβʔͷϓϥΠόγʔʹؔΘΔ৘ใؚ͕·ΕΔ৔߹ɺॏཁ
  42. App Transport Security (ATS) ͷઃఆ • App Transport Security (ATS)

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

    PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.d <plist version="1.0"> <dict> </dict> </plist> • App Transport Security (ATS) ͷྫ֎ͷઃఆ
  44. App Transport Security (ATS) ͷઃఆ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist

    PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.d <plist version="1.0"> <dict> <key>NSAppTransportSecurity</key> <dict> <key>NSExceptionDomains</key> <dict> <key>example.com</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSExceptionAllowsInsecureHTTPLoads</key> <true/> </dict> </dict> </dict> </dict> </plist> • App Transport Security (ATS) ͷྫ֎ͷઃఆ
  45. App Transport Security (ATS) ͷઃఆ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist

    PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.d <plist version="1.0"> <dict> <key>NSAppTransportSecurity</key> <dict> <key>NSExceptionDomains</key> <dict> <key>example.com</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSExceptionAllowsInsecureHTTPLoads</key> <true/> </dict> </dict> </dict> </dict> </plist> • App Transport Security (ATS) ͷྫ֎ͷઃఆ
  46. App Transport Security (ATS) ͷઃఆ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist

    PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.d <plist version="1.0"> <dict> <key>NSAppTransportSecurity</key> <dict> <key>NSExceptionDomains</key> <dict> <key>example.com</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSExceptionAllowsInsecureHTTPLoads</key> <true/> </dict> </dict> </dict> </dict> </plist> • App Transport Security (ATS) ͷྫ֎ͷઃఆ
  47. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.d

    <plist version="1.0"> <dict> <key>NSAppTransportSecurity</key> <dict> <key>NSExceptionDomains</key> <dict> <key>example.com</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSExceptionAllowsInsecureHTTPLoads</key> <true/> </dict> </dict> </dict> </dict> </plist> App Transport Security (ATS) ͷઃఆ • App Transport Security (ATS) ͷྫ֎ͷઃఆ
  48. ߴ౓ͳઃఆͱΧελϚΠζ • WKWebViewCon f igurationͷΧελϚΠζ • ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • σʔλετΞͷΧελϚΠζ •

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

    ϓϦϑΝϨϯεͷઃఆ • UIΧελϚΠζͱφϏήʔγϣϯ؅ཧ • WKUIDelegate ͷ࣮૷ • WKNavigationDelegate ͷ࣮૷ • ΧελϜφϏήʔγϣϯόʔͷ౷߹ • ͦͷଞͷ֦ு • ΧελϜURLεΩʔϚͷରԠ • ίϯςϯπϑΟϧλϦϯά
  50. ϓϩηεϓʔϧͷڞ༗ͱ෼཭ // 1. ڞ௨Ͱར༻͢ΔϓϩηεϓʔϧΠϯελϯεΛ४උ let sharedProcessPool = WKProcessPool() • ڞ༗

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

    ڞ௨ͷϓϩηεϓʔϧΛ࢖༻ͯ͠ WKWebViewConfiguration Λઃఆ let configuration = WKWebViewConfiguration() configuration.processPool = sharedProcessPool • ڞ༗ • Ωϟογϡ΍CookieͳͲͷσʔλ͕ෳ਺ͷWKWebViewؒͰڞ༗ • ϝϞϦ࢖༻ྔ͕ݮΓɺύϑΥʔϚϯε͕޲্
  52. ϓϩηεϓʔϧͷڞ༗ͱ෼཭ // 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ؒͰڞ༗ • ϝϞϦ࢖༻ྔ͕ݮΓɺύϑΥʔϚϯε͕޲্
  53. ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • ෼཭ • ҟͳΔWKWebViewؒͰΩϟογϡ΍CookieͳͲͷσʔλΛִ཭Ͱ͖Δ • ηΩϡϦςΟ͕޲্ // 1. ϓϩηεϓʔϧΛҟͳΔΠϯελϯεͰ࡞੒

    let processPool1 = WKProcessPool() let processPool2 = WKProcessPool() // 2. ֤ϓϩηεϓʔϧΛ࢖༻ͯ͠ WKWebViewConfiguration Λઃఆ let configuration1 = WKWebViewConfiguration() configuration1.processPool = processPool1 let configuration2 = WKWebViewConfiguration() configuration2.processPool = processPool2
  54. ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • ෼཭ • ҟͳΔ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)
  55. ߴ౓ͳઃఆͱΧελϚΠζ • WKWebViewCon f igurationͷΧελϚΠζ • ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • σʔλετΞͷΧελϚΠζ •

    ϓϦϑΝϨϯεͷઃఆ • UIΧελϚΠζͱφϏήʔγϣϯ؅ཧ • WKUIDelegate ͷ࣮૷ • WKNavigationDelegate ͷ࣮૷ • ΧελϜφϏήʔγϣϯόʔͷ౷߹ • ͦͷଞͷ֦ு • ΧελϜURLεΩʔϚͷରԠ • ίϯςϯπϑΟϧλϦϯά
  56. WKWebsiteDataStore • σϑΥϧτετϨʔδ • WKWebsiteDataStoreͷσϑΥϧτͷΠϯελϯεΛऔಘͰ͖Δ • ΞϓϦશମͰڞ௨ͷWebσʔλΛ؅ཧ // WKWebViewConfigurationΛ࡞੒͠ɺσϑΥϧτͷσʔλετϨʔδΛઃఆ͢Δ let

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

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

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

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

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

    = UUID() let customDataStore = WKWebsiteDataStore(forIdentifier: identifier) configuration.websiteDataStore = customDataStore // ઃఆΛద༻ͯ͠WKWebViewͷΠϯελϯεΛੜ੒͢Δ let webView = WKWebView(frame: .zero, configuration: configuration)
  62. ΫοΩʔͷऔಘ // WKWebsiteDataStore ͷΠϯελϯεΛऔಘ let dataStore = WKWebsiteDataStore.default() // શͯͷΫοΩʔΛऔಘ

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

    let cookies = await dataStore.httpCookieStore.allCookies() for cookie in cookies { await dataStore.httpCookieStore.deleteCookie(cookie) print("Cookie deleted: \(cookie)") }
  64. ৽͍͠ΫοΩʔͷઃఆ // 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)") }
  65. ৽͍͠ΫοΩʔͷઃఆ // 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)") }
  66. ΫοΩʔϙϦγʔͷઃఆ (iOS17) // WKWebsiteDataStore ͷΠϯελϯεΛऔಘ let dataStore = WKWebsiteDataStore.default() //

    ΫοΩʔϙϦγʔͷઃఆ // - .allow: // - .disallow: await dataStore.httpCookieStore.setCookiePolicy(.allow)
  67. ΫοΩʔมߋͷ؂ࢹ // 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)") } } }
  68. ΫοΩʔมߋͷ؂ࢹ // 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)") } } }
  69. ΫοΩʔมߋͷ؂ࢹ 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)
  70. Ωϟογϡͷ࡟আ // WKWebsiteDataStore ͷΠϯελϯεΛऔಘ let dataStore = WKWebsiteDataStore.default() // ࡟আର৅ͷσʔλλΠϓΛࢦఆ(Ұྫ)

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

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

    let dataTypes: Set<String> = [ WKWebsiteDataTypeMemoryCache, // ϝϞϦΩϟογϡ WKWebsiteDataTypeDiskCache, // σΟεΫΩϟογϡ WKWebsiteDataTypeOfflineWebApplicationCache // ΦϑϥΠϯWebΞϓϦέʔγϣϯΩϟογϡ ]) // σʔλϨίʔυΛऔಘ let records = await dataStore.dataRecords(ofTypes: dataTypes) // ֤σʔλϨίʔυΛ࡟আ for record in records { await dataStore.removeData(ofTypes: dataTypes, for: [record]) print("Ωϟογϡ͕࡟আ͞Ε·ͨ͠ɻ") }
  73. 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 σʔλϕʔε
  74. HTTPϦΫΤετ͝ͱʹΩϟογϡϙϦγʔΛઃఆ let webView = WKWebView(frame: .zero) let urlRequest = URLRequest(

    url: URL(string: “https://www.apple.com")!, cachePolicy: .reloadIgnoringLocalCacheData, // ϦΫΤετ͝ͱͷΩϟογϡϙϦγʔͷઃఆ ) webView.load(urlRequest)
  75. ΩϟογϡϙϦγʔҰཡ • useProtocolCachePolicy: ॳظ஋ / αʔόʔ͕ఏڙ͢ΔΩϟογϡ੍ޚϔομʔʹج͍ͮͯద༻ • reloadIgnoringLocalCacheData: ϩʔΧϧΩϟογϡΛແࢹ •

    reloadIgnoringLocalAndRemoteCacheData: ϩʔΧϧ͓ΑͼϦϞʔτΩϟογϡΛແࢹ • returnCacheDataElseLoad: Ωϟογϡ͕͋Ε͹ͦΕΛ࢖༻͠ɺͳ͚Ε͹ωοτϫʔΫ͔Βऔಘ • returnCacheDataDontLoad: Ωϟογϡ͕͋Ε͹ͦΕΛ࢖༻͠ɺͳ͚Ε͹ϦΫΤετ͠ͳ͍ • reloadRevalidatingCacheData: ΩϟογϡΛ࠶ݕূ͠ɺඞཁʹԠͯ͡ωοτϫʔΫ͔Βऔಘ
  76. ΫοΩʔͱΩϟογϡͷؔ܎ • ΫοΩʔ • ΢ΣϒαΠτͷઃఆ΍ηογϣϯ৘ใΛอଘ͢ΔͨΊͷ΋ͷ • WKHTTPCookieStore Ͱ؅ཧ • Ωϟογϡ

    • ΢ΣϒαΠτͷσʔλΛอଘͯ͠ύϑΥʔϚϯεΛ޲্ͤ͞Δ΋ͷ WKWebsiteDataStore Ͱ؅ཧ • ΫοΩʔΛ࡟আͯ͠΋Ωϟογϡ΍ϩʔΧϧετϨʔδ͸ӨڹΛड͚ͳ͍ • ΩϟογϡΛ࡟আͯ͠΋ΫοΩʔ΍ϩʔΧϧετϨʔδ͸ӨڹΛड͚ͳ͍
  77. ߴ౓ͳઃఆͱΧελϚΠζ • WKWebViewCon f igurationͷΧελϚΠζ • ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • σʔλετΞͷΧελϚΠζ •

    ϓϦϑΝϨϯεͷઃఆ • UIΧελϚΠζͱφϏήʔγϣϯ؅ཧ • WKUIDelegate ͷ࣮૷ • WKNavigationDelegate ͷ࣮૷ • ΧελϜφϏήʔγϣϯόʔͷ౷߹ • ͦͷଞͷ֦ு • ΧελϜURLεΩʔϚͷରԠ • ίϯςϯπϑΟϧλϦϯά
  78. WKWebpagePreferences Ұཡᶃ let preferences = WKPreferences() // ΢ΣϒίϯςϯπͰ࢖༻͞ΕΔ࠷খϑΥϯταΠζΛϙΠϯτ୯ҐͰࢦఆɻσϑΥϧτ஋͸ 0 preferences.minimumFontSize

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

    = 16.0 // JavaScript ͕Ϣʔβʔͷૢ࡞ͳ͠ʹ৽͍͠΢Οϯυ΢Λ։͚Δ͔Ͳ͏͔ΛࢦఆɻσϑΥϧτ஋͸ iOS Ͱ͸ false preferences.javaScriptCanOpenWindowsAutomatically = true // ࠮ٗతͳ΢ΣϒαΠτʹରͯ͠ܯࠂΛදࣔ͢Δ͔Ͳ͏͔Λࢦఆ preferences.isFraudulentWebsiteWarningEnabled = true // ΢ΣϒϖʔδΛҹ࡮͢ΔࡍʹഎܠΛؚΊΔ͔Ͳ͏͔Λࢦఆ preferences.shouldPrintBackgrounds = true
  80. WKWebpagePreferences Ұཡᶄ let preferences = WKPreferences() // ΢Σϒίϯςϯπ಺ͰͷςΩετΠϯλϥΫγϣϯʢબ୒΍ίϐʔͳͲʣ͕༗ޮ͔Ͳ͏͔Λࢦఆ preferences.isTextInteractionEnabled =

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

    false // ಛఆͷ΢ΣϒαΠτʹର͢Δޓ׵ੑ޲্ͷͨΊͷΫΠʔΫΛద༻͢Δ͔Ͳ͏͔Λࢦఆ // ΫΠʔΫʢQuirksʣɿ // ಛఆͷ΢ΣϒαΠτ΍΢ΣϒΞϓϦέʔγϣϯ͕ਖ਼ৗʹಈ࡞͢ΔΑ͏ʹɺϒϥ΢β͕ಠࣗʹద༻͢Δಈ࡞ͷௐ੔΍ಛྫͷ͜ͱ preferences.isSiteSpecificQuirksModeEnabled = true // ΢Σϒϖʔδ಺ͷཁૉΛશը໘දࣔ͢Δ͔Ͳ͏͔Λࢦఆ preferences.isElementFullscreenEnabled = true // WKWebView ͕ඇΞΫςΟϒͰϏϡʔ֊૚͔Β֎Εͨͱ͖ͷεέδϡʔϦϯάϙϦγʔΛࢦఆ // - suspend: JavaScript ͷ࣮ߦ΍ϖʔδͷϨΠΞ΢τ͕ఀࢭ // - throttle: CPU ͷ࢖༻Λ੍ݶ͠·͕͢ɺ׬શʹ͸ఀࢭ͠ͳ͍ // - none: ಛผͳεέδϡʔϦϯάಈ࡞Λద༻ͤͣɺ௨ৗ௨Γಈ࡞ preferences.inactiveSchedulingPolicy = .none
  82. ߴ౓ͳઃఆͱΧελϚΠζ • WKWebViewCon f igurationͷΧελϚΠζ • ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • σʔλετΞͷΧελϚΠζ •

    ϓϦϑΝϨϯεͷઃఆ • UIΧελϚΠζͱφϏήʔγϣϯ؅ཧ • WKUIDelegate ͷ࣮૷ • WKNavigationDelegate ͷ࣮૷ • ΧελϜφϏήʔγϣϯόʔͷ౷߹ • ͦͷଞͷ֦ு • ΧελϜURLεΩʔϚͷରԠ • ίϯςϯπϑΟϧλϦϯά
  83. 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)) } } }
  84. 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 { }
  85. ओͳ WKUIDelegate ϝιου • ΞϥʔτμΠΞϩά • webView(_:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:) • ֬ೝμΠΞϩά •

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

    webView(_:runJavaScriptCon f irmPanelWithMessage:initiatedByFrame:completionHandler:) • ೖྗμΠΞϩά • webView(_:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:) • ৽͍͠΢Οϯυ΢/λϒ • webView(_:createWebViewWith:for:windowFeatures:) • ΢Οϯυ΢Ϋϩʔζ • webViewDidClose(_:)
  87. ΞϥʔτμΠΞϩά - 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) } }
  88. ΞϥʔτμΠΞϩά - 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) } }
  89. ΞϥʔτμΠΞϩά - 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) } }
  90. ओͳ WKUIDelegate ϝιου • ΞϥʔτμΠΞϩά • webView(_:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:) • ֬ೝμΠΞϩά •

    webView(_:runJavaScriptCon f irmPanelWithMessage:initiatedByFrame:completionHandler:) • ೖྗμΠΞϩά • webView(_:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:) • ৽͍͠΢Οϯυ΢/λϒ • webView(_:createWebViewWith:for:windowFeatures:) • ΢Οϯυ΢Ϋϩʔζ • webViewDidClose(_:)
  91. ֬ೝμΠΞϩά - 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) } }
  92. ֬ೝμΠΞϩά - 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) } }
  93. ֬ೝμΠΞϩά - 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) } }
  94. ओͳ WKUIDelegate ϝιου • ΞϥʔτμΠΞϩά • webView(_:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:) • ֬ೝμΠΞϩά •

    webView(_:runJavaScriptCon f irmPanelWithMessage:initiatedByFrame:completionHandler:) • ೖྗμΠΞϩά • webView(_:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:) • ৽͍͠΢Οϯυ΢/λϒ • webView(_:createWebViewWith:for:windowFeatures:) • ΢Οϯυ΢Ϋϩʔζ • webViewDidClose(_:)
  95. ೖྗμΠΞϩά - 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) }
  96. ೖྗμΠΞϩά - 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) }
  97. ೖྗμΠΞϩά - 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) }
  98. ೖྗμΠΞϩά - 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) }
  99. ओͳ WKUIDelegate ϝιου • ΞϥʔτμΠΞϩά • webView(_:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:) • ֬ೝμΠΞϩά •

    webView(_:runJavaScriptCon f irmPanelWithMessage:initiatedByFrame:completionHandler:) • ೖྗμΠΞϩά • webView(_:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:) • ৽͍͠΢Οϯυ΢/λϒ • webView(_:createWebViewWith:for:windowFeatures:) • ΢Οϯυ΢Ϋϩʔζ • webViewDidClose(_:)
  100. ৽͍͠΢Οϯυ΢/λϒ - createWebViewWith - // MARK: - WKUIDelegate extension ViewController:

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

    WKUIDelegate { // λΠϛϯά: ΢Σϒϖʔδ͔Β৽͍͠΢Οϯυ΢΍λϒΛ։ͨ͘Ίͷཁٻ͕͋ͬͨͱ͖ func webView( _ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures ) -> WKWebView? { // WKWebViewͰ͸target="_blank"͕ઃఆ͞Ε͍ͯΔϦϯΫλοϓ࣌ɺͦͷ··Ͱ͸ભҠ͠ͳ͍ return nil } }
  102. ৽͍͠΢Οϯυ΢/λϒ - 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 } }
  103. ओͳ WKUIDelegate ϝιου • ΞϥʔτμΠΞϩά • webView(_:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:) • ֬ೝμΠΞϩά •

    webView(_:runJavaScriptCon f irmPanelWithMessage:initiatedByFrame:completionHandler:) • ೖྗμΠΞϩά • webView(_:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:) • ৽͍͠΢Οϯυ΢/λϒ • webView(_:createWebViewWith:for:windowFeatures:) • ΢Οϯυ΢Ϋϩʔζ • webViewDidClose(_:)
  104. ΢Οϯυ΢Ϋϩʔζ - webViewDidClose - // MARK: - WKUIDelegate extension ViewController:

    WKUIDelegate { // λΠϛϯά: window.close()͕ݺ͹Εͨͱ͖ʹWebView͕ด͡ΒΕͨͱ͖ func webViewDidClose(_ webView: WKWebView) { // Handle the WebView closure, e.g., remove it from the UI } }
  105. ߴ౓ͳઃఆͱΧελϚΠζ • WKWebViewCon f igurationͷΧελϚΠζ • ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • σʔλετΞͷΧελϚΠζ •

    ϓϦϑΝϨϯεͷઃఆ • UIΧελϚΠζͱφϏήʔγϣϯ؅ཧ • WKUIDelegate ͷ࣮૷ • WKNavigationDelegate ͷ࣮૷ • ΧελϜφϏήʔγϣϯόʔͷ౷߹ • ͦͷଞͷ֦ு • ΧελϜURLεΩʔϚͷରԠ • ίϯςϯπϑΟϧλϦϯά
  106. 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)) } } }
  107. 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 { }
  108. 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) }
  109. 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) } }
  110. URLRequestΛloadͨ͠ޙͷϝιουݺͼग़͠ͷॱংʢ௨ৗ࣌ʣ 1. load(_:) 2. decidePolicyFor navigationAction: φϏήʔγϣϯϑϩʔͷ։࢝ɻφϏήʔτΛڐՄ 3. didStartProvisionalNavigation: φϏήʔγϣϯ։࢝Λݕ஌

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

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

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

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

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

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

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

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

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

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

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

    φϏήʔγϣϯ։࢝Λݕ஌ 4. αʔόʔ͔ΒͷϨεϙϯε଴ػ 5. didCommit: ίϯςϯπͷಡΈࠐΈ։࢝ 6. didFail:withError: ίϯςϯπͷಡΈࠐΈதͷΤϥʔɻλΠϛϯάʹΑͬͯ͸ didCommit ͕ݺ͹Εͳ͍ɻ
  122. ߴ౓ͳઃఆͱΧελϚΠζ • WKWebViewCon f igurationͷΧελϚΠζ • ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • σʔλετΞͷΧελϚΠζ •

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

    ϓϦϑΝϨϯεͷઃఆ • UIΧελϚΠζͱφϏήʔγϣϯ؅ཧ • WKUIDelegate ͷ࣮૷ • WKNavigationDelegate ͷ࣮૷ • ΧελϜφϏήʔγϣϯόʔͷ౷߹ • ͦͷଞͷ֦ு • ΧελϜURLεΩʔϚͷରԠ • ίϯςϯπϑΟϧλϦϯά
  124. WKURLSchemeHandler ͷར༻ྫ • ΞηοτͷϩʔΧϧಡΈࠐΈ • ྫɿ`custom-scheme://images/logo.png` • ΞϓϦ಺ͷϦιʔε΍σʔλΛ WKWebView ಺Ͱ௚઀ѻ͏͜ͱ͕Ͱ͖Δ

    • σόοά৘ใͷૹ৴ • ྫɿ`custom-scheme://debug?info=someDebugInfo` • Web ίϯςϯπ͔ΒΞϓϦέʔγϣϯʹσόοά৘ใΛૹ৴
  125. 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) } } }
  126. 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) } } }
  127. 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) } } }
  128. WKURLSchemeHandler // WKURLSchemeHandler: ΧελϜURLεΩʔϜͷϦιʔεΛಡΈࠐΉͨΊͷϓϩτίϧ class CustomSchemeHandler: NSObject, WKURLSchemeHandler { //

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

    URLSchemeϦΫΤετͷॲཧΛ։࢝͢Δϝιου // WKURLSchemeTask Λ௨ͯ͡ϦΫΤετʹԠ౴ func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) { } // ϦΫΤετॲཧΛఀࢭ͢Δϝιου // ͢΂ͯͷඇಉظϦΫΤετΛΩϟϯηϧ͢Δࡍʹ࢖༻ func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) { } }
  130. 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) ) } }
  131. ߴ౓ͳઃఆͱΧελϚΠζ • WKWebViewCon f igurationͷΧελϚΠζ • ϓϩηεϓʔϧͷڞ༗ͱ෼཭ • σʔλετΞͷΧελϚΠζ •

    ϓϦϑΝϨϯεͷઃఆ • UIΧελϚΠζͱφϏήʔγϣϯ؅ཧ • WKUIDelegate ͷ࣮૷ • WKNavigationDelegate ͷ࣮૷ • ΧελϜφϏήʔγϣϯόʔͷ౷߹ • ͦͷଞͷ֦ு • ΧελϜURLεΩʔϚͷରԠ • ίϯςϯπϑΟϧλϦϯά
  132. ίϯςϯπϑΟϧλϦϯά • WKContentRuleListStore • ίϯςϯπϒϩοΩϯάϧʔϧΛ؅ཧ͢ΔͨΊͷΫϥε • ϧʔϧΛJSONܗࣜͰ࡞੒͠WKWebViewʹదԠͤ͞Δ • ϧʔϧ͸ Safari

    Web Extension ͷ ʮCreating a content blockerʯͱ͍͏ υΩϡϝϯτʹهड़͕͋Γɺಉ͡Α͏ʹWKWebViewͰ΋ར༻Ͱ͖Δ https://developer.apple.com/documentation/safariservices/creating-a-content-blocker
  133. WKContentRuleListStore // ϧʔϧͷఆٛ let contentRules = """ [ { "trigger":

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

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

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

    WKContentRuleListStore Λऔಘ let contentRuleListStore = WKContentRuleListStore.default() // ϧʔϧϦετΛίϯύΠϧ contentRuleListStore?.compileContentRuleList( forIdentifier: "myContentRuleList", encodedContentRuleList: contentRules ) {}
  137. 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 } }
  138. 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 } }
  139. ίϯςϯπϑΟϧλϦϯά • WKContentRuleListStore ͰͰ͖Δ͜ͱ • block: • ϦιʔεͷಡΈࠐΈΛఀࢭ • Safari

    ͷΩϟογϡ͕͋Δ৔߹Ͱ΋ແࢹ͞ΕΔ • block-cookies: • ϔομʔ͔ΒΫοΩʔΛ࡟আ • Safari ͷϓϥΠόγʔઃఆʹै͏ • css-display-none: • CSS ηϨΫλʹج͍ͮͯϖʔδͷཁૉΛඇදࣔʹ͢Δʢdisplay: none Λઃఆʣ • ignore-previous-rules: • Ҏલʹద༻͞ΕͨϧʔϧΛແࢹ͢Δ • make-https: • URL ͷϓϩτίϧΛ http ͔Β https ʹมߋ • ͨͩ͠ɺࢦఆ͞Εͨϙʔτ΍ଞͷϓϩτίϧʹ͸Өڹ͠ͳ͍
  140. Web Inspectorͷ༗ޮԽ • WebView ͷ isInspectable Λ true ʹ͢Δ let

    webConfiguration = WKWebViewConfiguration() let webView = WKWebView(frame: .zero, configuration: webConfiguration) webView.isInspectable = true
  141. Web Inspectorͷར༻ • ࢖͍ํ͸௨ৗͷ Safari Web Inspector ͱಉ͡ • Web

    InspectorΛ࢖ͬͨجຊతͳૢ࡞ • Elements: • HTMLυΩϡϝϯτͷߏ଄Λ֬ೝ͠ɺCSSελΠϧΛมߋͰ͖Δ • Console: • JavaScriptͷΤϥʔϝοηʔδ΍ϩάΛ֬ೝͰ͖Δ • JavaScripͷଈ࣮࣌ߦ΋Մೳ • Network: • ωοτϫʔΫϦΫΤετͱϨεϙϯεͷৄࡉΛ֬ೝͰ͖Δ
  142. 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, *)
  143. 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) } } }
  144. 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) } }
  145. KVO؂ࢹͷར༻ྫᶃ import WebKit class ViewController: UIViewController { … override func

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

    viewDidLoad() { … webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil) } deinit { webView.removeObserver(self, forKeyPath: "estimatedProgress") } }
  147. 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") }
  148. 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") }
  149. KVO؂ࢹͷར༻ྫᶄ • url • WKWebViewΠϯελϯε্ͷݱࡏͷURLΛ؂ࢹͰ͖Δ • ௨ৗͷφϏήʔγϣϯͰ͋Ε͹WKNavigationDelegate ͰURLભҠ͸ϋϯυϦ ϯάͰ͖Δ͕… •

    Webίϯςϯπ͕SPA(Single Page Application) ͷ৔߹ • URL͕มԽͯ͠΋φϏήʔγϣϯʹӨڹ͸ͳ͍ͨΊɺWKNavigationDelegate ͰURLΛϋϯυϦϯά͢Δ͜ͱ͸Ͱ͖ͳ͍ • ͦͷͨΊSPAͷ৔߹ͰURLͷมԽʹ൐ͬͯԿ͔ͷΞΫγϣϯΛ͍ͨ࣌͠ɺKVO ؂ࢹΛߦ͏ඞཁ͕͋Δ
  150. ࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ // MutationObserver ͷ࡞੒ const observer = new MutationObserver(() =>

    { }); // ؂ࢹͷ։࢝ observer.observe(document, { childList: true, characterData: true, subtree: true });
  151. ࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ // 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 });
  152. ࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ // 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 });
  153. ࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ 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") {
  154. ࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ 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ར༻ͷࡍ͸υΩϡϝϯτಡΈࠐΈલʹૠೖ
  155. ࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ 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)
  156. ࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ extension ViewController: WKScriptMessageHandler { func userContentController( _ userContentController: WKUserContentController,

    didReceive message: WKScriptMessage ) { if message.name == "mutationObserver" { if let messageBody = message.body as? [String: Any] { } } } }
  157. ࣮૷ྫɿϔομʔͷΧʔτόοδΛऔಘͯ͠ωΠςΟϒͱ࿈ܞ͢Δ 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ʹ࿈ܞ } } } }