SlideShare a Scribd company logo
1 of 63
Download to read offline
let UIWebView
as WKWebView
@taketo1024
2015/02/14
iOSオールスターズ勉強会
http://maths4pg.connpass.com
 本題
from: WWDC2014 Introducing the Modern WebKit API
from: WWDC2014 Introducing the Modern WebKit API
• iOS 2.0 からお馴染み
• JavaScript が Safari より遅い
from: WWDC2014 Introducing the Modern WebKit API
• iOS 2.0 からお馴染み
• JavaScript が Safari より遅い
• iOS 8.0 から導入
• Safari と同じ JavaScript エンジン!
• ページ遷移ジェスチャーにも対応
• その他も何かと高性能で良い
WKWebViewを使いたい!
でもまだ iOS 7 を切るわけには行かない…
UIWebView (親: UIView) WKWebView (親: UIView)
var canGoBack: Bool { get }
var canGoForward: Bool { get }
var loading: Bool { get }
var canGoBack: Bool { get }
var canGoForward: Bool { get }
var loading: Bool { get }
var title: String? { get }
var URL: NSURL? { get }
var estimatedProgress: Double { get }
func loadRequest(request: NSURLRequest)


func reload()
func stopLoading()
func goBack()
func goForward()
func loadRequest(request: NSURLRequest) ->
WKNavigation?
func reload() -> WKNavigation?
func stopLoading()

func goBack() -> WKNavigation?
func goForward() -> WKNavigation?
func stringByEvaluatingJavaScriptFromString
(script: String) -> String?
func evaluateJavaScript(javaScriptString:
String, completionHandler: ((AnyObject!,
NSError!) -> Void)?)
weak var delegate: UIWebViewDelegate? weak var navigationDelegate:
WKNavigationDelegate?

weak var UIDelegate: WKUIDelegate?
インターフェースは似ているが互換性はない
UIWebView (親: UIView) WKWebView (親: UIView)
var canGoBack: Bool { get }
var canGoForward: Bool { get }
var loading: Bool { get }
var canGoBack: Bool { get }
var canGoForward: Bool { get }
var loading: Bool { get }
var title: String? { get }
var URL: NSURL? { get }
var estimatedProgress: Double { get }
func loadRequest(request: NSURLRequest)


func reload()
func stopLoading()
func goBack()
func goForward()
func loadRequest(request: NSURLRequest) ->
WKNavigation?
func reload() -> WKNavigation?
func stopLoading()

func goBack() -> WKNavigation?
func goForward() -> WKNavigation?
func stringByEvaluatingJavaScriptFromString
(script: String) -> String?
func evaluateJavaScript(javaScriptString:
String, completionHandler: ((AnyObject!,
NSError!) -> Void)?)
weak var delegate: UIWebViewDelegate? weak var navigationDelegate:
WKNavigationDelegate?

weak var UIDelegate: WKUIDelegate?
インターフェースは似ているが互換性はない
WKWebView にしかないプロパティ
UIWebView (親: UIView) WKWebView (親: UIView)
var canGoBack: Bool { get }
var canGoForward: Bool { get }
var loading: Bool { get }
var canGoBack: Bool { get }
var canGoForward: Bool { get }
var loading: Bool { get }
var title: String? { get }
var URL: NSURL? { get }
var estimatedProgress: Double { get }
func loadRequest(request: NSURLRequest)


func reload()
func stopLoading()
func goBack()
func goForward()
func loadRequest(request: NSURLRequest) ->
WKNavigation?
func reload() -> WKNavigation?
func stopLoading()

func goBack() -> WKNavigation?
func goForward() -> WKNavigation?
func stringByEvaluatingJavaScriptFromString
(script: String) -> String?
func evaluateJavaScript(javaScriptString:
String, completionHandler: ((AnyObject!,
NSError!) -> Void)?)
weak var delegate: UIWebViewDelegate? weak var navigationDelegate:
WKNavigationDelegate?

weak var UIDelegate: WKUIDelegate?
インターフェースは似ているが互換性はない
load系 は WKNavigation? 型の戻り値がある
UIWebView (親: UIView) WKWebView (親: UIView)
var canGoBack: Bool { get }
var canGoForward: Bool { get }
var loading: Bool { get }
var canGoBack: Bool { get }
var canGoForward: Bool { get }
var loading: Bool { get }
var title: String? { get }
var URL: NSURL? { get }
var estimatedProgress: Double { get }
func loadRequest(request: NSURLRequest)


func reload()
func stopLoading()
func goBack()
func goForward()
func loadRequest(request: NSURLRequest) ->
WKNavigation?
func reload() -> WKNavigation?
func stopLoading()

func goBack() -> WKNavigation?
func goForward() -> WKNavigation?
func stringByEvaluatingJavaScriptFromString
(script: String) -> String?
func evaluateJavaScript(javaScriptString:
String, completionHandler: ((AnyObject!,
NSError!) -> Void)?)
weak var delegate: UIWebViewDelegate? weak var navigationDelegate:
WKNavigationDelegate?

weak var UIDelegate: WKUIDelegate?
インターフェースは似ているが互換性はない
JavaScript はコールバック形式
UIWebView (親: UIView) WKWebView (親: UIView)
var canGoBack: Bool { get }
var canGoForward: Bool { get }
var loading: Bool { get }
var canGoBack: Bool { get }
var canGoForward: Bool { get }
var loading: Bool { get }
var title: String? { get }
var URL: NSURL? { get }
var estimatedProgress: Double { get }
func loadRequest(request: NSURLRequest)


func reload()
func stopLoading()
func goBack()
func goForward()
func loadRequest(request: NSURLRequest) ->
WKNavigation?
func reload() -> WKNavigation?
func stopLoading()

func goBack() -> WKNavigation?
func goForward() -> WKNavigation?
func stringByEvaluatingJavaScriptFromString
(script: String) -> String?
func evaluateJavaScript(javaScriptString:
String, completionHandler: ((AnyObject!,
NSError!) -> Void)?)
weak var delegate: UIWebViewDelegate? weak var navigationDelegate:
WKNavigationDelegate?

