[iOS] WKWebView にYouTube 動画を埋め込み、バックグラウンド再生させる

結論

無理です!
通知センターを上から開いた場合などはまだ動画は再生されますが、アプリをバックグラウンドにした瞬間に再生が終わって音声が途切れます。

悲しいので調査内容だけでもまとめました。

YouTube をWKWebView の中に埋め込む方法は、
以前の投稿 [iOS] iOSアプリにYouTube 動画を埋め込み、インライン再生させる に書いています。

試したこと

  1. Background Mode とAVAudioSession を設定
  2. JS の Player オブジェクトを操作する
  3. iframe の中の video タグから情報を取得する
  4. ネイティブのコードでPicture in Picture
  5. JavaScript のコードでPicture in Picture

Background Mode とAVAudioSession を設定

アプリでバックグラウンド再生させるときの通常の手法です。

Qiita: [Swift4] バックグラウンドでオーディオ再生する などを参考に設定してみましたが、
バックグラウンド再生ができるようにはなりませんでした。

JavaScript の Player オブジェクトを操作する

Webインスペクタを使ってJavaScriptの情報を探してみます。
Webインスペクタのやり方は Qiita: Simulatorを使ってiOS版SafariのWebページの挙動をデバッグする などが参考になりそう。
iPhone の Safari のデバッグ手順として記載されていますが、シミュレータ内のWKWebView などもデバッグ可能です。

で、Webインスペクタを眺めていたら player.getApiInterface() というメソッドがありました。
何か使えそうな情報が取得できるかもしれないので実行してみると、利用可能らしいAPI一覧が取得できました。
しかし使えそうなものはありませんでした。

["cueVideoById", "loadVideoById", "cueVideoByUrl", "loadVideoByUrl", "playVideo", 
"pauseVideo", "stopVideo", "clearVideo", "getVideoBytesLoaded", "getVideoBytesTotal", 
"getVideoLoadedFraction", "getVideoStartBytes", "cuePlaylist", "loadPlaylist", "nextVideo", 
"previousVideo", "playVideoAt", "setShuffle", "setLoop", "getPlaylist", 
"getPlaylistIndex", "getPlaylistId", "loadModule", "unloadModule", "setOption", 
"getOption", "getOptions", "mute", "unMute", "isMuted", 
"setVolume", "getVolume", "seekTo", "getPlayerState", "getPlaybackRate", 
"setPlaybackRate", "getAvailablePlaybackRates", "getPlaybackQuality", "setPlaybackQuality", "getAvailableQualityLevels", 
"getCurrentTime", "getDuration", "addEventListener", "removeEventListener", "getDebugText", 
"getVideoData", "addCueRange", "removeCueRange", "setSize", "getApiInterface", 
"destroy", "showVideoInfo", "hideVideoInfo", "isVideoInfoVisible", "getSphericalProperties", 
"setSphericalProperties", "getVideoEmbedCode", "getVideoUrl", "getMediaReferenceTime"]

iframe の中の video タグから情報を取得する

YouTube iIFrame API はiframe の中にvideoタグが生成されます。
中身が見れないかと video タグにアクセスしましたが、CORS 制限でJavaScript からは iframe の中のDOMにはアクセスできませんでした。

ざっくり言うとYouTube のドメインからしか iframe の中にアクセスできないということかと思います。
そもそもアクセスできたとしてもその情報を使うのはYouTube の規約に引っかかりそうです🙅‍♂️

ネイティブのコードでPicture in Picture

切り口を変えて、Picture in Picture でずっと再生させられないかを検討しました。
Appleのドキュメントがありました。
しかしこれはAVPlayer を使って実現する機能なので、WKWebView では実現できません。
WKWebView から AVPlayer を抽出する方法もあるようですが、YouTube の規約に引っかかりそうなので採用しない方がよさそう。

JavaScript のコードでPicture in Picture

