- 投稿日:2020-07-27T21:57:12+09:00
【厳選】これだけは覚えておきたいXcodeショートカットまとめ
コード編集
ショートカットキー 結果 Cmd + I インデント整理 Cmd + / コメントアウト Cmd + Click(※) 定義へ移動 Opt + Click Quick Help(リファレンス) Cmd + Ctr + E ファイル内変数のRename Cmd + Ctr + ← ジャンプ前のコードに戻る Cmd + Ctr + → 進む(上記の逆) Opt + ← 左の単語にカーソル移動 Opt + → 右の単語にカーソル移動 Cmd + [ インデントを下げる Cmd + ] インデントを上げる Cmd + ← カーソルを行頭へ移動 Cmd + → カーソルを行末へ移動 Ctrl + D 右の1文字削除 ※Cmd + Click で Jump to Definition を押せば、定義元に移動できます。
これが面倒な方は、上部メニュー Xcode > Preferences > Navigation > Command-clock on Coder を Jump to Definition に変更すれば、Cmd + Click するだけで、定義元に移動できます。
検索
ショートカットキー 結果 Cmd + Shift + O プロジェクト内検索 Cmd + F ファイル内検索 ウィンドウ・エリア
ショートカットキー 結果 Cmd + 0 ナビゲートエリア表示 Cmd + Opt + 0 ユーティリティーエリア表示 Cmd + Shift + y デバッグエリア表示 Cmd + Opt + Ctr + Return アシスタントエディタ表示/非表示 実行・ビルド・デバッグ
ショートカットキー 結果 Cmd + R 実行 Cmd + B ビルド Cmd + Shift + K クリーンビルド Cmd + . 停止 Cmd + \ ブレイクポイント設定/解除 Cmd + Ctr + Y ブレイクポイントで止まった処理をコンテニュー F6 ステップオーバー F7 ステップイン シミュレータ
ショートカットキー 結果 Cmd + Shift + H ホーム Cmd + L ロック Cmd + ← 反時計回りに回転 Cmd + → 時計回りに回転 Cmd + Ctr + Z 振る Cmd + K キーボード表示/非表示 Cmd + Shift + K Macのキーボードから入力 Cmd + S スクリーンショット 参考記事
- 投稿日:2020-07-27T21:28:16+09:00
【Swift】スコープについて
スコープの種類
スコープは以下の2種類に分類することができる
・ ローカルスコープ
・ グローバルスコープローカルスコープ
if
やfor
などの制御構文や関数内で定義されるスコープ
制御構文や関数内などで定義された定数・変数はその外部から使用することができない制御構文の例
if true { // hoge関数内で定数a、変数bを宣言 let a = 100 var b = 200 } print(a) // エラー! print(b) // エラー!上記の様に、制御構文内で定義された(= ローカルスコープ)定数や変数を関数外で使用することはできない
if true { // hoge関数内で定数a、変数bを宣言 let a = 100 var b = 200 print(a) // -> 100 print(b) // -> 200 }上記の様に、関数内であれば使用することができる
関数の例
func hoge() { // hoge関数内で定数a、変数bを宣言 let a = 100 var b = 200 } print(a) // エラー! print(b) // エラー!上記の様に、関数内で定義された(= ローカルスコープ)定数や変数を関数外で使用することはできない
func hoge() { // hoge関数内で定数a、変数bを宣言 let a = 100 var b = 200 print(a) print(b) } hoge() // -> 100 // 200上記の様に、関数内であれば使用することができる
グローバルスコープ
制御構文や関数など、どの範囲にも含まれないスコープ
let a = 100 var b = 200 if true { print(a) // -> 100 print(b) // -> 200 } func hoge() { print(a) print(b) } print(a) // -> 100 print(b) // -> 200 hoge() // -> 100 // 200上記の様に、どこからでも使用することができる
どこからでも使用できるグローバルスコープは便利である反面、意図しない変更が生じるリスクがあるので使用する際は注意が必要
- 投稿日:2020-07-27T11:59:20+09:00
【iOS】 ReactorKitでStoryboardを使う方法
経緯
ReactorKitを使っているプロジェクトでStoryboardでレイアウトした画面を表示させたら画面に何も表示されなかったので原因を調べてみた
対応方法
最初はbind内でloadView()を呼んでViewの生成を行っていたがStoryboardを使っているのにloadView()を呼びたくないとおもい公式ドキュメントを調べてみたらそれ用のプロトコルが用意されていた
func bind(reactor: Reactor) { loadView() }以下のようにStoryboardViewプロトコルを使用するとViewの生成が行われ画面が表示された
class MyViewController: UIViewController, StoryboardView { func bind(reactor: MyViewReactor) { // this is called after the view is loaded (viewDidLoad) } }
- 投稿日:2020-07-27T11:52:31+09:00
遷移先でdismissした後に画面更新したい場合
iOS13からModalで画面遷移をする際にフルスクリーンではなくシート型に変更になりました。この場合、UIとしては格好いい(スワイプで閉じられる)のですが、遷移元の画面更新をviewWillAppearでやっていた場合、iOS13では呼ばれなくなりました。
対応策
遷移先のViewControllerのなかで下記のメソッドを呼んでやる必要があります。iOS12で呼ぶと二重で実行されてしまうので、実行をiOS13以上にしてやる必要があります。
override func viewWillAppear(_ animated: Bool) { if #available(iOS 13.0, *) { presentingViewController?.beginAppearanceTransition(false, animated: animated) } super.viewWillAppear(animated) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) if #available(iOS 13.0, *) { presentingViewController?.beginAppearanceTransition(true, animated: animated) presentingViewController?.endAppearanceTransition() } } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) if #available(iOS 13.0, *) { presentingViewController?.endAppearanceTransition() } }
- 投稿日:2020-07-27T11:41:06+09:00
Swift でショートムービ(動画)を再生する
はじめに
AVPlayer を使って動画を再生できるように実装していきます。
より詳細な情報は下記で載せました。動画を表示するためのクラス作成
まずは動画を再生するために、
AVPlayerLayer
をlayerClass
とする UIView のサブクラスを
作成していきます。class VideoPlayerView: UIView { private var playerLayer: AVPlayerLayer? { return layer as? AVPlayerLayer } // Override UIView property override static var layerClass: AnyClass { return AVPlayerLayer.self } }
VideoPlayerView
に Property を追加基本的には動画の制御に必要な API は
AVPlayer
が全て持ってます。
isPlaying
に関してはAVPlayer
に特にそれっぽい API が無かったのでプロパティを追加しま
した。class VideoPlayerView: UIView { private(set) var player: AVPlayer? { get { return playerLayer?.player } set { playerLayer?.player = newValue } } var isPlaying: Bool { guard let player = player else { return false } return player.rate != 0 && player.error == nil } private var playerLayer: AVPlayerLayer? { return layer as? AVPlayerLayer } // Override UIView property override static var layerClass: AnyClass { return AVPlayerLayer.self } func setPlayer(player: AVPlayer) { self.player = player } }再生してみる
// 再生 videoPlayerView.player?.play() // 一時停止 videoPlayerView.player?.pause()
- 投稿日:2020-07-27T03:12:02+09:00
実はiOSのWebView内のHTTPSリクエストは傍受できる(URLProtocol)
Chromeの開発ツールにはnetworkという項目があり,ブラウザでサイトにアクセスしたときのHTTP/HTTPSのアクセスのログを見ることができます.APIの動作確認などできるので,Web開発者なら重宝している機能の一つだと思います.
とても便利な機能なので,iOSのWKWebViewでも使えたらいいなと思い開発しました.
下記の動画が作ったサンプルなのですが,アプリ内のWKWebViewでGithubにアクセスしたときのHTTPSリクエストすべてをTableViewに表示できるようになっています.
こちらが今回作成したサンプルのレポジトリです.
https://github.com/tommy19970714/WebKitURLProtocol
これはログを表示するだけですが,この技術を応用する例としては,WKWebView上で開いているyahooのホームページ内にある画像をすべて猫の画像にすり替えるChrome Extensionのようなこともできます.
他に事例がないか調べたのですが,通信デバックライブラリのnetfoxやWormholyは非推奨になったUIWebViewのパケットキャプチャはできるようです.WKWebViewに対してパケットキャプチャしている事例はどこにもなかったので今回書くことにしました.
本記事ではURLProtocolについて解説します.ただURLProtocolを普通に使うだけでは,すべてのHTTPSリクエストを見ることはできません.ちょっとした裏技を使ったらできるようになるので,それについて解説します.
よくよく考えたら,SNSやニュースアプリなどアプリ内でブラウザを開くことのできるアプリは多くありますが,ユーザがそのブラウザを使ってアクセスしたログ(SNSのログイン情報や決済情報も含めて)をユーザの許可なく保存できてしまいます.なので悪用はしないでください.
URLProtocolとは
iOSではインターネット通信を行う際に
URL Loading System
というものを使っています.
HTTP/HTTPSなど通信プロトコルを使ってURLで特定されるWebサイトや画像等のリソースに非同期でアクセスするための仕組みのことです.URL Loding Systemについて詳しく知りたい方は公式のドキュメントがあります.
https://developer.apple.com/documentation/foundation/url_loading_systemURLProtocolとは
URL Loading System
の通信に使われるインターフェイスで,ネットワーク通信を開始しリクエストを送ってレスポンスを受け取るプロトコルです.
基本的にURLProtocolはデバック時のAPI通信のモックに使用できます.サーバのAPIが出来上がっていないけど,クライアントのiOS側も先に作りたいという場合に,URLProtocolを使うと実際の通信をインターセプトして戻り値を自由に設定できます.今回はHTTPSすべてのリクエストをインターセプトできるように,カスタマイズしたURLProtocolを作っていきます.
カスタマイズしたURLProtocolを作る
URLProtocolはサブクラス化することで実装できます.URLProtocolのサブクラスにするには下記の5つのoverrideメソッドを組み込む必要があります.
組み込むにあたってそれぞれのメソッドの簡単な説明を示します.class CustomURLProtocol: URLProtocol { override class func canInit(with request: URLRequest) -> Bool // このURLProtocolが引数に渡されるrequestを扱う必要があるかを判断します.trueにするとこのURLProtocolでインターセプトできます. override open class func canInit(with task: URLSessionTask) -> Bool // このURLProtocolが引数に渡されるtaskを扱う必要があるかを判断します.trueにするとこのURLProtocolでインターセプトできます. override func startLoading() // リクエストのロードが開始したときに呼び出されます. override func stopLoading() // リクエストの読み込みが終了するときに呼び出されます. open override class func canonicalRequest(for request: URLRequest) -> URLRequest // リクエストで送られたURLRequestにヘッダーをカスタマイズできます.特に何もする必要がなければ引数のrequestをそのまま返します. }最初に見せたサンプルのようにアクセスした先のURLを知るには,
canInit
メソッドのURLRequest
かURLSessionTask
内を見れば良いです.サンプルではcanInit
が呼ばれたときにURLSessionTask
の中身をtableviewに表示しています.そしてカスタマイズしたURLProtocolは次のようにして,登録できます.
URLProtocol.registerClass(CustomURLProtocol.self)CustomURLProtocolを登録をしたら.WKWebViewでページが移動する度に,それぞれのoverrideメソッドが呼ばれるようになります.
ただ今の段階だと,ページ移動時したときのURLしか分からないので,これからすべてのHTTPSリクエストに反応できるように設定していきます.URLProtocolのschemeを設定する
URLProtocol関連で調べていたら,たまたま次のレポジトリを発見しました.
https://github.com/Yeatse/NSURLProtocol-WebKitSupport
そのレポジトリではObjective-cでURLProtocolのAppleのドキュメントに載っていない非公開APIについて記述してありました.そのAPIはURLProtocolのトリガーとしてのschemeを設定できるようです.つまり,schemeとして「https」を設定したら,httpsから始まるリクエストにアクセスする度にURLProtocolを呼び出すというトリガーを設定できるということです.それをswiftに書き換えたのが下記のコードです.
extension URLProtocol { class func contextControllerClass()->AnyClass { return NSClassFromString("WKBrowsingContextController")! } class func registerSchemeSelector()->Selector { return NSSelectorFromString("registerSchemeForCustomProtocol:") } class func unregisterSchemeSelector()->Selector { return NSSelectorFromString("unregisterSchemeForCustomProtocol:") } class func wk_register(scheme:String){ let cls:AnyClass = contextControllerClass() let sel = registerSchemeSelector() if cls.responds(to: sel) { _ = (cls as AnyObject).perform(sel, with: scheme) } } class func wk_unregister(scheme:String){ let cls:AnyClass = contextControllerClass() let sel = unregisterSchemeSelector() if cls.responds(to: sel) { _ = (cls as AnyObject).perform(sel, with: scheme) } } }上記のURLProtocol Extensionを使用すると,次のようにURLProtocolのトリガーのSchemeを設定できます.
URLProtocol.wk_register(scheme: "https") URLProtocol.wk_register(scheme: "http")上記のコードを実行することで,https/httpのすべてのリクエストが呼ばれた際に先ほど登録したカスタムURLProtocolが呼ばれるようになります.
まとめ
この記事では,次の二段階ですべてのHTTP/HTTPSリクエストをパケットキャプチャできるようにしました.
1. カスタマイズしたURLProtocolを作る
2. URLProtocolのschemeを設定する
今回はたまたま発見した非公開APIを使用したやり方になっているため,推奨できません.あくまで開発のデバックの一つとして使用してください.
審査に通るかどうかは別にして,パケットキャプチャして得たURLを元にWebView上で見ている動画をダウンロードするツールとかは,この技術を使用して作れるかもしれないですね.
もしこの記事を見たAppleのレビュワーさんがいたら,この非公開APIの審査を厳しくしたほうが良さそうです.追記
海外のセキュリティの専門家がこの脆弱性(非公開API)を利用して,脱獄せずに他のアプリのHTTP/HTTPSリクエストを傍受する方法を指摘している記事を見つけました.
どうやって他のアプリを傍受するかというと,今回のコードをライブラリとして書き出してしまって,端末内のアプリのディレクトリに直接入れるだけとのことです.なぜそんなことができてしまうかというと,アプリ内で一度URLProtocolを登録してしまうと,必ずURLProtocolが呼ばれてしまうというstaticな設計になっているためです.これは設計を変えたほうが良さそうです.書きながら思ったのですが,もしこれをmacOSで同じことができてしまうのなら,結構大きなセキュリティホールになるのではと思ってしまいました.今度,macOSでも他のアプリの通信内容を傍受できてしまうのかを実験してみたいと思います.medium - Let’s write Swift code to intercept SSL Pinning HTTPS Requests
Network Interception - Write Swift codes to inspect network requests (even with SSL Pinning active)参考文献
netfox: URLProtocolを実装する際に参考にしました
【Swift】URLProtocolという名のclassについて: URLProtocolについて説明する際に参考にしました.