weak var UIDelegate: WKUIDelegate?
インターフェースは似ているが互換性はない
delegateが2種類ある(IFもだいぶ違う)
class MyViewController: UIViewController {
var wkWebView: WKWebView?
var uiWebView: UIWebView?
override func viewDidLoad() {
super.viewDidLoad()
if NSClassFromString("WKWebView") != nil {
wkWebView = WKWebView(frame: )
self.view.addSubview(wkWebView!)
} else {
uiWebView = UIWebView(frame: )
self.view.addSubview(uiWebView!)
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let URL = NSURL(string: "http://yahoo.co.jp")
let req = NSURLRequest(URL: URL!)
if(wkWebView != nil) {
wkWebView!.loadRequest(req)
} else {
uiWebView!.loadRequest(req)
}
}
ナイーブな下位互換対応をすると…
class MyViewController: UIViewController {
var wkWebView: WKWebView?
var uiWebView: UIWebView?
override func viewDidLoad() {
super.viewDidLoad()
if NSClassFromString("WKWebView") != nil {
wkWebView = WKWebView(frame: )
self.view.addSubview(wkWebView!)
} else {
uiWebView = UIWebView(frame: )
self.view.addSubview(uiWebView!)
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let URL = NSURL(string: "http://yahoo.co.jp")
let req = NSURLRequest(URL: URL!)
if(wkWebView != nil) {
wkWebView!.loadRequest(req)
} else {
uiWebView!.loadRequest(req)
}
}
ナイーブな下位互換対応をすると…
UIWebView / WKWebView の両方を変数で宣言
class MyViewController: UIViewController {
var wkWebView: WKWebView?
var uiWebView: UIWebView?
override func viewDidLoad() {
super.viewDidLoad()
if NSClassFromString("WKWebView") != nil {
wkWebView = WKWebView(frame: )
self.view.addSubview(wkWebView!)
} else {
uiWebView = UIWebView(frame: )
self.view.addSubview(uiWebView!)
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let URL = NSURL(string: "http://yahoo.co.jp")
let req = NSURLRequest(URL: URL!)
if(wkWebView != nil) {
wkWebView!.loadRequest(req)
} else {
uiWebView!.loadRequest(req)
}
}
ナイーブな下位互換対応をすると…
対応/非対応に応じて一方にインスタンスを入れる
class MyViewController: UIViewController {
var wkWebView: WKWebView?
var uiWebView: UIWebView?
override func viewDidLoad() {
super.viewDidLoad()
if NSClassFromString("WKWebView") != nil {
wkWebView = WKWebView(frame: )
self.view.addSubview(wkWebView!)
} else {
uiWebView = UIWebView(frame: )
self.view.addSubview(uiWebView!)
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let URL = NSURL(string: "http://yahoo.co.jp")
let req = NSURLRequest(URL: URL!)
if(wkWebView != nil) {
wkWebView!.loadRequest(req)
} else {
uiWebView!.loadRequest(req)
}
}
ナイーブな下位互換対応をすると…
そして毎回分岐して同じ処理を2度書くことに…
これはよくない
バージョン分岐だらけのコードは危険
• コントローラやモデルの本来の役割が見えにくくなる。
• 古いコードが山積するといずれ誰も手に負えなくなる。
• 下位バージョンのサポートを切るときにも不必要なコー
ドが残りがち。
「OSの最新バージョンだけサポートしてればいいじゃん」
2015/02 時点でも iOS 8 のシェアは 72%。
100万 ユーザいれば 28万 は iOS 7 以前。
大きなユーザ数を持つサービスには重大な問題。
As measured by the App Store on February 2, 2015.
どうするか?
「複雑性保存の法則」
タスクの複雑性はある点を超えて
減らすことはできない。
移動のみ可能である。
ラリー・テスラー

インタラクションデザイナ
ソフトウェアの設計についてもあてはまる!
下位互換対応は「隠 」せよ
• コントローラやモデルなどの上位層からはバージョ
ン分岐を意識しなくていいようにする。
• 混み入った分岐はどこかにまとめておいて、いつで
も簡単に切り離せるようにしておく。
• 広範囲で使用されるものでも、依存性は最小限に。
2014-01-15 @ potatotips 3
Method Swizzling で iOS 7 コードを iOS 6 でも動かす
今回のお題
UIWebViewと
WKWebViewの

分岐処理を隠蔽せよ
方針1: ラッパーで包む
IF は WKWebView と共通にする
WKWebView
UIWebView
前処理
iOS 8 以上 iOS 7 以下
WKWebViewWrapper : UIView
class WKWebViewWrapper: UIView {
// どちらか一方はnil
var wkWebView: WKWebView?
var uiWebView: UIWebView?
override init(frame: CGRect) {
super.init(frame: frame)
if NSClassFromString("WKWebView") != nil {
wkWebView = WKWebView(frame: self.bounds)
self.addSubview(wkWebView!)
} else {
uiWebView = UIWebView(frame: self.bounds)
self.addSubview(uiWebView!)
}
}
func loadRequest(request: NSURLRequest) -> WKNavigation? {
if(wkWebView != nil) {
return wkWebView!.loadRequest(request);
} else {
uiWebView!.loadRequest(request)
return nil
}
}
内部の実装イメージ
class WKWebViewWrapper: UIView {
// どちらか一方はnil
var wkWebView: WKWebView?
var uiWebView: UIWebView?
override init(frame: CGRect) {
super.init(frame: frame)
if NSClassFromString("WKWebView") != nil {
wkWebView = WKWebView(frame: self.bounds)
self.addSubview(wkWebView!)
} else {
uiWebView = UIWebView(frame: self.bounds)
self.addSubview(uiWebView!)
}
}
func loadRequest(request: NSURLRequest) -> WKNavigation? {
if(wkWebView != nil) {
return wkWebView!.loadRequest(request);
} else {
uiWebView!.loadRequest(request)
return nil
}
}
内部の実装イメージ
UIWebView / WKWebView の両方を変数で宣言
class WKWebViewWrapper: UIView {
// どちらか一方はnil
var wkWebView: WKWebView?
var uiWebView: UIWebView?
override init(frame: CGRect) {
super.init(frame: frame)
if NSClassFromString("WKWebView") != nil {
wkWebView = WKWebView(frame: self.bounds)
self.addSubview(wkWebView!)
} else {
uiWebView = UIWebView(frame: self.bounds)
self.addSubview(uiWebView!)
}
}
func loadRequest(request: NSURLRequest) -> WKNavigation? {
if(wkWebView != nil) {
return wkWebView!.loadRequest(request);
} else {
uiWebView!.loadRequest(request)
return nil
}
}
内部の実装イメージ
対応/非対応に応じて一方にインスタンスを入れる
class WKWebViewWrapper: UIView {
// どちらか一方はnil
var wkWebView: WKWebView?
var uiWebView: UIWebView?
override init(frame: CGRect) {
super.init(frame: frame)
if NSClassFromString("WKWebView") != nil {
wkWebView = WKWebView(frame: self.bounds)
self.addSubview(wkWebView!)
} else {
uiWebView = UIWebView(frame: self.bounds)
self.addSubview(uiWebView!)
}
}
func loadRequest(request: NSURLRequest) -> WKNavigation? {
if(wkWebView != nil) {
return wkWebView!.loadRequest(request);
} else {
uiWebView!.loadRequest(request)
return nil
}
}
内部の実装イメージ
すべてのメソッドで分岐処理
目的は達成できているのだが、
全メソッドに分岐を入れるのが
あまりエレガントな感じしない。
方針2:
UIWebView に
WKWebView のフリをさせる
WKWebView UIWebView
iOS 8 以上 iOS 7 以下
WKWebView
UIWebView
iOS 8 以上 iOS 7 以下
WKWebView
UIWebView
WKWebView の IF
iOS 8 以上 iOS 7 以下
WKWebView
UIWebView
WKWebView の IF
Controller
iOS 8 以上 iOS 7 以下
外からは同じ型に見える
Obj-C 先輩の力を借りる
えっ
Why Obj-C ?
UIWebView WKWebView
var canGoBack: Bool { get }
var canGoForward: Bool { get }
var loading: Bool { get }
var canGoBack: Bool { get }
var canGoForward: Bool { get }
var loading: Bool { get }
var title: String? { get }
var URL: NSURL? { get }
var estimatedProgress: Double { get }
func loadRequest(request: NSURLRequest)


func reload()
func stopLoading()
func goBack()
func goForward()
func loadRequest(request: NSURLRequest) ->
WKNavigation?
func reload() -> WKNavigation?
func stopLoading()

func goBack() -> WKNavigation?
func goForward() -> WKNavigation?
func stringByEvaluatingJavaScriptFromString
(script: String) -> String?
func evaluateJavaScript(javaScriptString: String,
completionHandler: ((AnyObject!, NSError!) -> Void)?)
weak var delegate: UIWebViewDelegate? weak var navigationDelegate: WKNavigationDelegate?

weak var UIDelegate: WKUIDelegate?
Why Obj-C ?
UIWebView WKWebView
var canGoBack: Bool { get }
var canGoForward: Bool { get }
var loading: Bool { get }
var canGoBack: Bool { get }
var canGoForward: Bool { get }
var loading: Bool { get }
var title: String? { get }
var URL: NSURL? { get }
var estimatedProgress: Double { get }
func loadRequest(request: NSURLRequest)


func reload()
func stopLoading()
func goBack()
func goForward()
func loadRequest(request: NSURLRequest) ->
WKNavigation?
func reload() -> WKNavigation?
func stopLoading()

func goBack() -> WKNavigation?
func goForward() -> WKNavigation?
func stringByEvaluatingJavaScriptFromString
(script: String) -> String?
func evaluateJavaScript(javaScriptString: String,
completionHandler: ((AnyObject!, NSError!) -> Void)?)
weak var delegate: UIWebViewDelegate? weak var navigationDelegate: WKNavigationDelegate?

weak var UIDelegate: WKUIDelegate?
Swift はココの戻り値の型の違いを区別してしまう!
(かなり頑張ったけど無理でした笑)
Obj-C の緩さに甘える
(どうせ隠 するんだしいいでしょ)
…許す
手順1: WKWebView の IF を Protocol として切り出す
// WKWebViewProtocol.swift
#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
@protocol WKWebViewProtocol <NSObject>
@property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack;
@property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward;
@property (nonatomic, readonly, getter=isLoading) BOOL loading;
@property (nonatomic, readonly) NSURL *URL;
@property (nonatomic, readonly, copy) NSString *title;
- (void)loadRequest:(NSURLRequest *)request;
- (void)reload;
- (void)stopLoading;
- (void)goBack;
- (void)goForward;
- (void)evaluateJavaScript:(NSString *)javaScriptString
completionHandler:(void (^)(id, NSError *))completionHandler;
@end
手順1: WKWebView の IF を Protocol として切り出す
// WKWebViewProtocol.swift
#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
@protocol WKWebViewProtocol <NSObject>
@property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack;
@property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward;
@property (nonatomic, readonly, getter=isLoading) BOOL loading;
@property (nonatomic, readonly) NSURL *URL;
@property (nonatomic, readonly, copy) NSString *title;
- (void)loadRequest:(NSURLRequest *)request;
- (void)reload;
- (void)stopLoading;
- (void)goBack;
- (void)goForward;
- (void)evaluateJavaScript:(NSString *)javaScriptString
completionHandler:(void (^)(id, NSError *))completionHandler;
@end
WKWebViewのヘッダファイルからコピペしてくる
手順2: WKWebView の拡張で Protocol に適合
// WKWebView+ProtocolConformed.h
#import "WKWebViewProtocol.h"
@interface WKWebView(YSSWebViewProtocol) <WKWebViewProtocol>
@end
// WKWebView+ProtocolConformed.m
#import "WKWebView+ProtocolConformed.h"
@implementation WKWebView(YSSWebViewProtocol)
@end
手順2: WKWebView の拡張で Protocol に適合
// WKWebView+ProtocolConformed.h
#import "WKWebViewProtocol.h"
@interface WKWebView(YSSWebViewProtocol) <WKWebViewProtocol>
@end
// WKWebView+ProtocolConformed.m
#import "WKWebView+ProtocolConformed.h"
@implementation WKWebView(YSSWebViewProtocol)
@end
Protocol の適合を宣言
手順2: WKWebView の拡張で Protocol に適合
// WKWebView+ProtocolConformed.h
#import "WKWebViewProtocol.h"
@interface WKWebView(YSSWebViewProtocol) <WKWebViewProtocol>
@end
// WKWebView+ProtocolConformed.m
#import "WKWebView+ProtocolConformed.h"
@implementation WKWebView(YSSWebViewProtocol)
@end
元から実装されているので拡張は何もいらない!
手順3: 同様に UIWebView の拡張も作成
// UIWebView+WKProtocolConformed.h
#import "WKWebViewProtocol.h"
@interface UIWebView (WKProtocolConformed) <WKWebViewProtocol>
@end
手順3: 同様に UIWebView の拡張も作成
// UIWebView+WKProtocolConformed.h
#import "WKWebViewProtocol.h"
@interface UIWebView (WKProtocolConformed) <WKWebViewProtocol>
@end 同様に WKWebView の Protocol 適合を宣言
UIWebView には足りないメソッドを実装する!
// UIWebView+WKProtocolConformed.m
#import "UIWebView+WKProtocolConformed.h"
@implementation UIWebView (WKProtocolConformed)
- (NSURL *)URL
{
NSString *URLString = [self stringByEvaluatingJavaScriptFromString:@"document.URL"];
return [NSURL URLWithString:URLString];
}
- (NSString *)title
{
return [self stringByEvaluatingJavaScriptFromString:@"document.title"];
}
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id,
NSError *))completionHandler
{
NSString *result = [self stringByEvaluatingJavaScriptFromString:javaScriptString];
if(completionHandler) {
completionHandler(result, nil);
}
}
@end
UIWebView には足りないメソッドを実装する!
// UIWebView+WKProtocolConformed.m
#import "UIWebView+WKProtocolConformed.h"
@implementation UIWebView (WKProtocolConformed)
- (NSURL *)URL
{
NSString *URLString = [self stringByEvaluatingJavaScriptFromString:@"document.URL"];
return [NSURL URLWithString:URLString];
}
- (NSString *)title
{
return [self stringByEvaluatingJavaScriptFromString:@"document.title"];
}
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id,
NSError *))completionHandler
{
NSString *result = [self stringByEvaluatingJavaScriptFromString:javaScriptString];
if(completionHandler) {
completionHandler(result, nil);
}
}
@end loadRequest: などは元から実装されているのでスルー!
準備はここまで
(Obj-Cのことはもう忘れてOK)
Controller で WKWebViewProtocol 型として webView を保持
// MyViewController.swift
import UIKit
import WebKit
class ViewController: UIViewController {
var webView: WKWebViewProtocol!
override func viewDidLoad() {
super.viewDidLoad()
self.createWebView()
}
func createWebView() {
if NSClassFromString("WKWebView") != nil {
let wkWebView = WKWebView(frame: self.view.bounds)
self.view.addSubview(wkWebView)
webView = wkWebView
} else {
let uiWebView = UIWebView(frame: self.view.bounds)
self.view.addSubview(uiWebView)
webView = uiWebView as WKWebViewProtocol
}
}
…(続く)
Controller で WKWebViewProtocol 型として webView を保持
// MyViewController.swift
import UIKit
import WebKit
class ViewController: UIViewController {
var webView: WKWebViewProtocol!
override func viewDidLoad() {
super.viewDidLoad()
self.createWebView()
}
func createWebView() {
if NSClassFromString("WKWebView") != nil {
let wkWebView = WKWebView(frame: self.view.bounds)
self.view.addSubview(wkWebView)
webView = wkWebView
} else {
let uiWebView = UIWebView(frame: self.view.bounds)
self.view.addSubview(uiWebView)
webView = uiWebView as WKWebViewProtocol
}
}
…(続く)
WKWebViewProtocol! 型として1個だけ変数を保持!
Controller で WKWebViewProtocol 型として webView を保持
// MyViewController.swift
import UIKit
import WebKit
class ViewController: UIViewController {
var webView: WKWebViewProtocol!
override func viewDidLoad() {
super.viewDidLoad()
self.createWebView()
}
func createWebView() {
if NSClassFromString("WKWebView") != nil {
let wkWebView = WKWebView(frame: self.view.bounds)
self.view.addSubview(wkWebView)
webView = wkWebView
} else {
let uiWebView = UIWebView(frame: self.view.bounds)
self.view.addSubview(uiWebView)
webView = uiWebView as WKWebViewProtocol
}
}
…(続く)
インスタンス化の後は、WK / UI 共に変数 webView に格納できる!
Controller で WKWebViewProtocol 型として webView を保持
// MyViewController.swift
…(続き)
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let URL = NSURL(string: "http://yahoo.co.jp")
let req = NSURLRequest(URL: URL!)
webView.loadRequest(req)
}
}
Controller で WKWebViewProtocol 型として webView を保持
// MyViewController.swift
…(続き)
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let URL = NSURL(string: "http://yahoo.co.jp")
let req = NSURLRequest(URL: URL!)
webView.loadRequest(req)
}
}
以後は何の区別もなく同じ webView として扱える!
DEMO
方針1:
Wrapper で包む
方針2:
UIWebViewをいじる
• 特別なことをしていないので安心。
• だいぶ裏技っぽい。
(戻り値型を捻じ曲げてる辺り特に)
• 全メソッドを実装して、それぞれ分岐
処理を書くのが面倒。
• UIWebViewで足りないメソッドだけ実
装すれば良い。
いつか iOS 7 のサポートを切るとき、
UIWebView と気持ち良く別れられるように。
2014年12月 5日 Yahoo! JAPAN Tech Blog
let UIWebView as WKWebView
Thanks!
Blog: http://taketo1024.hateblo.jp
Twitter: @taketo1024

More Related Content

What's hot

Service workerとwebプッシュ通知
Service workerとwebプッシュ通知Service workerとwebプッシュ通知
Service workerとwebプッシュ通知zaru sakuraba
 
React入門-JSONを取得して表示する
React入門-JSONを取得して表示するReact入門-JSONを取得して表示する
React入門-JSONを取得して表示するregret raym
 
React Native GUIDE
React Native GUIDEReact Native GUIDE
React Native GUIDEdcubeio
 
React.jsでクライアントサイドなWebアプリ入門
React.jsでクライアントサイドなWebアプリ入門React.jsでクライアントサイドなWebアプリ入門
React.jsでクライアントサイドなWebアプリ入門spring_raining
 
HTML5の前のJavaScript入門
HTML5の前のJavaScript入門HTML5の前のJavaScript入門
HTML5の前のJavaScript入門Hiroki Toyokawa
 
Service worker が拓く mobile web の新しいかたち
Service worker が拓く mobile web の新しいかたちService worker が拓く mobile web の新しいかたち
Service worker が拓く mobile web の新しいかたちKinuko Yasuda
 
iOS 9 Bootcamp #6 UIKit
iOS 9 Bootcamp #6 UIKitiOS 9 Bootcamp #6 UIKit
iOS 9 Bootcamp #6 UIKitShingo Hiraya
 
SpringOne 2GX 2014 参加報告 & Spring 4.1について #jsug
SpringOne 2GX 2014 参加報告 & Spring 4.1について #jsugSpringOne 2GX 2014 参加報告 & Spring 4.1について #jsug
SpringOne 2GX 2014 参加報告 & Spring 4.1について #jsugToshiaki Maki
 
REST with Spring Boot #jqfk
REST with Spring Boot #jqfkREST with Spring Boot #jqfk
REST with Spring Boot #jqfkToshiaki Maki
 
Web workers&parallel.js html5勉強会lt大会
Web workers&parallel.js   html5勉強会lt大会Web workers&parallel.js   html5勉強会lt大会
Web workers&parallel.js html5勉強会lt大会Yuta Shimakawa
 
React を導入した フロントエンド開発
React を導入したフロントエンド開発React を導入したフロントエンド開発
React を導入した フロントエンド開発 daisuke-a-matsui
 
Spring Bootで変わる Javaアプリ開発! #jsug
Spring Bootで変わる Javaアプリ開発! #jsugSpring Bootで変わる Javaアプリ開発! #jsug
Spring Bootで変わる Javaアプリ開発! #jsugToshiaki Maki
 
【Camphor ×サイボウズ】selenium勉強会
【Camphor ×サイボウズ】selenium勉強会【Camphor ×サイボウズ】selenium勉強会
【Camphor ×サイボウズ】selenium勉強会Yuki Okada
 
React+TypeScriptもいいぞ
React+TypeScriptもいいぞReact+TypeScriptもいいぞ
React+TypeScriptもいいぞMitsuru Ogawa
 
コンポーネント指向による、Reactのベストプラクティスとバッドプラクティス
コンポーネント指向による、Reactのベストプラクティスとバッドプラクティスコンポーネント指向による、Reactのベストプラクティスとバッドプラクティス
コンポーネント指向による、ReactのベストプラクティスとバッドプラクティスKohei Asai
 
はじめよう Backbone.js
はじめよう Backbone.jsはじめよう Backbone.js
はじめよう Backbone.jsHiroki Toyokawa
 

What's hot (20)

Service workerとwebプッシュ通知
Service workerとwebプッシュ通知Service workerとwebプッシュ通知
Service workerとwebプッシュ通知
 
React入門-JSONを取得して表示する
React入門-JSONを取得して表示するReact入門-JSONを取得して表示する
React入門-JSONを取得して表示する
 
REACT & WEB API
REACT & WEB APIREACT & WEB API
REACT & WEB API
 
One night Vue.js
One night Vue.jsOne night Vue.js
One night Vue.js
 
React Native GUIDE
React Native GUIDEReact Native GUIDE
React Native GUIDE
 
React.jsでクライアントサイドなWebアプリ入門
React.jsでクライアントサイドなWebアプリ入門React.jsでクライアントサイドなWebアプリ入門
React.jsでクライアントサイドなWebアプリ入門
 
HTML5の前のJavaScript入門
HTML5の前のJavaScript入門HTML5の前のJavaScript入門
HTML5の前のJavaScript入門
 
Service worker が拓く mobile web の新しいかたち
Service worker が拓く mobile web の新しいかたちService worker が拓く mobile web の新しいかたち
Service worker が拓く mobile web の新しいかたち
 
iOS 9 Bootcamp #6 UIKit
iOS 9 Bootcamp #6 UIKitiOS 9 Bootcamp #6 UIKit
iOS 9 Bootcamp #6 UIKit
 
SpringOne 2GX 2014 参加報告 & Spring 4.1について #jsug
SpringOne 2GX 2014 参加報告 & Spring 4.1について #jsugSpringOne 2GX 2014 参加報告 & Spring 4.1について #jsug
SpringOne 2GX 2014 参加報告 & Spring 4.1について #jsug
 
REST with Spring Boot #jqfk
REST with Spring Boot #jqfkREST with Spring Boot #jqfk
REST with Spring Boot #jqfk
 
Web workers&parallel.js html5勉強会lt大会
Web workers&parallel.js   html5勉強会lt大会Web workers&parallel.js   html5勉強会lt大会
Web workers&parallel.js html5勉強会lt大会
 
React を導入した フロントエンド開発
React を導入したフロントエンド開発React を導入したフロントエンド開発
React を導入した フロントエンド開発
 
Spring Bootで変わる Javaアプリ開発! #jsug
Spring Bootで変わる Javaアプリ開発! #jsugSpring Bootで変わる Javaアプリ開発! #jsug
Spring Bootで変わる Javaアプリ開発! #jsug
 
Service Workerとの戦い ~ 実装編 ~ #scripty03
Service Workerとの戦い ~ 実装編 ~ #scripty03Service Workerとの戦い ~ 実装編 ~ #scripty03
Service Workerとの戦い ~ 実装編 ~ #scripty03
 
【Camphor ×サイボウズ】selenium勉強会
【Camphor ×サイボウズ】selenium勉強会【Camphor ×サイボウズ】selenium勉強会
【Camphor ×サイボウズ】selenium勉強会
 
React+TypeScriptもいいぞ
React+TypeScriptもいいぞReact+TypeScriptもいいぞ
React+TypeScriptもいいぞ
 
コンポーネント指向による、Reactのベストプラクティスとバッドプラクティス
コンポーネント指向による、Reactのベストプラクティスとバッドプラクティスコンポーネント指向による、Reactのベストプラクティスとバッドプラクティス
コンポーネント指向による、Reactのベストプラクティスとバッドプラクティス
 
はじめよう Backbone.js
はじめよう Backbone.jsはじめよう Backbone.js
はじめよう Backbone.js
 
20120128
2012012820120128
20120128
 

Similar to let UIWebView as WKWebView

Web area 2013-07-16
Web area 2013-07-16Web area 2013-07-16
Web area 2013-07-16kmiyako
 
みんなでWAIWAI Webアプリ
みんなでWAIWAI WebアプリみんなでWAIWAI Webアプリ
みんなでWAIWAI WebアプリKiwamu Okabe
 
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみたYuki Takei
 
Xamarin で ReactiveUI を使ってみた
Xamarin で ReactiveUI を使ってみたXamarin で ReactiveUI を使ってみた
Xamarin で ReactiveUI を使ってみたHironov OKUYAMA
 
cordova/electronの構造を知る
cordova/electronの構造を知るcordova/electronの構造を知る
cordova/electronの構造を知るYasuharu Seki
 
Browser Computing Structure
Browser Computing StructureBrowser Computing Structure
Browser Computing StructureShogo Sensui
 
Head toward Java 16 (Night Seminar Edition)
Head toward Java 16 (Night Seminar Edition)Head toward Java 16 (Night Seminar Edition)
Head toward Java 16 (Night Seminar Edition)Yuji Kubota
 
第4回REST勉強会 RequireJS編
第4回REST勉強会 RequireJS編第4回REST勉強会 RequireJS編
第4回REST勉強会 RequireJS編ksimoji
 
レスポンシブWebデザイン【発展編】
レスポンシブWebデザイン【発展編】レスポンシブWebデザイン【発展編】
レスポンシブWebデザイン【発展編】Yasuhito Yabe
 
iTamabi 13 第9回:ARTSAT API 実践 3 ジオコーディングで衛星の位置を取得
iTamabi 13 第9回:ARTSAT API 実践 3 ジオコーディングで衛星の位置を取得iTamabi 13 第9回:ARTSAT API 実践 3 ジオコーディングで衛星の位置を取得
iTamabi 13 第9回:ARTSAT API 実践 3 ジオコーディングで衛星の位置を取得Atsushi Tadokoro
 
WebViewでエラーページを表示する話
WebViewでエラーページを表示する話WebViewでエラーページを表示する話
WebViewでエラーページを表示する話Tetsuya Kaneuchi
 
Functional JavaScript with Lo-Dash.js
Functional JavaScript with Lo-Dash.jsFunctional JavaScript with Lo-Dash.js
Functional JavaScript with Lo-Dash.jsShogo Sensui
 
アメブロ2016 アメブロフロント刷新にみる ひかりとつらみ
アメブロ2016 アメブロフロント刷新にみる ひかりとつらみアメブロ2016 アメブロフロント刷新にみる ひかりとつらみ
アメブロ2016 アメブロフロント刷新にみる ひかりとつらみKazunari Hara
 
WordBench京都12月、WordCampUSからのWP REST APIな話
WordBench京都12月、WordCampUSからのWP REST APIな話WordBench京都12月、WordCampUSからのWP REST APIな話
WordBench京都12月、WordCampUSからのWP REST APIな話Hidetaka Okamoto
 
Angular js はまりどころ
Angular js はまりどころAngular js はまりどころ
Angular js はまりどころAyumi Goto
 
Couch Db勉強会0623 by yssk22
Couch Db勉強会0623 by yssk22Couch Db勉強会0623 by yssk22
Couch Db勉強会0623 by yssk22Yohei Sasaki
 
今さら始めるCoffeeScript
今さら始めるCoffeeScript今さら始めるCoffeeScript
今さら始めるCoffeeScriptAshitaba YOSHIOKA
 
何が変わった JavaFX 2.0
何が変わった JavaFX 2.0何が変わった JavaFX 2.0
何が変わった JavaFX 2.0Yuichi Sakuraba
 

Similar to let UIWebView as WKWebView (20)

Web area 2013-07-16
Web area 2013-07-16Web area 2013-07-16
Web area 2013-07-16
 
みんなでWAIWAI Webアプリ
みんなでWAIWAI WebアプリみんなでWAIWAI Webアプリ
みんなでWAIWAI Webアプリ
 
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
 
Xamarin で ReactiveUI を使ってみた
Xamarin で ReactiveUI を使ってみたXamarin で ReactiveUI を使ってみた
Xamarin で ReactiveUI を使ってみた
 
cordova/electronの構造を知る
cordova/electronの構造を知るcordova/electronの構造を知る
cordova/electronの構造を知る
 
Browser Computing Structure
Browser Computing StructureBrowser Computing Structure
Browser Computing Structure
 
Head toward Java 16 (Night Seminar Edition)
Head toward Java 16 (Night Seminar Edition)Head toward Java 16 (Night Seminar Edition)
Head toward Java 16 (Night Seminar Edition)
 
覚醒!JavaScript
覚醒!JavaScript覚醒!JavaScript
覚醒!JavaScript
 
第4回REST勉強会 RequireJS編
第4回REST勉強会 RequireJS編第4回REST勉強会 RequireJS編
第4回REST勉強会 RequireJS編
 
レスポンシブWebデザイン【発展編】
レスポンシブWebデザイン【発展編】レスポンシブWebデザイン【発展編】
レスポンシブWebデザイン【発展編】
 
iTamabi 13 第9回:ARTSAT API 実践 3 ジオコーディングで衛星の位置を取得
iTamabi 13 第9回:ARTSAT API 実践 3 ジオコーディングで衛星の位置を取得iTamabi 13 第9回:ARTSAT API 実践 3 ジオコーディングで衛星の位置を取得
iTamabi 13 第9回:ARTSAT API 実践 3 ジオコーディングで衛星の位置を取得
 
WebViewでエラーページを表示する話
WebViewでエラーページを表示する話WebViewでエラーページを表示する話
WebViewでエラーページを表示する話
 
Functional JavaScript with Lo-Dash.js
Functional JavaScript with Lo-Dash.jsFunctional JavaScript with Lo-Dash.js
Functional JavaScript with Lo-Dash.js
 
アメブロ2016 アメブロフロント刷新にみる ひかりとつらみ
アメブロ2016 アメブロフロント刷新にみる ひかりとつらみアメブロ2016 アメブロフロント刷新にみる ひかりとつらみ
アメブロ2016 アメブロフロント刷新にみる ひかりとつらみ
 
WordBench京都12月、WordCampUSからのWP REST APIな話
WordBench京都12月、WordCampUSからのWP REST APIな話WordBench京都12月、WordCampUSからのWP REST APIな話
WordBench京都12月、WordCampUSからのWP REST APIな話
 
Swiftyを試す
Swiftyを試すSwiftyを試す
Swiftyを試す
 
Angular js はまりどころ
Angular js はまりどころAngular js はまりどころ
Angular js はまりどころ
 
Couch Db勉強会0623 by yssk22
Couch Db勉強会0623 by yssk22Couch Db勉強会0623 by yssk22
Couch Db勉強会0623 by yssk22
 
今さら始めるCoffeeScript
今さら始めるCoffeeScript今さら始めるCoffeeScript
今さら始めるCoffeeScript
 
何が変わった JavaFX 2.0
何が変わった JavaFX 2.0何が変わった JavaFX 2.0
何が変わった JavaFX 2.0
 

More from Taketo Sano

Divisibility of Lee’s class and its relation with Rasmussen’s invariant / 201...
Divisibility of Lee’s class and its relation with Rasmussen’s invariant / 201...Divisibility of Lee’s class and its relation with Rasmussen’s invariant / 201...
Divisibility of Lee’s class and its relation with Rasmussen’s invariant / 201...Taketo Sano
 
トポロジーと圏論の夜明け
トポロジーと圏論の夜明けトポロジーと圏論の夜明け
トポロジーと圏論の夜明けTaketo Sano
 
Swift で数学研究のススメ
Swift で数学研究のススメSwift で数学研究のススメ
Swift で数学研究のススメTaketo Sano
 
(意欲的な中高生のための)トポロジー・圏論・コンピュータ
(意欲的な中高生のための)トポロジー・圏論・コンピュータ(意欲的な中高生のための)トポロジー・圏論・コンピュータ
(意欲的な中高生のための)トポロジー・圏論・コンピュータTaketo Sano
 
特性類の気持ち
特性類の気持ち特性類の気持ち
特性類の気持ちTaketo Sano
 
Swift で数学のススメ 〜 プログラミングと数学は同時に学べ
Swift で数学のススメ 〜 プログラミングと数学は同時に学べSwift で数学のススメ 〜 プログラミングと数学は同時に学べ
Swift で数学のススメ 〜 プログラミングと数学は同時に学べTaketo Sano
 
山手線は丸いのか?プログラマのためのトポロジー入門
山手線は丸いのか?プログラマのためのトポロジー入門山手線は丸いのか?プログラマのためのトポロジー入門
山手線は丸いのか?プログラマのためのトポロジー入門Taketo Sano
 
何もないところから数を作る
何もないところから数を作る何もないところから数を作る
何もないところから数を作るTaketo Sano
 
「数える」とは何か? 〜 「とは何か?」を問う、AI時代の数学
「数える」とは何か? 〜 「とは何か?」を問う、AI時代の数学「数える」とは何か? 〜 「とは何か?」を問う、AI時代の数学
「数える」とは何か? 〜 「とは何か?」を問う、AI時代の数学Taketo Sano
 
情報幾何学 #2.4
情報幾何学 #2.4情報幾何学 #2.4
情報幾何学 #2.4Taketo Sano
 
情報幾何学 #2 #infogeo16
情報幾何学 #2 #infogeo16情報幾何学 #2 #infogeo16
情報幾何学 #2 #infogeo16Taketo Sano
 
objc2swift 〜 Objective-C から Swift への「コード&パラダイム」シフト
objc2swift 〜 Objective-C から Swift への「コード&パラダイム」シフトobjc2swift 〜 Objective-C から Swift への「コード&パラダイム」シフト
objc2swift 〜 Objective-C から Swift への「コード&パラダイム」シフトTaketo Sano
 
何もないところから数を作る
何もないところから数を作る何もないところから数を作る
何もないところから数を作るTaketo Sano
 
objc2swift (続・自動変換の野望)
objc2swift (続・自動変換の野望) objc2swift (続・自動変換の野望)
objc2swift (続・自動変換の野望) Taketo Sano
 
さらに上を目指すための iOS アプリ設計
さらに上を目指すための iOS アプリ設計さらに上を目指すための iOS アプリ設計
さらに上を目指すための iOS アプリ設計Taketo Sano
 
基底変換、固有値・固有ベクトル、そしてその先
基底変換、固有値・固有ベクトル、そしてその先基底変換、固有値・固有ベクトル、そしてその先
基底変換、固有値・固有ベクトル、そしてその先Taketo Sano
 
objc2swift (自動変換の野望)
objc2swift (自動変換の野望)objc2swift (自動変換の野望)
objc2swift (自動変換の野望)Taketo Sano
 
プログラマのための線形代数再入門2 〜 要件定義から学ぶ行列式と逆行列
プログラマのための線形代数再入門2 〜 要件定義から学ぶ行列式と逆行列プログラマのための線形代数再入門2 〜 要件定義から学ぶ行列式と逆行列
プログラマのための線形代数再入門2 〜 要件定義から学ぶ行列式と逆行列Taketo Sano
 
2015 02-18 xxx-literalconvertible
2015 02-18 xxx-literalconvertible2015 02-18 xxx-literalconvertible
2015 02-18 xxx-literalconvertibleTaketo Sano
 
プログラマのための線形代数再入門
プログラマのための線形代数再入門プログラマのための線形代数再入門
プログラマのための線形代数再入門Taketo Sano
 

More from Taketo Sano (20)

Divisibility of Lee’s class and its relation with Rasmussen’s invariant / 201...
Divisibility of Lee’s class and its relation with Rasmussen’s invariant / 201...Divisibility of Lee’s class and its relation with Rasmussen’s invariant / 201...
Divisibility of Lee’s class and its relation with Rasmussen’s invariant / 201...
 
トポロジーと圏論の夜明け
トポロジーと圏論の夜明けトポロジーと圏論の夜明け
トポロジーと圏論の夜明け
 
Swift で数学研究のススメ
Swift で数学研究のススメSwift で数学研究のススメ
Swift で数学研究のススメ
 
(意欲的な中高生のための)トポロジー・圏論・コンピュータ
(意欲的な中高生のための)トポロジー・圏論・コンピュータ(意欲的な中高生のための)トポロジー・圏論・コンピュータ
(意欲的な中高生のための)トポロジー・圏論・コンピュータ
 
特性類の気持ち
特性類の気持ち特性類の気持ち
特性類の気持ち
 
Swift で数学のススメ 〜 プログラミングと数学は同時に学べ
Swift で数学のススメ 〜 プログラミングと数学は同時に学べSwift で数学のススメ 〜 プログラミングと数学は同時に学べ
Swift で数学のススメ 〜 プログラミングと数学は同時に学べ
 
山手線は丸いのか?プログラマのためのトポロジー入門
山手線は丸いのか?プログラマのためのトポロジー入門山手線は丸いのか?プログラマのためのトポロジー入門
山手線は丸いのか?プログラマのためのトポロジー入門
 
何もないところから数を作る
何もないところから数を作る何もないところから数を作る
何もないところから数を作る
 
「数える」とは何か? 〜 「とは何か?」を問う、AI時代の数学
「数える」とは何か? 〜 「とは何か?」を問う、AI時代の数学「数える」とは何か? 〜 「とは何か?」を問う、AI時代の数学
「数える」とは何か? 〜 「とは何か?」を問う、AI時代の数学
 
情報幾何学 #2.4
情報幾何学 #2.4情報幾何学 #2.4
情報幾何学 #2.4
 
情報幾何学 #2 #infogeo16
情報幾何学 #2 #infogeo16情報幾何学 #2 #infogeo16
情報幾何学 #2 #infogeo16
 
objc2swift 〜 Objective-C から Swift への「コード&パラダイム」シフト
objc2swift 〜 Objective-C から Swift への「コード&パラダイム」シフトobjc2swift 〜 Objective-C から Swift への「コード&パラダイム」シフト
objc2swift 〜 Objective-C から Swift への「コード&パラダイム」シフト
 
何もないところから数を作る
何もないところから数を作る何もないところから数を作る
何もないところから数を作る
 
objc2swift (続・自動変換の野望)
objc2swift (続・自動変換の野望) objc2swift (続・自動変換の野望)
objc2swift (続・自動変換の野望)
 
さらに上を目指すための iOS アプリ設計
さらに上を目指すための iOS アプリ設計さらに上を目指すための iOS アプリ設計
さらに上を目指すための iOS アプリ設計
 
基底変換、固有値・固有ベクトル、そしてその先
基底変換、固有値・固有ベクトル、そしてその先基底変換、固有値・固有ベクトル、そしてその先
基底変換、固有値・固有ベクトル、そしてその先
 
objc2swift (自動変換の野望)
objc2swift (自動変換の野望)objc2swift (自動変換の野望)
objc2swift (自動変換の野望)
 
プログラマのための線形代数再入門2 〜 要件定義から学ぶ行列式と逆行列
プログラマのための線形代数再入門2 〜 要件定義から学ぶ行列式と逆行列プログラマのための線形代数再入門2 〜 要件定義から学ぶ行列式と逆行列
プログラマのための線形代数再入門2 〜 要件定義から学ぶ行列式と逆行列
 
2015 02-18 xxx-literalconvertible
2015 02-18 xxx-literalconvertible2015 02-18 xxx-literalconvertible
2015 02-18 xxx-literalconvertible
 
プログラマのための線形代数再入門
プログラマのための線形代数再入門プログラマのための線形代数再入門
プログラマのための線形代数再入門
 

let UIWebView as WKWebView

  • 2.
  • 4.
  • 6. from: WWDC2014 Introducing the Modern WebKit API
  • 7. from: WWDC2014 Introducing the Modern WebKit API • iOS 2.0 からお馴染み • JavaScript が Safari より遅い
  • 8. from: WWDC2014 Introducing the Modern WebKit API • iOS 2.0 からお馴染み • JavaScript が Safari より遅い • iOS 8.0 から導入 • Safari と同じ JavaScript エンジン! • ページ遷移ジェスチャーにも対応 • その他も何かと高性能で良い
  • 10. でもまだ iOS 7 を切るわけには行かない…
  • 11. UIWebView (親: UIView) WKWebView (親: UIView) var canGoBack: Bool { get } var canGoForward: Bool { get } var loading: Bool { get } var canGoBack: Bool { get } var canGoForward: Bool { get } var loading: Bool { get } var title: String? { get } var URL: NSURL? { get } var estimatedProgress: Double { get } func loadRequest(request: NSURLRequest) 
 func reload() func stopLoading() func goBack() func goForward() func loadRequest(request: NSURLRequest) -> WKNavigation? func reload() -> WKNavigation? func stopLoading()
 func goBack() -> WKNavigation? func goForward() -> WKNavigation? func stringByEvaluatingJavaScriptFromString (script: String) -> String? func evaluateJavaScript(javaScriptString: String, completionHandler: ((AnyObject!, NSError!) -> Void)?) weak var delegate: UIWebViewDelegate? weak var navigationDelegate: WKNavigationDelegate?
 weak var UIDelegate: WKUIDelegate? インターフェースは似ているが互換性はない
  • 12. UIWebView (親: UIView) WKWebView (親: UIView) var canGoBack: Bool { get } var canGoForward: Bool { get } var loading: Bool { get } var canGoBack: Bool { get } var canGoForward: Bool { get } var loading: Bool { get } var title: String? { get } var URL: NSURL? { get } var estimatedProgress: Double { get } func loadRequest(request: NSURLRequest) 
 func reload() func stopLoading() func goBack() func goForward() func loadRequest(request: NSURLRequest) -> WKNavigation? func reload() -> WKNavigation? func stopLoading()
 func goBack() -> WKNavigation? func goForward() -> WKNavigation? func stringByEvaluatingJavaScriptFromString (script: String) -> String? func evaluateJavaScript(javaScriptString: String, completionHandler: ((AnyObject!, NSError!) -> Void)?) weak var delegate: UIWebViewDelegate? weak var navigationDelegate: WKNavigationDelegate?
 weak var UIDelegate: WKUIDelegate? インターフェースは似ているが互換性はない WKWebView にしかないプロパティ
  • 13. UIWebView (親: UIView) WKWebView (親: UIView) var canGoBack: Bool { get } var canGoForward: Bool { get } var loading: Bool { get } var canGoBack: Bool { get } var canGoForward: Bool { get } var loading: Bool { get } var title: String? { get } var URL: NSURL? { get } var estimatedProgress: Double { get } func loadRequest(request: NSURLRequest) 
 func reload() func stopLoading() func goBack() func goForward() func loadRequest(request: NSURLRequest) -> WKNavigation? func reload() -> WKNavigation? func stopLoading()
 func goBack() -> WKNavigation? func goForward() -> WKNavigation? func stringByEvaluatingJavaScriptFromString (script: String) -> String? func evaluateJavaScript(javaScriptString: String, completionHandler: ((AnyObject!, NSError!) -> Void)?) weak var delegate: UIWebViewDelegate? weak var navigationDelegate: WKNavigationDelegate?
 weak var UIDelegate: WKUIDelegate? インターフェースは似ているが互換性はない load系 は WKNavigation? 型の戻り値がある
  • 14. UIWebView (親: UIView) WKWebView (親: UIView) var canGoBack: Bool { get } var canGoForward: Bool { get } var loading: Bool { get } var canGoBack: Bool { get } var canGoForward: Bool { get } var loading: Bool { get } var title: String? { get } var URL: NSURL? { get } var estimatedProgress: Double { get } func loadRequest(request: NSURLRequest) 
 func reload() func stopLoading() func goBack() func goForward() func loadRequest(request: NSURLRequest) -> WKNavigation? func reload() -> WKNavigation? func stopLoading()
 func goBack() -> WKNavigation? func goForward() -> WKNavigation? func stringByEvaluatingJavaScriptFromString (script: String) -> String? func evaluateJavaScript(javaScriptString: String, completionHandler: ((AnyObject!, NSError!) -> Void)?) weak var delegate: UIWebViewDelegate? weak var navigationDelegate: WKNavigationDelegate?
 weak var UIDelegate: WKUIDelegate? インターフェースは似ているが互換性はない JavaScript はコールバック形式
  • 15. UIWebView (親: UIView) WKWebView (親: UIView) var canGoBack: Bool { get } var canGoForward: Bool { get } var loading: Bool { get } var canGoBack: Bool { get } var canGoForward: Bool { get } var loading: Bool { get } var title: String? { get } var URL: NSURL? { get } var estimatedProgress: Double { get } func loadRequest(request: NSURLRequest) 
 func reload() func stopLoading() func goBack() func goForward() func loadRequest(request: NSURLRequest) -> WKNavigation? func reload() -> WKNavigation? func stopLoading()
 func goBack() -> WKNavigation? func goForward() -> WKNavigation? func stringByEvaluatingJavaScriptFromString (script: String) -> String? func evaluateJavaScript(javaScriptString: String, completionHandler: ((AnyObject!, NSError!) -> Void)?) weak var delegate: UIWebViewDelegate? weak var navigationDelegate: WKNavigationDelegate?
 weak var UIDelegate: WKUIDelegate? インターフェースは似ているが互換性はない delegateが2種類ある(IFもだいぶ違う)
  • 16. class MyViewController: UIViewController { var wkWebView: WKWebView? var uiWebView: UIWebView? override func viewDidLoad() { super.viewDidLoad() if NSClassFromString("WKWebView") != nil { wkWebView = WKWebView(frame: ) self.view.addSubview(wkWebView!) } else { uiWebView = UIWebView(frame: ) self.view.addSubview(uiWebView!) } } override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) let URL = NSURL(string: "http://yahoo.co.jp") let req = NSURLRequest(URL: URL!) if(wkWebView != nil) { wkWebView!.loadRequest(req) } else { uiWebView!.loadRequest(req) } } ナイーブな下位互換対応をすると…
  • 17. class MyViewController: UIViewController { var wkWebView: WKWebView? var uiWebView: UIWebView? override func viewDidLoad() { super.viewDidLoad() if NSClassFromString("WKWebView") != nil { wkWebView = WKWebView(frame: ) self.view.addSubview(wkWebView!) } else { uiWebView = UIWebView(frame: ) self.view.addSubview(uiWebView!) } } override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) let URL = NSURL(string: "http://yahoo.co.jp") let req = NSURLRequest(URL: URL!) if(wkWebView != nil) { wkWebView!.loadRequest(req) } else { uiWebView!.loadRequest(req) } } ナイーブな下位互換対応をすると… UIWebView / WKWebView の両方を変数で宣言
  • 18. class MyViewController: UIViewController { var wkWebView: WKWebView? var uiWebView: UIWebView? override func viewDidLoad() { super.viewDidLoad() if NSClassFromString("WKWebView") != nil { wkWebView = WKWebView(frame: ) self.view.addSubview(wkWebView!) } else { uiWebView = UIWebView(frame: ) self.view.addSubview(uiWebView!) } } override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) let URL = NSURL(string: "http://yahoo.co.jp") let req = NSURLRequest(URL: URL!) if(wkWebView != nil) { wkWebView!.loadRequest(req) } else { uiWebView!.loadRequest(req) } } ナイーブな下位互換対応をすると… 対応/非対応に応じて一方にインスタンスを入れる
  • 19. class MyViewController: UIViewController { var wkWebView: WKWebView? var uiWebView: UIWebView? override func viewDidLoad() { super.viewDidLoad() if NSClassFromString("WKWebView") != nil { wkWebView = WKWebView(frame: ) self.view.addSubview(wkWebView!) } else { uiWebView = UIWebView(frame: ) self.view.addSubview(uiWebView!) } } override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) let URL = NSURL(string: "http://yahoo.co.jp") let req = NSURLRequest(URL: URL!) if(wkWebView != nil) { wkWebView!.loadRequest(req) } else { uiWebView!.loadRequest(req) } } ナイーブな下位互換対応をすると… そして毎回分岐して同じ処理を2度書くことに…
  • 23. 2015/02 時点でも iOS 8 のシェアは 72%。 100万 ユーザいれば 28万 は iOS 7 以前。 大きなユーザ数を持つサービスには重大な問題。 As measured by the App Store on February 2, 2015.
  • 26. 下位互換対応は「隠 」せよ • コントローラやモデルなどの上位層からはバージョ ン分岐を意識しなくていいようにする。 • 混み入った分岐はどこかにまとめておいて、いつで も簡単に切り離せるようにしておく。 • 広範囲で使用されるものでも、依存性は最小限に。
  • 27. 2014-01-15 @ potatotips 3 Method Swizzling で iOS 7 コードを iOS 6 でも動かす
  • 29. 方針1: ラッパーで包む IF は WKWebView と共通にする WKWebView UIWebView 前処理 iOS 8 以上 iOS 7 以下 WKWebViewWrapper : UIView
  • 30. class WKWebViewWrapper: UIView { // どちらか一方はnil var wkWebView: WKWebView? var uiWebView: UIWebView? override init(frame: CGRect) { super.init(frame: frame) if NSClassFromString("WKWebView") != nil { wkWebView = WKWebView(frame: self.bounds) self.addSubview(wkWebView!) } else { uiWebView = UIWebView(frame: self.bounds) self.addSubview(uiWebView!) } } func loadRequest(request: NSURLRequest) -> WKNavigation? { if(wkWebView != nil) { return wkWebView!.loadRequest(request); } else { uiWebView!.loadRequest(request) return nil } } 内部の実装イメージ
  • 31. class WKWebViewWrapper: UIView { // どちらか一方はnil var wkWebView: WKWebView? var uiWebView: UIWebView? override init(frame: CGRect) { super.init(frame: frame) if NSClassFromString("WKWebView") != nil { wkWebView = WKWebView(frame: self.bounds) self.addSubview(wkWebView!) } else { uiWebView = UIWebView(frame: self.bounds) self.addSubview(uiWebView!) } } func loadRequest(request: NSURLRequest) -> WKNavigation? { if(wkWebView != nil) { return wkWebView!.loadRequest(request); } else { uiWebView!.loadRequest(request) return nil } } 内部の実装イメージ UIWebView / WKWebView の両方を変数で宣言
  • 32. class WKWebViewWrapper: UIView { // どちらか一方はnil var wkWebView: WKWebView? var uiWebView: UIWebView? override init(frame: CGRect) { super.init(frame: frame) if NSClassFromString("WKWebView") != nil { wkWebView = WKWebView(frame: self.bounds) self.addSubview(wkWebView!) } else { uiWebView = UIWebView(frame: self.bounds) self.addSubview(uiWebView!) } } func loadRequest(request: NSURLRequest) -> WKNavigation? { if(wkWebView != nil) { return wkWebView!.loadRequest(request); } else { uiWebView!.loadRequest(request) return nil } } 内部の実装イメージ 対応/非対応に応じて一方にインスタンスを入れる
  • 33. class WKWebViewWrapper: UIView { // どちらか一方はnil var wkWebView: WKWebView? var uiWebView: UIWebView? override init(frame: CGRect) { super.init(frame: frame) if NSClassFromString("WKWebView") != nil { wkWebView = WKWebView(frame: self.bounds) self.addSubview(wkWebView!) } else { uiWebView = UIWebView(frame: self.bounds) self.addSubview(uiWebView!) } } func loadRequest(request: NSURLRequest) -> WKNavigation? { if(wkWebView != nil) { return wkWebView!.loadRequest(request); } else { uiWebView!.loadRequest(request) return nil } } 内部の実装イメージ すべてのメソッドで分岐処理
  • 36. WKWebView UIWebView iOS 8 以上 iOS 7 以下
  • 39. WKWebView UIWebView WKWebView の IF Controller iOS 8 以上 iOS 7 以下 外からは同じ型に見える
  • 41. Why Obj-C ? UIWebView WKWebView var canGoBack: Bool { get } var canGoForward: Bool { get } var loading: Bool { get } var canGoBack: Bool { get } var canGoForward: Bool { get } var loading: Bool { get } var title: String? { get } var URL: NSURL? { get } var estimatedProgress: Double { get } func loadRequest(request: NSURLRequest) 
 func reload() func stopLoading() func goBack() func goForward() func loadRequest(request: NSURLRequest) -> WKNavigation? func reload() -> WKNavigation? func stopLoading()
 func goBack() -> WKNavigation? func goForward() -> WKNavigation? func stringByEvaluatingJavaScriptFromString (script: String) -> String? func evaluateJavaScript(javaScriptString: String, completionHandler: ((AnyObject!, NSError!) -> Void)?) weak var delegate: UIWebViewDelegate? weak var navigationDelegate: WKNavigationDelegate?
 weak var UIDelegate: WKUIDelegate?
  • 42. Why Obj-C ? UIWebView WKWebView var canGoBack: Bool { get } var canGoForward: Bool { get } var loading: Bool { get } var canGoBack: Bool { get } var canGoForward: Bool { get } var loading: Bool { get } var title: String? { get } var URL: NSURL? { get } var estimatedProgress: Double { get } func loadRequest(request: NSURLRequest) 
 func reload() func stopLoading() func goBack() func goForward() func loadRequest(request: NSURLRequest) -> WKNavigation? func reload() -> WKNavigation? func stopLoading()
 func goBack() -> WKNavigation? func goForward() -> WKNavigation? func stringByEvaluatingJavaScriptFromString (script: String) -> String? func evaluateJavaScript(javaScriptString: String, completionHandler: ((AnyObject!, NSError!) -> Void)?) weak var delegate: UIWebViewDelegate? weak var navigationDelegate: WKNavigationDelegate?
 weak var UIDelegate: WKUIDelegate? Swift はココの戻り値の型の違いを区別してしまう! (かなり頑張ったけど無理でした笑)
  • 44. 手順1: WKWebView の IF を Protocol として切り出す // WKWebViewProtocol.swift #import <UIKit/UIKit.h> #import <WebKit/WebKit.h> @protocol WKWebViewProtocol <NSObject> @property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack; @property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward; @property (nonatomic, readonly, getter=isLoading) BOOL loading; @property (nonatomic, readonly) NSURL *URL; @property (nonatomic, readonly, copy) NSString *title; - (void)loadRequest:(NSURLRequest *)request; - (void)reload; - (void)stopLoading; - (void)goBack; - (void)goForward; - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler; @end
  • 45. 手順1: WKWebView の IF を Protocol として切り出す // WKWebViewProtocol.swift #import <UIKit/UIKit.h> #import <WebKit/WebKit.h> @protocol WKWebViewProtocol <NSObject> @property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack; @property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward; @property (nonatomic, readonly, getter=isLoading) BOOL loading; @property (nonatomic, readonly) NSURL *URL; @property (nonatomic, readonly, copy) NSString *title; - (void)loadRequest:(NSURLRequest *)request; - (void)reload; - (void)stopLoading; - (void)goBack; - (void)goForward; - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler; @end WKWebViewのヘッダファイルからコピペしてくる
  • 46. 手順2: WKWebView の拡張で Protocol に適合 // WKWebView+ProtocolConformed.h #import "WKWebViewProtocol.h" @interface WKWebView(YSSWebViewProtocol) <WKWebViewProtocol> @end // WKWebView+ProtocolConformed.m #import "WKWebView+ProtocolConformed.h" @implementation WKWebView(YSSWebViewProtocol) @end
  • 47. 手順2: WKWebView の拡張で Protocol に適合 // WKWebView+ProtocolConformed.h #import "WKWebViewProtocol.h" @interface WKWebView(YSSWebViewProtocol) <WKWebViewProtocol> @end // WKWebView+ProtocolConformed.m #import "WKWebView+ProtocolConformed.h" @implementation WKWebView(YSSWebViewProtocol) @end Protocol の適合を宣言
  • 48. 手順2: WKWebView の拡張で Protocol に適合 // WKWebView+ProtocolConformed.h #import "WKWebViewProtocol.h" @interface WKWebView(YSSWebViewProtocol) <WKWebViewProtocol> @end // WKWebView+ProtocolConformed.m #import "WKWebView+ProtocolConformed.h" @implementation WKWebView(YSSWebViewProtocol) @end 元から実装されているので拡張は何もいらない!
  • 49. 手順3: 同様に UIWebView の拡張も作成 // UIWebView+WKProtocolConformed.h #import "WKWebViewProtocol.h" @interface UIWebView (WKProtocolConformed) <WKWebViewProtocol> @end
  • 50. 手順3: 同様に UIWebView の拡張も作成 // UIWebView+WKProtocolConformed.h #import "WKWebViewProtocol.h" @interface UIWebView (WKProtocolConformed) <WKWebViewProtocol> @end 同様に WKWebView の Protocol 適合を宣言
  • 51. UIWebView には足りないメソッドを実装する! // UIWebView+WKProtocolConformed.m #import "UIWebView+WKProtocolConformed.h" @implementation UIWebView (WKProtocolConformed) - (NSURL *)URL { NSString *URLString = [self stringByEvaluatingJavaScriptFromString:@"document.URL"]; return [NSURL URLWithString:URLString]; } - (NSString *)title { return [self stringByEvaluatingJavaScriptFromString:@"document.title"]; } - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler { NSString *result = [self stringByEvaluatingJavaScriptFromString:javaScriptString]; if(completionHandler) { completionHandler(result, nil); } } @end
  • 52. UIWebView には足りないメソッドを実装する! // UIWebView+WKProtocolConformed.m #import "UIWebView+WKProtocolConformed.h" @implementation UIWebView (WKProtocolConformed) - (NSURL *)URL { NSString *URLString = [self stringByEvaluatingJavaScriptFromString:@"document.URL"]; return [NSURL URLWithString:URLString]; } - (NSString *)title { return [self stringByEvaluatingJavaScriptFromString:@"document.title"]; } - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler { NSString *result = [self stringByEvaluatingJavaScriptFromString:javaScriptString]; if(completionHandler) { completionHandler(result, nil); } } @end loadRequest: などは元から実装されているのでスルー!
  • 54. Controller で WKWebViewProtocol 型として webView を保持 // MyViewController.swift import UIKit import WebKit class ViewController: UIViewController { var webView: WKWebViewProtocol! override func viewDidLoad() { super.viewDidLoad() self.createWebView() } func createWebView() { if NSClassFromString("WKWebView") != nil { let wkWebView = WKWebView(frame: self.view.bounds) self.view.addSubview(wkWebView) webView = wkWebView } else { let uiWebView = UIWebView(frame: self.view.bounds) self.view.addSubview(uiWebView) webView = uiWebView as WKWebViewProtocol } } …(続く)
  • 55. Controller で WKWebViewProtocol 型として webView を保持 // MyViewController.swift import UIKit import WebKit class ViewController: UIViewController { var webView: WKWebViewProtocol! override func viewDidLoad() { super.viewDidLoad() self.createWebView() } func createWebView() { if NSClassFromString("WKWebView") != nil { let wkWebView = WKWebView(frame: self.view.bounds) self.view.addSubview(wkWebView) webView = wkWebView } else { let uiWebView = UIWebView(frame: self.view.bounds) self.view.addSubview(uiWebView) webView = uiWebView as WKWebViewProtocol } } …(続く) WKWebViewProtocol! 型として1個だけ変数を保持!
  • 56. Controller で WKWebViewProtocol 型として webView を保持 // MyViewController.swift import UIKit import WebKit class ViewController: UIViewController { var webView: WKWebViewProtocol! override func viewDidLoad() { super.viewDidLoad() self.createWebView() } func createWebView() { if NSClassFromString("WKWebView") != nil { let wkWebView = WKWebView(frame: self.view.bounds) self.view.addSubview(wkWebView) webView = wkWebView } else { let uiWebView = UIWebView(frame: self.view.bounds) self.view.addSubview(uiWebView) webView = uiWebView as WKWebViewProtocol } } …(続く) インスタンス化の後は、WK / UI 共に変数 webView に格納できる!
  • 57. Controller で WKWebViewProtocol 型として webView を保持 // MyViewController.swift …(続き) override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) let URL = NSURL(string: "http://yahoo.co.jp") let req = NSURLRequest(URL: URL!) webView.loadRequest(req) } }
  • 58. Controller で WKWebViewProtocol 型として webView を保持 // MyViewController.swift …(続き) override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) let URL = NSURL(string: "http://yahoo.co.jp") let req = NSURLRequest(URL: URL!) webView.loadRequest(req) } } 以後は何の区別もなく同じ webView として扱える!
  • 59. DEMO
  • 60. 方針1: Wrapper で包む 方針2: UIWebViewをいじる • 特別なことをしていないので安心。 • だいぶ裏技っぽい。 (戻り値型を捻じ曲げてる辺り特に) • 全メソッドを実装して、それぞれ分岐 処理を書くのが面倒。 • UIWebViewで足りないメソッドだけ実 装すれば良い。
  • 61. いつか iOS 7 のサポートを切るとき、 UIWebView と気持ち良く別れられるように。
  • 62. 2014年12月 5日 Yahoo! JAPAN Tech Blog let UIWebView as WKWebView