こちらはWebView が再生する動画に対してPicture in Picture の属性を付与する方法。
Appleの公式ドキュメント にやり方が載っていましたが、video エレメントに webkitSetPresentationMode を設定するという手法でした。

そもそも CORS の制限により iframe 内の videoエレメントが取得・操作できないのでこれも採用できません。

結論

無理です!
ダーティな手を使うと実現可能だとは思いますが、現状は正攻法では実現できないと思います!

もし実現方法を知っている方がいらっしゃったら、コメントしていただけるととてもありがたいです🙌

最後に

以上です。お疲れ様でした!

[iOS] WKWebView にYouTube 動画を埋め込み、自動再生させる & 別の動画を再生させる

前提条件

以下の環境で実行しています。

  • Xcode 12.0
  • iOSシミュレータ: iOS14系

まずYouTube をWKWebView の中に埋め込む方法

以前の投稿 [iOS] iOSアプリにYouTube 動画を埋め込み、インライン再生させる に書いています。

ということで、以降は自動再生と別の動画を再生させる方法を書いていきます。

自動再生させる

iOS側とJavaScript 側両方に少しコードを追加するだけで可能です。

まずiOS側。
WKWebViewConfiguration に設定をし、それをWKWebView に読み込ませます。
追加する設定はこちら

config.mediaTypesRequiringUserActionForPlayback = []

再生させるためにユーザのアクション(タップなど)を必要とする、というプロパティがあるのですが、
そこに空配列を設定します。
ここの値には、.audio.video.all を格納することができますが、
デフォルトでは .video のみが設定されているようです。
なので、ここに空配列を設定することにより、アプリとしては自動再生可能な状態にできます。

次にJavaScript 側。

// 4. The API will call this function when the video player is ready.
function onPlayerReady(event) {
event.target.playVideo();
}

このように、動画再生準備完了のイベントハンドラに、再生メソッドを記述するだけです。

上記2点を設定すれば、動画の自動再生機能が実装できます!

YouTube ライブラリで用意されている autoplay について

公式リファレンスにはプレーヤー初期化時に渡せるプロパティとして autoplay がありました。
これは、今回のようにWKWebViewConfiguration に設定している状態では不要でした。
Safari や Chrome などの通常のブラウザでは必要となる設定のようです。

通常のブラウザで自動再生をさせる方法

補足ですが、通常のブラウザの場合は「音声を出しつつ」「自動再生させる」ことはできません。

  • muted 属性が設定されていれば autoplay 属性は有効になります。
  • muted 属性が設定されていなければ autoplay 属性は無効になります。

つまり、自動再生させるなら音声は出せず、音声を出すなら自動再生できません。
このあたりはMDN Web docs のvideo タグについてのドキュメントが参考になりそうです。

音量周りは volume とかを変更してもいけるかもしれません。(試してない)

YouTube ライブラリで自動再生をする場合は2箇所修正を入れれば実現できます。(ただし上記の通り音声は出ません)

  1. プレーヤー初期化時の playerVarsautoplay: 1 を設定
  2. 動画再生準備完了のイベントハンドラに、ミュートメソッドと再生メソッドを記述(以下記述)
// 4. The API will call this function when the video player is ready.
function onPlayerReady(event) {
event.target.mute();
  event.target.playVideo();
}

別の動画を再生させる

次に別の動画に差し替える方法です!

JavaScript で操作します。
Player を都度初期化しても実現できると思いますが、より適しているのは loadVideoById のようです。
これをするとプレイヤーの初期化処理が走らないので、おそらく無駄な処理を省くことができそうです。

本ポストは以上です!

バックグラウンド再生についても書きたかったのですが難しそうなので、というかできるのか…??
という感じなので、別にまとめます。

最後に

以上です。お疲れ様でした!

[iOS] iOSアプリにYouTube 動画を埋め込み、インライン再生させる

前提条件

以下の環境で実行しています。

  • Xcode 12.0
  • iOSシミュレータ: iOS14系

HTML と JavaScript で埋め込むか、Swift で埋め込むか

公式のリファレンスを元に埋め込みます。
方法は大きく分けて2つ。

Swift で埋め込む方法はYouTube のライブラリを使って埋め込むのですが、
その中で結局WebView を作っているようです(要はJavaScript 版のラッパーのよう)。
しかしドキュメントは充実しておらず、YouTube 側もあまり力を入れている訳ではなさそうだし、
ちょろっと書かれたドキュメントを見る限り柔軟な使い方はできなさそう。

HTML と JavaScript で埋め込む、に決めた!

という理由により、HTML と JavaScript で直接コードを書いて埋め込んでいきます。

WKWebView を用意

まずWKWebView を準備します。
storyboard や xib ではなく、直接コードにより作っていきます。
コードの方が色々小回りが効くので、今回はこちらの方が良い気がします。

let config = WKWebViewConfiguration()
config.allowsInlineMediaPlayback = true
let webView = WKWebView(
    frame: CGRect(x: 0, y: 0, width: 640, height: 360),
    configuration: config
)
view.addSubview(webView)

ここでポイントなのが、config.allowsInlineMediaPlayback = true の箇所。
これを指定しないと、YouTube動画がインライン再生されず、
再生ボタンをタップした瞬間に全画面再生になってしまいます。

そのためにはWKWebViewConfiguration を WKWebView にセットする必要があるのですが、
storyboard や xib で生成し終わったWKWebView に後からセットできるのかは謎🤔

空のHTMLファイルを作成し、WKWebView に読み込ませる

次に、空のHTMLファイルを作ります。
適当に「index.html」という名前でXcodeプロジェクト内に作成します。

で、以下がWKWebView に読み込ませるコード。

guard let path: String = Bundle.main.path(forResource: "index", ofType: "html") else {
return
}

let localHTMLUrl = URL(fileURLWithPath: path, isDirectory: false)
webView.loadFileURL(localHTMLUrl, allowingReadAccessTo: localHTMLUrl)

特に言うことはありません。

次はいよいよ本題であるHTML と JavaScript を記述します。

HTML と JavaScript でインライン再生をさせる

といっても、とりあえず動画を再生させるだけなら、公式リファレンスのコードをHTMLに貼り付ければ動きます。
ただ、再生ボタンを押すと全画面再生になってしまうと思います。

インライン再生させるためには、JSでのプレーヤー初期化時にパラメータを渡してやる必要があります。
playsinline というパラメータに1を指定して渡します。

どうやって渡すか。
プレーヤー初期化時に playerVars というオブジェクト経由で渡します。
リファレンスのコードにはそれが書いてない(文章では書いているけど、サンプルコードには書いてない)ので最初混乱しましたね!

というわけでプレーヤー初期化する箇所の抜粋コード。

// 3. This function creates an <iframe> (and YouTube player)
//    after the API code downloads.
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
    height: '360',
        width: '640',
        videoId: 'M7lc1UVf-VE',
        playerVars: {
            playsinline: 1
        },
        events: {
          'onReady': onPlayerReady,
            'onStateChange': onPlayerStateChange
        }
    });
}

これでインライン 再生が可能になりました!

他にも初期化時に渡せるパラメータ はたくさんあって playerVars 経由で渡せます。
それによりもう少し動画の挙動や見た目を制御できます。

で、「自動再生」や「次の動画を指定して再生」や「バックグラウンド再生」などを試してみたいのですが、
長くなりそうなので今回はここまで。

最後に

以上です。お疲れ様でした!

WordPress.com のサイトをGoogle検索でヒット時、適切なスニペットを表示する (SEO対策)

スニペットとは

Google検索のサイト名の下に出てくる短い説明文のことです。

例えば本ブログの記事のスニペット↓

スニペットを読んでも内容が分からない

例えば本ブログのトップページのスニペット↓

英語はやめたい

両方ともよろしくない😵

上の画像はポストの先頭の文字たち(目次部分の文字がツラツラと)が並んでいて文章になっていないので、変更したいと思います。
下の画像はブログ作成時に書いた適当な英語なので、良い感じに変更したいと思います。

各記事たちのスニペットを変更する

まずポストの編集画面を表示し、サイドバーの「投稿」タブをクリックします。

「投稿」タブをクリック

そのサイドバーを下の方にスクロールすると、「抜粋」という入力欄があります。
これが検索結果のスニペットに表示される項目となります。

「抜粋「抜粋」を設定

こんな感じで設定してみました↓

Google検索で表示すると、このような表示になります。
予想より文字数が少ないけど少しはそれらしいスニペットになりました。

少しはそれらしいスニペットになった

次にブログのトップページのスニペットを変更していきます。

ブログのトップページのスニペットを変更する

ブログの管理画面を表示します。

管理画面

サイドバーの下の方に「管理」>「設定」の項目があるので、それをクリックします。

「設定」をクリック

設定画面が表示されるので、以下画像のように「サイトのタグライン」を編集します。
編集した後、「設定を保存」ボタンを押すのを忘れずに!

「サイトのタグライン」を編集

Google検索で表示すると、このような表示になります👏

分かりやすいスニペットになった

最後に

以上です。お疲れ様でした!

[iOS] YouTube Data API v3 を使ってみる

iOSアプリでYouTubeを検索して動画一覧を表示したい

お気に入りに登録などアカウント周りの処理はせず、YouTubeに公開されている動画を検索して一覧を表示したい。
そう思ったので、そのAPIにアクセスするまでの手順をまとめます。
ざっくり手順は公式ドキュメントに記載されていますが、個人的にやや混乱しがちだったので。

Google にプロジェクトを登録する

まず Google APIs の認証情報ページ にいきます。
以下のようなダイアログが表示されるので、国と利用規約を確認して、右下の「同意して続行」をクリックします。

最初のダイアログ

以下のような画面に変わるので、「プロジェクトを作成」をクリックします。

「プロジェクトを作成」をクリック

で、以下の画面でプロジェクト名と場所に適切な内容を入力して、「作成」をクリック。

新しいプロジェクト

以下のような画面になりました。

「 ⚠️ 必ず、アプリケーションに関する情報を使用して OAuth 同意画面を構成してください。」
という気になる注意書きがありますが、内容をざっと見るとOAuth 用の設定のようでした。
今回はOAuth ではなくAPIキーによる認証のため、スルーします。

プロジェクト作成完了

これでプロジェクトの作成が完了しました。

API キーを作成し、若干の項目を設定する

次にAPIキーを作成します。
APIキーはプロジェクトに紐づいています。
URLにAPIキーを付与しないとAPIがエラーを返します。

「+ 認証情報を作成」をクリックすると、認証情報の種類一覧が出てくるので、「API キー」を選択します。

APIキーを選択

すると以下のようなダイアログが表示され、APIキーが作成されました。
このAPIキーをURLのパラメータに含めれば、誰でもどこでもAPIにアクセスできるようになりました。
検証用とは言えそれは怖いので、アクセス制限をかけます。
右下の「キーを制限」をクリックします。

APIキー作成完了

以下の画面になりました。
よければついでにAPIキーの名前も変えます。
なおこの画面はいつでも見られるので、ここにある情報は後で何回でも変更可能です。

「⚠️ このキーに制限はありません。」と表示されています。
怖いですね。
「アプリケーションの制限」でiOSアプリを選択します。

iOSアプリからのみに制限したい

iOSアプリを選択すると以下の表示になります。
「項目を追加」と押すとバンドルIDの入力欄が出てくるので埋めます。
先述の通り、この画面は後でも表示できるので、まだアプリのバンドルIDの用意がない場合は後で埋められます。

iOSアプリのみに制限

で、そのすぐ下の「キーを制限」を選択します。
すると以下のように「Select APIs」というセレクトボックスが表示されますが、何も選択できません。
プロジェクトで利用可能なAPIをまだ設定していないためです(すぐ後で設定します)。
つまりキー制限をするといまは何のAPIも叩けません。

とりあえず「保存」を押せば、一旦完了です!

「キーを制限」を選択して、「保存」を押す

保存を押すと、もとの画面に戻ります。
以下画像の通り、APIキーの行をクリックすると先程のAPIキーの制限設定や名前変更画面に遷移します。
iOSアプリのバンドルIDを決定したら設定します。

元の画面

作成したAPIキーに利用可能なAPIを設定する

APIキーは作成できましたが、利用可能なAPIを選択していない(できない)ので、何もアクセスできません。
なので、YouTube Data API をアクセス可能にします。

画面上の検索窓で「youtube」と入力すると以下のようなサジェストが表示されるので、「YouTube Data API v3」をクリックします。

YouTube Data API v3をクリック

もちろん「有効にする」を押します。

その右の「このAPIを試す」を押すとAPIドキュメントに飛ばされますが、APIドキュメントにはブラウザでAPIアクセスできるサンドボックスが用意されているので、多分それを使ってねということだと思います。

YouTube Data API v3 を有効にする

「有効にする」ボタンを押すと、以下の画面に移ります。
「認証情報」をクリックします。

認証情報をクリック

先程作成したAPIキーが表示されるのでクリックします。

APIキーをクリック

「Select APIs」をクリックするとYouTube Data API v3 が選択可能になっています。
チェックを入れて「保存」をクリックして下準備は終了です。

YouTube Data API v3 を選択

これでAPIが叩けるようになりました!

APIにアクセスする

あとは検索APIのドキュメントを見つつパラメータを組み合わせてGETアクセスするのみです。
実際のURLは以下のようになります。
keyパラメータを付与するのを忘れずに!

https://www.googleapis.com/youtube/v3/search?key=<作成したAPIキー>&好きなパラメータ...

ちなみにpart キーに snippet、type キーに video と指定すると、サムネイルやタイトルや動画のIDが取得できてYouTubeの検索結果っぽくできそうです。

ちなみに、ドキュメントだけだとAPIのレスポンススキーマが把握しづらいと思います。
そんなときはドキュメントの下の方にある実際に試してみるから、ブラウザ上でAPIを叩いてレスポンスを確認できます。
今回APIキーでのアクセスをiOSアプリからのみに制限したことで、アドレスバーにURLを入力してレスポンスを確認するという方法が取れない状態です。
そういうときのためにこの機能があるのだと思います。

これでAPIを叩いて色々いじれるようになりました!

最後に

以上です。お疲れ様でした!

WordPress.com のサイトをGoogle検索でヒットさせるようにする (SEO対策)

Google検索に全然ヒットしない😭 その原因とは

頑張ってブログポストしたのに悲しいですね。

原因の一つとして、Googleにインデックスされていない可能性、つまりまだサイトを見つけられていない可能性が挙げられます。
自分のサイトのURLで検索して結果ページに出てこなかったら、まだインデックスされていない可能性があります。
本ブログも二ヶ月ほど経ってもインデックスされていませんでした。

そうしたときに Google Search Console でサイトの認証をすれば、Googleが早くインデックスしてくれるかもしれません。

その方法がWordPressとGoogle Search Console を行ったり来たりで少し分かりづらかったのでまとめました。

インデックスされた結果どうなったか

もちろん自分のサイトのURLで検索して、結果ページに出てくるようになりました。
またWordPress のSEOはデフォルト設定で既に優秀なので、ある検索クエリーなどでは検索結果の一番上に表示されました🙌

ということで以下、サイト認証をする方法を記載します。

Google Search Console で下準備(TXTレコードを取得する)

まずはGoogle Seach Console でサイトのドメインを登録します。
Google Seach Console にアクセスし、画像の場所をクリックします。
(本サイトの登録は完了しているため、画像では既に表示されている状態です)

で、「プロパティを追加」をクリックします。

すると、以下のような画面が出てきます。
左に「ドメイン」、右に「URL プレフィックス」とあります。

左の「ドメイン」の方に自分のサイトのドメインを入力します。
例えば本ブログのURLは https://web-y.dev/ なので、web-y.dev と入力します。

(ちなみにWordPressのサポートにもGoogle Search Consoleを設定する方法 が書かれており、
そこでは左の「ドメイン」は機能しない、と書かれていますが、そんなことはなくてきちんと機能していそうです 🧐
追記: 問い合わせたら、英語の説明文には「ドメイン」を選択する方式の説明もあることが分かりました。
で、本ポストでやったことと同様の手順が書かれていました)

右の「URLプレフィックス」の方は各ページのURLを逐一登録する必要があり、
ブログはたくさんのページを頻繁に生成するため向かないと思います。

で、ドメインを入力したら「続行」をクリックします。

そうすると以下のような画面が出てくるのでコピーを押します。
コピーしたTXTレコードを、次は自分のWordPress.com に設定していきます。

TXTレコードとは

TXTレコードとは、DNSで定義されるそのドメインについての情報の種類の一つで、
あるホスト名の付加情報を自由に定義するためのもの。
DNSの仕様上は特定の用途や書式は定められておらず、
管理者が任意の文字データを設定できる。

http://e-words.jp/w/TXT%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89.html

Google の説明によると、この文字列はGoogleのドメインの所有権を認証するために使われるとのことです。

TXTレコードをWordPressに設定する

自分のWordPressの管理画面で作業していきます。
メニューの下の方にある「ドメイン」をクリックします。

Google にインデックスして欲しいドメインをクリックします。

ドメインに関する設定事項一覧が表示されるので、「ネームサーバーとDNSレコードを変更する」をクリックします。

「DNSレコード」をクリックします。

ようやくTXTレコードを設定する画面になりました。
以下の画像のように入力し、「新規 DNS レコードを追加」をクリックします。

これで WordPress上での設定は完了しました。
あとは Google Search Console に戻って登録するだけです。

Google Search Console で登録を完了する

Google Search Console で未登録の欄にあるドメインをクリックします。

すると、以下のように登録が完了したというダイアログが表示されます。

これで登録作業が完了しました!

あとは待ちましょう🙌

僕の場合は12時間ほどでGoogle にインデックス登録され、Google検索にヒットするようになりました。
Google Search Console の画面にも各数値が反映されるようになりました。

最後に

以上です。お疲れ様でした!

[iOS] Xcode12 で SwiftGen を使う

SwiftGen とは

SwiftGenは、プロジェクトのリソース(画像、ローカライズされた文字列など)のSwiftコードを自動的に生成して、
タイプセーフに使用できるようにするツールです。

https://github.com/SwiftGen/SwiftGen#swiftgen

AssetCatalog などで作成した色などは通常文字列で指定しますが、その文字列たちをenumの値にマッピングしてくれるOSSです。
そうするとtypoがなくなり、またオートサジェストが効くようになるので、開発の規模が大きくなるほど効率が上がります。

LICENSE はこちらに記載されています。

検証のためのAssetCatalog を準備

Assets.xcassets

SampleBackgroundColor という名前で色の AssetCatalog を作成しました。
これから SwiftGen を使って、SampleBackgroundColor を Swift の定数として利用できるようにしていきます。

SwiftGen をインストール

https://github.com/SwiftGen/SwiftGen#installation に書いてある通りにすれば導入できます。
本稿ではCocoaPods でインストールしました。

設定ファイルを作成

https://github.com/SwiftGen/SwiftGen#configuration-file に書いてある通り、コマンドラインで下記を実行します。
すると、swiftgen.yml というファイルができます。

swiftgen config init

CocoaPods でインストールしたためswiftgen というコマンドがないよと言われました。
なので、下記ようにパス指定して swiftgen コマンドを打ちます。

<YourProjectDirectory>/Pods/SwiftGen/bin/swiftgen config init

設定ファイルを編集

作成された swiftgen.yml を編集していきます。
色のAssetCatalog で利用したいので、swiftgen.yml 中の以下の記述を編集していきます。

# xcassets:
#   inputs:
#     - Main.xcassets
#     - ProFeatures.xcassets
#   outputs:
#     - templateName: swift5
#       params:
#         forceProvidesNamespaces: true
#       output: XCAssets+Generated.swift

このように編集しました。

xcassets:
  inputs:
    - <PathToAssetFile>/Assets.xcassets
  outputs:
    - templateName: swift5
      params:
        forceProvidesNamespaces: true
      output: <PathToOutputDestinationDirectory>/XCAssets+Generated.swift

forceProvidesNamespaces: true について

少なくともこの例では書いても書かなくても特に動作が変わらないように見えました🤔

swiftgen.yml 中のコメントによると、これを設定しない場合
AssetCatalog のグループなどで Provides Namespace にチェックが入っているもののみが適応される、
とのことだったのですが。
Provides Namespace にチェックを入れなくても生成されていました🤔

Provides Namespace にチェックが入っていない

設定ファイルを元にSwiftで扱うコードを生成する

以下のコマンドを打ちます。

swiftgen

すると、先の設定ファイルに記述したファイル名でSwiftコードが生成されました。
以下抜粋。

internal enum Asset {
  internal enum Color {
    internal static let sampleBackgroundColor = ColorAsset(name: "SampleBackgroundColor")
  }
}

あとは生成したSwiftファイルをXcodeにインポートすれば、コードなどで使えます。

Xcode 内で使ってみる

インポートしたあと、以下のようにソースコード中で使えます。

let _: UIColor = Asset.Color.sampleBackgroundColor.color

ビルドする度にSwiftコードを生成する

これまでの手順で、手動でSwiftGen が走るようになりました。

でもAssetCatalog に追加する度に手動で swiftgen コマンドを走らせるのはスイッチングコストがかかります。

なので、ビルド時に都度設定を反映させます。
Build Phases から New Run Script Phase で以下のようなシェルを追加すればよいです。

$PODS_ROOT/SwiftGen/bin/swiftgen config run --config $SRCROOT/<YourPath>/swiftgen.yml
Build Phases から New Run Script Phase を追加する

最後に

以上です。お疲れ様でした!

[iOS] Xcode12 Beta版で Carthage のビルドエラーを解消する

こちらのシェルを実行するとCarthage のビルドエラーが解消するかもしれません

https://github.com/Carthage/Carthage/issues/3019#issuecomment-665136323

ビルドするときのシェルがあれば、そちらに記述。
もしくはそれがなければ単独でシェルファイルを作成して、上記のコードを記述・実行すると解消するかもしれません。

[iOS] Alamofire と Codable でAPIをパースする

環境

  1. MacOS Catalina バージョン10.15.5
  2. Xcode11.6
  3. Swift 5.2.4

Alamofire をインストール

Swift Package Manager でインストールしてみます。
Qiita に iOSアプリ開発にSwift Package Managerを使おう という記事があるので、その通りに進めます。

パッケージのリポジトリのURLは https://github.com/Alamofire/Alamofire を入力します。

一瞬でインストールできました!

一旦適当なJSON APIを叩いて適当にオブジェクトを取得する

以下のような最低限のコードを書くだけでHTTP通信をしてレスポンスを取得できます。
ちなみにAlamofire の クラス名はAFです。
JSON のサンプルとして https://jsonplaceholder.typicode.com/todos/1 のURLを使っています。

        AF.request("https://jsonplaceholder.typicode.com/todos/1").response{ response in
            guard let data = response.data else {
                return
            }
            
            let jsonObject = try? JSONSerialization.jsonObject(with: data, options:.allowFragments) as? [String: Any]
            
            print(response.response?.allHeaderFields)  // レスポンスヘッダ
            print("=====================================")
            print(jsonObject)    // レスポンスボディ
        }

レスポンスヘッダとレスポンスボディがプリントされると思います。
JSONのスキーマを特に予想していないため、JSONSerialization を使ってJSONオブジェクトに変換しています。

Codable を使ってJSONオブジェクトを取得する

シンプルなDicationary型の構造の場合

上記JSONのレスポンスボディは以下です。

{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}

それをもとに、Codable に準拠した構造体を作ります。
適当なところに下記を定義します。

struct Todo: Decodable {
    var userId: Int
    var id: Int
    var title: String
    var completed: Bool
}

JSONのキーとstruct のプロパティ名、型を一致させます。
プロパティ名は大文字・小文字も厳密にJSONのキー名に一致させます。
(構造体の名前については適当です。
JSONのURLがtodos というパスだったので、適当にTodo という名前にしてます)

以下のようなコードで期待するJSONオブジェクトを取得できます。

        AF.request("https://jsonplaceholder.typicode.com/todos/1")   { urlRequest in
            urlRequest.timeoutInterval = 5
            urlRequest.allowsConstrainedNetworkAccess = false
        }
        .response{ response in
            guard let data = response.data else {
                return
            }
            
            let jsonObject = try? JSONDecoder.init().decode(Todo.self, from: data)
            print(jsonObject)    // レスポンスボディ
        }

レスポンスボディがプリントされたと思います!
定義したJSONのスキーマ採用するので、JSONDecodable を使ってスキーマ に沿ったオブジェクトに変換しています。

JSONDecodable: スキーマを予測できる場合、スキーマ に沿ったオブジェクトを生成する
JSONSerialization: スキーマを予測できない場合、とりあえずJSONオブジェクトを生成する

配列型の場合

以下のような配列型の場合

[
  {
    "userId": 1,
    "id": 1,
    "title": "delectus aut autem",
    "completed": false
  },
  {
    "userId": 1,
    "id": 2,
    "title": "quis ut nam facilis et officia qui",
    "completed": false
  }
]

以下のように、配列を指定してDecode すれば、オブジェクトの配列に変換できます!

 let jsonObject = try? JSONDecoder.init().decode([Todo].self, from: data)

最後に

以上です。お疲れ様でした!

[Mac][Bash] コマンドラインでCSVをJSONに変換する

コマンドラインでCSVをJSONに変換する

csvtojson というNodeパッケージを使えば一発でした。

まず、以下のようなCSVファイルを用意してみました。
prefectures.csv というファイル名で作成しました。
ちなみに一行目はJSONのテーブル定義となるのでそちらも含みます。

名称,読み方
北海道,ほっかいどう
青森,あおもり

で、以下のコマンドを打つと、JSONに変換されます。
csvtojson パッケージはインストールするまでもなくアドホックで使えればいいかなと思うのでnpx コマンドです。

$ npx csvtojson prefectures.csv > prefectures.json

中身を見るとほぼ狙い通りのJSONになっています。
改行がなんだか不自然ですが、気にしません。

$ cat prefectures.json 
[
{"名称":"北海道","読み方":"ほっかいどう"}
,
{"名称":"青森","読み方":"あおもり"}

]

ちなみに、値などにカンマを使いたい場合、その値をダブルクォーテーションで囲むと、
カンマも含めて値として扱われます。
csvはこんな感じ。

名称,読み方
北海道,"ほっかいどう,aaa"
青森,あおもり

最後に

以上です。お疲れ様でした!