20210314のiOSに関する記事は11件です。

[初リリース]Youtubeの複数曲mix動画をSpotifyプレイリストに変換するアプリ

こんにちは!趣味でエンジニアリングをしているcanalunです。こちらが自己紹介です!

去年の12月頃から初めてのiOSアプリ開発に取り組み、紆余曲折を経てなんとかYoutube-Spotify連携型プレイリスト作成アプリを完成・リリースさせることができました
せっかくなので、作成の経緯や苦労したポイントを自分なりにここに書き残してみようかと思います!

なお、アプリストアへのリンクはこちらです?
mixed!

どんなアプリなのか??

YouTubeには複数の曲を1つのクリップにまとめた"mix"と呼ばれるタイプの動画が多く存在します。hiphopやlofiと呼ばれるジャンルの音楽が好きな方は、きっとよくご存知ですよね?

例えばこのような動画です!
【ネオシティポップとか】Neo City Pop / Japanese Hip Hop etc. Mix + Video【日本語ラップとか】
[ ???????? ] 코딩할때 듣기 좋은 노래 • lofi type beat • 3 hours playlist

今回作成したアプリは、こういった動画をSpotify上のプレイリストに変換するものです!
具体的には、下記のような操作およびUIで動きます。
1. Spotifyでログイン
2. 好きなmix動画のURLを入力し、プレイリスト作成を開始
3. プレイリスト作成の成功/失敗を通知(成功の場合はSpotifyプレイリスト画面への遷移機能も提供)

Group 34@3x.png

なぜ作ろうと思ったのか??

私はlofiやhiphopが好きなのですが、特にこれらのジャンルはYouTube上にmix動画が多くあげられており、よく再生しています。
ただ、いつも再生しながらmixの曲順や構成を編集したいなあとかバックグラウンドで再生したいなあと思うことがよくありました。(YouTubeプレミアムには入っていないんですわ)
そこである日、「こういうのってもしかしたら動画を音楽プラットフォームのプレイリストに変換できれば解決するのでは??」と考え、アプリ開発を始めてみました!
幸い、いちおう調べた範囲で同じようなアプリはありませんでした?

使ってくれる人はいるだろうか??

結論としては「lofiやhiphopのような音楽を好むようなわりかし音楽が好きな人たちを中心に、需要自体はそれなりにあるのではないか」と考えました。
下記のような考え方をしましたが、自分の感覚で推測したに過ぎないので実際にはあまり当たっていない可能性もあるなと考えています?

めちゃ単純に"lofi mix"でgoogle検索すれば52,800,000本、"hiphop mix"でgoogle検索すれば12,100,000本の動画が見つかります。動画の多さを需要の大きさであると単純解釈すれば、lofiやhiphopのmixってそんな少ないわけではなくて、そこそこ多くの人に再生されているんだろうなと考えられます。

すなわち、今回のアプリを使ってくれる人が満たしているべき最低条件である「mix動画を好んでよく聴いている人」というのはまあまあいることになります(たぶんだけど)。
問題はこの人たちが下記2点を満たすかです。
1. Spotifyを使っているのか
2. mix動画をプレイリストにしたいと思っているか

1「Spotifyを使っているのか」については、十中八九Yesかと考えました。
lofiもhiphopも日常生活で話題に出ると、よく聴く人とあまり聴かない人に二分されるイメージがあります。lofiって言った時、たいてい反応は「lofiって何?」or「lofiいいよね!」です。肌感覚としては、そういう感じです。
そしてlofiなんていうジャンルを知っている人はけっこう音楽が好きなタイプですので、たいていはサブスクリプションサービスに登録していて、結構マニアックでかっこいい曲を知っています
問題はSpotifyを使っているのかということになりますが、これについてはサブスクリプションの双璧をなすapple musicとのユーザー数比較から、Spotifyにフォーカスしてもよいのではと思わされます。
2.5億人利用のSpotifyが見せつけるApple Musicとの「差」

Spotifyは2019年第3四半期の決算報告書を公開し、月間アクティブユーザー(MAU)の合計が2億4800万人に達したことを明らかにした...Appleは今年6月に同社が運営する音楽ストリーミングサービス「Apple Music」のユーザー数を6000万人と発表した。現段階では多少の増加をしていることは想定されるが、それでもSpotifyが世界的に多くのシェアを占めている状況に変わりない。

ただ、今回はiOSアプリを作るというわけで、iPhoneを使っている人はけっこうapple musicなんじゃないのというのは考慮すべきでした……実際、友人たちと話しながら最近そんな気がしてきています?

2「mix動画をプレイリストにしたいと思っているか」については、まあ多分そうなんじゃないのという感じです。
アプリを作ろうと思ったきっかけにも書きましたが、mixの曲順や構成を編集したいなあとかバックグラウンドで再生したいなあという思いはlofiやhiphopのmixを聴いているような人も多く抱えている気持ちだろうと推測できます。
まず編集モチベーションは、先程も述べたlofiやhiphopを聴いている人たちって結構マニアックだというところから、気に入ったプレイリストに曲の追加なんかをしたいということは十分にありうるかと考えました。
また、lofiやhiphopのmixは特に「作業用」という位置づけで動画が作られていることが多いことから、バックグラウンド再生の需要は相当あるのではと推測しました。パソコンの作業中であればYouTubeで事足りますが、電車の移動中や勉強中のバックグラウンド再生に十分に需要がありえます

以上より、lofiやhiphopに限ってもそれなりにいるのではないかと推測し、また実際にはmix動画は他音楽ジャンルにも多くあるので、必ずしも小さいとは言えないアンメットニーズが存在しうると考えました。(いちおう調べた範囲で同じようなアプリはありませんでした!よかった……)

技術的に苦労または工夫したポイント

ここからは技術的に苦労や工夫をしたポイントについて軽く触れていきたいと思います。
これらのポイントについては個別に深堀りした記事を今後作るつもり満々です。一旦、ここでは簡単に自分にとっての現段階での要点をおさえるに留めさせてください!
なお、今回は初めてのiOSアプリ開発ということもあり、4ヶ月前には「storyboardってなんや……」とか「API……アピ??」とか言っていました。そんな人間にとっての「要点」だということを心に留めておいていただけますと幸いです!
……要は、間違っているかもしれませんよということです!もし何かお気づきになったことやアドバイスがあったら、コメントいただけますと幸いです?

動画に用いられている曲情報の取得

動画そのものから曲の情報を取得するのは厳しかったので、概要欄から攻めることにしました。
競プロでの経験が活きて、簡単なアルゴリズムではありますが時間をかけずに作成できました!

たいていのmix動画は概要欄に曲の情報が記載されており、その多くは下記のような作りになっています。これはあくまでも私の経験則です

[曲の情報以外のこと]
XX:XX(再生時間) XX(曲名) [区切り文字(/や;や空白)] XX(アーティスト名)
XX:XX(再生時間) XX(曲名) [区切り文字(/や;や空白)] XX(アーティスト名)
……(繰り返し)
[曲の情報以外のこと]

ことが少し複雑化する要因は再生時間や曲名の記載順が動画によって前後することです。
例えば先ほど上で貼った動画の概要欄はこうなっています。

IV△7 - III7 - VIm7 - Vm7 - I7 とか
Track List
01 スチャダラパー × Nice & Smooth / 今夜はBoogie Down Bronx (original mix) 0:00​
02 Yogee New Waves / CLIMAX NIGHT 0:19​
03 宇宙ネコ子 / Night Cruising Love (with 入江陽 & lulu & Enjoy Music Club) 2:28​
……
30 TOCCHI / これだけで十分なのに 39:24​
CITYPOP​ HIPHOP​
MIX​
JAPANESECITYPOP​ JAPANESEHIPHOP​
シティポップ​ 日本語ラップ​
当動画で表示される広告は楽曲の使用許可に基づくものであり、動画制作者は管理できません。また、全ての広告収入は著作権者並びに各アーティストの方々に支払われます。ご理解のほど宜しくお願い致します。

そこで、今回はこういった概要欄をAPIで取得した上で、以下の手順でプレイリストを作成しています。
1. :が入っている行のみ抽出
2. 抽出した各行に対して、数字と記号を除去する操作を実行
3. "feat."など、Spotify検索時にノイズとなってしまう特定種類の文字列を除去
4. 各行を検索にかけ、検索結果トップの曲をプレイリストに追加

各行をそのまま検索にかけることで歌手名と曲名を区別する必要性を回避しました!

ただ当然、現段階ではこれで対応できない動画も多くあります。
1. :が入っていない動画
時間の表記をXX'XX"としている動画もあります。これは今後、対応が簡単です!
2. 時間が書いていない動画
これは相当厄介です。ただのテキストから曲名とアーティスト名だけを抜き出す方法は現段階では思いついていません。連続する単語列を片っ端からSpotify検索にかけてもいいのですが、最適列幅の特定やノイズとして入り込む無関係曲の除去をどうすればいいのでしょうか
3. 概要欄に何も書いていない動画
これが一番きついです。しかもまあまあこういう動画もあります。「今後実装したいこと」で述べるmusic recognitionとの連携が必須になるでしょう

このアルゴリズム部分が当該アプリのクオリティを80%決定する気がするので今後改善していかなければならないなと思っています……!

クロージャーを用いた通信待ち

API通信を主として、外部との通信を行うアプリの制作においては、通信した結果に得られたデータを用いてこの処理がしたい!という瞬間が必ず存在すると考えています。
この時、普通にプログラムを書いてしまうと、通信を実施するのに必要な時間が考慮されないまま、通信している間にプログラムが実行されていってしまい、サーバーからデータが得られたときにはプログラムが終わっているなんていうことがあります。というか、めちゃありました。
そこで、swiftのclosureと呼ばれる関数を用いてこれを解決します。closureには様々な説明の仕方があるようですが、今は一旦処理対象の値が確定してから処理を実行するタイプの関数であると理解しています。
そうです、通信して取得したデータに行いたい処理はclosureの形で書けば、対象であるデータが確定(=取得)されるまでは待っていてくれるようになるのです!初めはclosureってややこしそうだな、使うのやめとことか思っていましたが、こういった用途を学んだときはとても感動しました。

ライフサイクルを把握した上での実装

いっとき、viewに配置したボタンがどうがんばってもズレる。いくらコードを見直しても設定値は全く間違っていないという自体に心を折られました。
結局の所、viewのライフサイクルを理解せず、画面のレイアウトが決定される段階以前の部分で座標計算をしようとしていたことが原因でした
また、UI実装にとどまらず、githubで見つけたライブラリを使用した際にも、iOSのバージョンアップによるライフサイクルチェンジに対応させることが必要でした。
このときも、なぜか全然動かないライブラリを前に途方にくれた記憶があります。デバッグの手段をprint()を配置することしか知らない私は、とりあえずあらゆる部分にprintを配置し、2時間ほど溶かしたあとにappdelegateの関数が呼び出されていないことに気づいた次第です。デバッグの方法は今後勉強していきたいです。どなたかよい文献をご存知でしたら教えて下さい泣

SegueとPresentの使い分け

遷移の仕方や値渡しの仕方が異なる2つの画面遷移方法、SegueとPresentをどう使い分ければよいかを考えました。
他論点と同様に詳細は別記事にまわしますが、今回は異なるview間での値渡しが、クロージャーを用いていたとしても容易なPresentを主軸に実装を行いました

ユーザーの動きを想定したUI設計

今回アプリのUI設計は、とても親しいUIデザイナーの方に手伝ってもらいました。私のイメージしていた第一案は、そうとうbrush upされました。なんならもともと、アイコンはipadで作った手書きのニコニコマークでいくつもりでした。正気じゃないです。
この過程は相当勉強になりました。本当にありがとうございます泣
ここはまだ言語化できていない学びが多く、あとでまた記事にしていきたいと思います!!!

どう広めるか

アプリを作ったはいいものの、使う可能性のある人にどう届けるかが課題です。
広告を打つとかそんなことをするようなアレではないので、今回は一旦redditといった海外のものも含めたオンラインコミュニティ上で、適当な場所を見つけ、リンクと共に紹介文を投稿するやり方を取りました。
せっかく音楽関連であれば言語の垣根を越えようと思い、積極的に英語での使用を意識して作成しました!
他の方たちはどのような広め方をしているのか、もう少し今後勉強していきたいと思っています?

今後実装したいこと

今後実装したいことはいまのところ下記の通りです。

アルゴリズムの向上

先程も述べたアルゴリズムを採用していることから、実際にプレイリストを作成できる動画はかなり限られます。今後のaspirationとして、music recognition技術を連携することで「動画で使用されている音楽をplaylistにするアプリ」を実現できたら面白いなと考えています。
ShazamやSound Houndに代表されるmusic recognition技術はAPIがかなり限られており、いまのところの第一候補はAudDです。まさかの無料です(たぶん)。
もし実現すれば、mix動画だけでなく、例えばファッションショーのBGMをプレイリスト化する、気になる映像作品のBGMを特定するといった幅広い用途が拓かれていきます
なんとかしてYouTube動画の音声を抽出できればチャンスは十分あると考えていますが、調べてみた範囲ではYouTubeの規約では動画のDLは禁止されており、なんとかできないのかなあと検討しています。

ユーザー登録とレコメンド機能

これはもうおなじみですが、ユーザー登録できるようにしてレコメンドができるようにしてみたいです。
これは本当にシンプルなミドルウェアから、所有IPを伴ったミドルウェアへの価値向上という意味でも重要かと考えていますし、DBとサーバー連携の実装を勉強する意味でもかなりいい題材かもなと考えています。

変換履歴の蓄積

ユーザーの変換を履歴として蓄積するのも下記の効果をもたらす意味で有効かと思っています。
1. キャッシュの利用によるプレイリスト作成の高速化
これは特に、先ほど書いたようなmusic recognitionの実装を通じてアプリの処理が重くなってきたらクリティカルになるでしょう。計算量を線形から定数にできる(はずですよね流石に??)ので、大事です。
2. ランキングの作成
動画の変換される回数ランキングが作れるようになります。面白いかどうかはわかりませんが、「ユーザー登録とレコメンド機能」でも述べた意味をこれも同様に持つでしょう。

以上が初めて作ったiOSアプリの解説記事です!長く書いて疲れてしまいました?
こんな長文で書いて誰か読んでくれるのかな……もしここまで読んでいただいた方がいたら、本当にありがとうございました!!??

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Youtube-Spotify連携型プレイリスト作成アプリを作りました!

こんにちは!趣味でエンジニアリングをしているcanalunです。こちらが自己紹介です!

去年の12月頃から初めてのiOSアプリ開発に取り組み、紆余曲折を経てなんとかYoutube-Spotify連携型プレイリスト作成アプリを完成・リリースさせることができました
せっかくなので、作成の経緯や苦労したポイントを自分なりにここに書き残してみようかと思います!

なお、アプリストアへのリンクはこちらです?
mixed!

どんなアプリなのか??

YouTubeには複数の曲を1つのクリップにまとめた"mix"と呼ばれるタイプの動画が多く存在します。hiphopやlofiと呼ばれるジャンルの音楽が好きな方は、きっとよくご存知ですよね?

例えばこのような動画です!
【ネオシティポップとか】Neo City Pop / Japanese Hip Hop etc. Mix + Video【日本語ラップとか】
[ ???????? ] 코딩할때 듣기 좋은 노래 • lofi type beat • 3 hours playlist

今回作成したアプリは、こういった動画をSpotify上のプレイリストに変換するものです!
具体的には、下記のような操作およびUIで動きます。
1. Spotifyでログイン
2. 好きなmix動画のURLを入力し、プレイリスト作成を開始
3. プレイリスト作成の成功/失敗を通知(成功の場合はSpotifyプレイリスト画面への遷移機能も提供)

Group 34@3x.png

なぜ作ろうと思ったのか??

私はlofiやhiphopが好きなのですが、特にこれらのジャンルはYouTube上にmix動画が多くあげられており、よく再生しています。
ただ、いつも再生しながらmixの曲順や構成を編集したいなあとかバックグラウンドで再生したいなあと思うことがよくありました。(YouTubeプレミアムには入っていないんですわ)
そこである日、「こういうのってもしかしたら動画を音楽プラットフォームのプレイリストに変換できれば解決するのでは??」と考え、アプリ開発を始めてみました!
幸い、いちおう調べた範囲で同じようなアプリはありませんでした?

使ってくれる人はいるだろうか??

結論としては「lofiやhiphopのような音楽を好むようなわりかし音楽が好きな人たちを中心に、需要自体はそれなりにあるのではないか」と考えました。
下記のような考え方をしましたが、自分の感覚で推測したに過ぎないので実際にはあまり当たっていない可能性もあるなと考えています?

めちゃ単純に"lofi mix"でgoogle検索すれば52,800,000本、"hiphop mix"でgoogle検索すれば12,100,000本の動画が見つかります。動画の多さを需要の大きさであると単純解釈すれば、lofiやhiphopのmixってそんな少ないわけではなくて、そこそこ多くの人に再生されているんだろうなと考えられます。

すなわち、今回のアプリを使ってくれる人が満たしているべき最低条件である「mix動画を好んでよく聴いている人」というのはまあまあいることになります(たぶんだけど)。
問題はこの人たちが下記2点を満たすかです。
1. Spotifyを使っているのか
2. mix動画をプレイリストにしたいと思っているか

1「Spotifyを使っているのか」については、十中八九Yesかと考えました。
lofiもhiphopも日常生活で話題に出ると、よく聴く人とあまり聴かない人に二分されるイメージがあります。lofiって言った時、たいてい反応は「lofiって何?」or「lofiいいよね!」です。肌感覚としては、そういう感じです。
そしてlofiなんていうジャンルを知っている人はけっこう音楽が好きなタイプですので、たいていはサブスクリプションサービスに登録していて、結構マニアックでかっこいい曲を知っています
問題はSpotifyを使っているのかということになりますが、これについてはサブスクリプションの双璧をなすapple musicとのユーザー数比較から、Spotifyにフォーカスしてもよいのではと思わされます。
2.5億人利用のSpotifyが見せつけるApple Musicとの「差」

Spotifyは2019年第3四半期の決算報告書を公開し、月間アクティブユーザー(MAU)の合計が2億4800万人に達したことを明らかにした...Appleは今年6月に同社が運営する音楽ストリーミングサービス「Apple Music」のユーザー数を6000万人と発表した。現段階では多少の増加をしていることは想定されるが、それでもSpotifyが世界的に多くのシェアを占めている状況に変わりない。

ただ、今回はiOSアプリを作るというわけで、iPhoneを使っている人はけっこうapple musicなんじゃないのというのは考慮すべきでした……実際、友人たちと話しながら最近そんな気がしてきています?

2「mix動画をプレイリストにしたいと思っているか」については、まあ多分そうなんじゃないのという感じです。
アプリを作ろうと思ったきっかけにも書きましたが、mixの曲順や構成を編集したいなあとかバックグラウンドで再生したいなあという思いはlofiやhiphopのmixを聴いているような人も多く抱えている気持ちだろうと推測できます。
まず編集モチベーションは、先程も述べたlofiやhiphopを聴いている人たちって結構マニアックだというところから、気に入ったプレイリストに曲の追加なんかをしたいということは十分にありうるかと考えました。
また、lofiやhiphopのmixは特に「作業用」という位置づけで動画が作られていることが多いことから、バックグラウンド再生の需要は相当あるのではと推測しました。パソコンの作業中であればYouTubeで事足りますが、電車の移動中や勉強中のバックグラウンド再生に十分に需要がありえます

以上より、lofiやhiphopに限ってもそれなりにいるのではないかと推測し、また実際にはmix動画は他音楽ジャンルにも多くあるので、必ずしも小さいとは言えないアンメットニーズが存在しうると考えました。(いちおう調べた範囲で同じようなアプリはありませんでした!よかった……)

技術的に苦労または工夫したポイント

ここからは技術的に苦労や工夫をしたポイントについて軽く触れていきたいと思います。
これらのポイントについては個別に深堀りした記事を今後作るつもり満々です。一旦、ここでは簡単に自分にとっての現段階での要点をおさえるに留めさせてください!
なお、今回は初めてのiOSアプリ開発ということもあり、4ヶ月前には「storyboardってなんや……」とか「API……アピ??」とか言っていました。そんな人間にとっての「要点」だということを心に留めておいていただけますと幸いです!
……要は、間違っているかもしれませんよということです!もし何かお気づきになったことやアドバイスがあったら、コメントいただけますと幸いです?

動画に用いられている曲情報の取得

動画そのものから曲の情報を取得するのは厳しかったので、概要欄から攻めることにしました。
競プロでの経験が活きて、簡単なアルゴリズムではありますが時間をかけずに作成できました!

たいていのmix動画は概要欄に曲の情報が記載されており、その多くは下記のような作りになっています。これはあくまでも私の経験則です

[曲の情報以外のこと]
XX:XX(再生時間) XX(曲名) [区切り文字(/や;や空白)] XX(アーティスト名)
XX:XX(再生時間) XX(曲名) [区切り文字(/や;や空白)] XX(アーティスト名)
……(繰り返し)
[曲の情報以外のこと]

ことが少し複雑化する要因は再生時間や曲名の記載順が動画によって前後することです。
例えば先ほど上で貼った動画の概要欄はこうなっています。

IV△7 - III7 - VIm7 - Vm7 - I7 とか
Track List
01 スチャダラパー × Nice & Smooth / 今夜はBoogie Down Bronx (original mix) 0:00​
02 Yogee New Waves / CLIMAX NIGHT 0:19​
03 宇宙ネコ子 / Night Cruising Love (with 入江陽 & lulu & Enjoy Music Club) 2:28​
……
30 TOCCHI / これだけで十分なのに 39:24​
CITYPOP​ HIPHOP​
MIX​
JAPANESECITYPOP​ JAPANESEHIPHOP​
シティポップ​ 日本語ラップ​
当動画で表示される広告は楽曲の使用許可に基づくものであり、動画制作者は管理できません。また、全ての広告収入は著作権者並びに各アーティストの方々に支払われます。ご理解のほど宜しくお願い致します。

そこで、今回はこういった概要欄をAPIで取得した上で、以下の手順でプレイリストを作成しています。
1. :が入っている行のみ抽出
2. 抽出した各行に対して、数字と記号を除去する操作を実行
3. "feat."など、Spotify検索時にノイズとなってしまう特定種類の文字列を除去
4. 各行を検索にかけ、検索結果トップの曲をプレイリストに追加

各行をそのまま検索にかけることで歌手名と曲名を区別する必要性を回避しました!

ただ当然、現段階ではこれで対応できない動画も多くあります。
1. :が入っていない動画
時間の表記をXX'XX"としている動画もあります。これは今後、対応が簡単です!
2. 時間が書いていない動画
これは相当厄介です。ただのテキストから曲名とアーティスト名だけを抜き出す方法は現段階では思いついていません。連続する単語列を片っ端からSpotify検索にかけてもいいのですが、最適列幅の特定やノイズとして入り込む無関係曲の除去をどうすればいいのでしょうか
3. 概要欄に何も書いていない動画
これが一番きついです。しかもまあまあこういう動画もあります。「今後実装したいこと」で述べるmusic recognitionとの連携が必須になるでしょう

このアルゴリズム部分が当該アプリのクオリティを80%決定する気がするので今後改善していかなければならないなと思っています……!

クロージャーを用いた通信待ち

API通信を主として、外部との通信を行うアプリの制作においては、通信した結果に得られたデータを用いてこの処理がしたい!という瞬間が必ず存在すると考えています。
この時、普通にプログラムを書いてしまうと、通信を実施するのに必要な時間が考慮されないまま、通信している間にプログラムが実行されていってしまい、サーバーからデータが得られたときにはプログラムが終わっているなんていうことがあります。というか、めちゃありました。
そこで、swiftのclosureと呼ばれる関数を用いてこれを解決します。closureには様々な説明の仕方があるようですが、今は一旦処理対象の値が確定してから処理を実行するタイプの関数であると理解しています。
そうです、通信して取得したデータに行いたい処理はclosureの形で書けば、対象であるデータが確定(=取得)されるまでは待っていてくれるようになるのです!初めはclosureってややこしそうだな、使うのやめとことか思っていましたが、こういった用途を学んだときはとても感動しました。

ライフサイクルを把握した上での実装

いっとき、viewに配置したボタンがどうがんばってもズレる。いくらコードを見直しても設定値は全く間違っていないという自体に心を折られました。
結局の所、viewのライフサイクルを理解せず、画面のレイアウトが決定される段階以前の部分で座標計算をしようとしていたことが原因でした
また、UI実装にとどまらず、githubで見つけたライブラリを使用した際にも、iOSのバージョンアップによるライフサイクルチェンジに対応させることが必要でした。
このときも、なぜか全然動かないライブラリを前に途方にくれた記憶があります。デバッグの手段をprint()を配置することしか知らない私は、とりあえずあらゆる部分にprintを配置し、2時間ほど溶かしたあとにappdelegateの関数が呼び出されていないことに気づいた次第です。デバッグの方法は今後勉強していきたいです。どなたかよい文献をご存知でしたら教えて下さい泣

SegueとPresentの使い分け

遷移の仕方や値渡しの仕方が異なる2つの画面遷移方法、SegueとPresentをどう使い分ければよいかを考えました。
他論点と同様に詳細は別記事にまわしますが、今回は異なるview間での値渡しが、クロージャーを用いていたとしても容易なPresentを主軸に実装を行いました

ユーザーの動きを想定したUI設計

今回アプリのUI設計は、とても親しいUIデザイナーの方に手伝ってもらいました。私のイメージしていた第一案は、そうとうbrush upされました。なんならもともと、アイコンはipadで作った手書きのニコニコマークでいくつもりでした。正気じゃないです。
この過程は相当勉強になりました。本当にありがとうございます泣
ここはまだ言語化できていない学びが多く、あとでまた記事にしていきたいと思います!!!

どう広めるか

アプリを作ったはいいものの、使う可能性のある人にどう届けるかが課題です。
広告を打つとかそんなことをするようなアレではないので、今回は一旦redditといった海外のものも含めたオンラインコミュニティ上で、適当な場所を見つけ、リンクと共に紹介文を投稿するやり方を取りました。
せっかく音楽関連であれば言語の垣根を越えようと思い、積極的に英語での使用を意識して作成しました!
他の方たちはどのような広め方をしているのか、もう少し今後勉強していきたいと思っています?

今後実装したいこと

今後実装したいことはいまのところ下記の通りです。

アルゴリズムの向上

先程も述べたアルゴリズムを採用していることから、実際にプレイリストを作成できる動画はかなり限られます。今後のaspirationとして、music recognition技術を連携することで「動画で使用されている音楽をplaylistにするアプリ」を実現できたら面白いなと考えています。
ShazamやSound Houndに代表されるmusic recognition技術はAPIがかなり限られており、いまのところの第一候補はAudDです。まさかの無料です(たぶん)。
もし実現すれば、mix動画だけでなく、例えばファッションショーのBGMをプレイリスト化する、気になる映像作品のBGMを特定するといった幅広い用途が拓かれていきます
なんとかしてYouTube動画の音声を抽出できればチャンスは十分あると考えていますが、調べてみた範囲ではYouTubeの規約では動画のDLは禁止されており、なんとかできないのかなあと検討しています。

ユーザー登録とレコメンド機能

これはもうおなじみですが、ユーザー登録できるようにしてレコメンドができるようにしてみたいです。
これは本当にシンプルなミドルウェアから、所有IPを伴ったミドルウェアへの価値向上という意味でも重要かと考えていますし、DBとサーバー連携の実装を勉強する意味でもかなりいい題材かもなと考えています。

変換履歴の蓄積

ユーザーの変換を履歴として蓄積するのも下記の効果をもたらす意味で有効かと思っています。
1. キャッシュの利用によるプレイリスト作成の高速化
これは特に、先ほど書いたようなmusic recognitionの実装を通じてアプリの処理が重くなってきたらクリティカルになるでしょう。計算量を線形から定数にできる(はずですよね流石に??)ので、大事です。
2. ランキングの作成
動画の変換される回数ランキングが作れるようになります。面白いかどうかはわかりませんが、「ユーザー登録とレコメンド機能」でも述べた意味をこれも同様に持つでしょう。

以上が初めて作ったiOSアプリの解説記事です!長く書いて疲れてしまいました?
こんな長文で書いて誰か読んでくれるのかな……もしここまで読んでいただいた方がいたら、本当にありがとうございました!!??

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Swift5]MessageUIを用いたメール機能(お問い合わせ機能)の実装方法

やりたいこと

今回はMessageUIを用いたメール機能の実装方法を紹介します。

メール機能 == お問い合わせ機能として扱えるかと思います。
ユーザー情報を管理する場合、通報機能としても代用できるのでぜひ参考にしてください!

使用するライブラリ

import MessageUI

コード紹介

今回はお問い合わせボタンが存在すると仮定して紹介します。
@IBAction func tapMailButton(_ sender: Any)はお問い合わせボタンがタップされると呼ばれます。

// お問い合わせボックスをタップすると呼ばれる
@IBAction func tapMailButton(_ sender: Any) {

  // メールを送信できるかどうかの確認
  if !MFMailComposeViewController.canSendMail() {
    print("Mail services are not available")
    return
  }

  // インスタンスの作成とデリゲートの委託
  let mailViewController = MFMailComposeViewController()
      mailViewController.mailComposeDelegate = self

  // 宛先の設定(開発者側のアドレス)
  let toRecipients = ["ここにユーザーから受信するメールアドレスを入力"]

  // 件名と宛先の表示
  mailViewController.setSubject("開発者側で設定する件名")
  mailViewController.setToRecipients(toRecipients)
  mailViewController.setMessageBody("開発者側で設定する本文", isHTML: false)

  // mailViewControllerの反映(メール内容の反映)
  self.present(mailViewController, animated: true, completion: nil)
}

これでメールの設定は完了です。

次にメールを開いた後の結果でアクションを分岐します。

// メール機能終了処理
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {

  // メールの結果で条件分岐
  switch result {

  // キャンセルの場合
  case .cancelled:
    print("Email Send Cancelled")
    break

  // 下書き保存の場合
  case .saved:
    print("Email Saved as a Draft")
    break

  // 送信成功の場合
  case .sent:
    print("Email Sent Successfully")
    break

  // 送信失敗の場合
  case .failed:
    print("Email Send Failed")
    break
  default:
    break
  }

  //メールを閉じる
  controller.dismiss(animated: true, completion: nil)
}

これでMessageUIを用いたメール機能(お問い合わせ機能)の実装は完了です。
アドレスや、件名や本文を自身で触ってみると楽しいですよ!

備考

メール機能はXcodeのシュミレーターでは動かないので、動作確認は実機で行うようにして下さい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flutterで普通のスワイプで画面をpopする

FlutterでNavigator.push(context)して遷移した先の画面で元の画面に戻るときにはNavigator.pop(context)をアイコンに設置したりエッジスワイプしたりしますよね。
通常のスワイプでも戻したかったのでやり方を見つけるのに少々苦労しましたがなんとかできました。

GestureDetector(
  onHorizontalDragUpdate: (details) {
    if (details.delta.dx > 18) {
      Navigator.pop(context);
    }
  },
  child: ・・・,
);

このようにGestureDetector.onHorizontalDragUpdateを設定して横方向のスワイプをトリガーにnavigator.pop(context)を実行するようにしました。
details.delta.dxはスワイプの速度で、正の向きが左→右です。しきい値として設定した18を超えるとnavigator.pop(context)を実行します。この値は実機で触りながら調整しました。

参考

Flutter公式ドキュメント onHorizontalDragUpdate
https://api.flutter.dev/flutter/widgets/GestureDetector/onHorizontalDragUpdate.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

さよならCocoaPods

CocoaPodsからSwift Package Managerにライブラリの管理を移行するメモ

プロジェクトファイル内のCocoaPods関連の依存を削除する

pod deintegrate

続いて残ったファイルを削除する

git rm -r  Podfile Podfile.lock *.xcworkspace

これできれいになったので改めてCocoaPodsで利用していたライブラリをSwift Package Managerを使った形にしていく。
Xcodeのメニューから[File]-[Swift Packages]-[Add Package Dependency]を実行し、githubなどのリポジトリURLを貼り付けていく。

podコマンドがmac OSのバージョンアップで通らなくなっている場合がある。
その場合は以下を実行する。

sudo gem install cocoapods -n /usr/local/bin
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google AdMob iOS ( Objective-C ) でアダプティブバナーを表示する

サンプルプログラムを実行してみる

1.手順1

下記のリンクより、 最新バージョンの "Adaptive Banner Example" の ObjC をダウンロードする。
( 記事作成時の最新バージョンは v7.22 )

2.手順2

( 1 ) Mobile Ads SDK をインポートします。

( 2 ) プロジェクトのビルド設定で、[Other Linker Flags] に -ObjC リンカーフラグを追加します。

具体的な作業内容は、下記のリンクの内容を確認してください。

3.手順3

プロビジョニングファイルを登録し、iOS Simulators または、実機により実行する。

4.備考

サンプルプログラムは、 Interface Builder により広告を表示するようになっています。
プログラムで表示する場合は、下記の手順でサンプルプログラムを修正してください。

メイン画面をプログラムで表示する場合 ( Objective-C )

1."Main.storyboard"(メイン画面)を削除します。

image.png

2.Info.plist の "Launch screen interface file base name" 、 "Main storyboard file base name" を削除します。

image.png

3.AppDelegate.h を修正し、メイン画面をプログラムで定義します。

AppDelegate.h ( Objective-C )
#import <UIKit/UIKit.h>

/* 下記内容を追加 (メイン画面定義) */
#import "ViewController.h"
@class ViewController;

@interface AppDelegate : UIResponder <UIApplicationDelegate>
{
    /* 下記内容を追加 (メイン画面定義) */
    ViewController *viewController_;
}

@property (strong, nonatomic) UIWindow *window

@end

4.AppDelegate.m を修正し、メイン画面をプログラムで表示します。

AppDelegate.m ( Objective-C )
- (BOOL)application:(UIApplication *)application 
     didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

    /* 下記内容を追加 (メイン画面を表示) */
    /* window 作成 */
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    /* メイン画面作成・表示 */
    viewController_ = [[ViewController alloc] init];
    [self.window addSubview:viewController_.view];
    /* rootViewControllerの設定 [ iOS9対応 ] */
    self.window.rootViewController = viewController_;
    /* メイン画面を前面にする */
    [self.window bringSubviewToFront:viewController_.view];


    ( その他の記述... )

}

5.ViewController.h のバナー定義を削除する。

ViewController.h ( Objective-C )
#import <UIKit/UIKit.h>

/* 下記を削除 */
/* @class GADBannerView; */

@interface ViewController : UIViewController

/* 下記を削除 */
/* @property(nonatomic, weak) IBOutlet GADBannerView *bannerView; */

@end

6.下記のリンクの 「プログラムで行う場合」 の項目内容を参考にして広告を表示してください。

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React/スマホアプリ開発未経験者がReact NativeでiOSアプリを作ってみた

JavaScript(フレームワーク未使用)業務経験1年・React/React Nativeもスマホアプリ開発のいろはも分からなかった私が、平日夜・休日の時間を使って、約2ヶ月(80時間)でReact NativeでiOSアプリを開発したときの流れや参考になった記事・動画を紹介します!

自分自身、何度も壁に打ち当たり、心が折れそうだったので、少しでも役に立てればと思います!
あと、少し込み入った問題になると、英語のサイトがかなり多くなってくるので、ポジティブに捉えるなら、良い英語の勉強になると思います!笑

作った単語帳アプリlankiageこちら
ソースコードはこちら

そもそもReact/React Nativeとは?

Reactは、Facebook社が開発した、WebアプリケーションのUIを構築するためのライブラリです。
JavaScript三大フレームワーク・ライブラリとして、他にVue.jsやAngular.jsがありますが、世界的にReactが最も利用されているため、フロントエンドとしては最も身につけておくべきスキルだと思います。

一方、React Nativeは、JavaSciptでiOSアプリ・Androidアプリを、同時に、かつReactライクに開発できるフレームワークで、いわゆるクロスプラットフォームフレームワークの1つです。
従来は、OSに合わせて言語を使い分ける必要がありましたが、これによってコード1つで両方のアプリが開発できるわけです。
現在は、Flutterというフレームワークの方が注目されているという記事もありますが、DartというGoogleの独自言語ということもあり、React NativeはJavaScirpt経験者がさらっとスマホアプリに手を出すにはもってこいのライブラリだと思います。

1. 開発環境構築編

1-1. 開発プロジェクトのインストール

coming soon...

1-2. Xcode

coming soon...

2. 実装編

実際に使用した主なライブラリについてご紹介します。

2-1. Realm(ローカルデータベース)

coming soon...

2-2. react-native-vector-icons(アイコン)

豊富なアイコンが使える便利なライブラリですが、かなりビルドに苦労しました。。。

特にこのステップを見落としがちなので、丁寧にやっていきましょう!

2-3. React Navigation(画面遷移)

画面間の遷移を扱うライブラリです。
似たライブラリとしてReact Native Navigationもありますが、設定ファイルであるAppDelegate.mをいじる必要があった上に、使い勝手が良くないと感じたので、今回はReact Navigationを使うことにしました。

3. アプリ申請編

coming soon...

4. 一通り開発をやってみて...

coming soon...

5. 実際に開発したアプリ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初めてReact NativeでiOSアプリを開発した時に参考にした記事・サイトたち

JavaScript(フレームワーク未使用)業務経験1年・React/React Nativeもスマホアプリ開発のいろはも分からなかった私が、平日夜・休日の時間を使って、約2ヶ月(80時間)かけてReact NativeでiOSアプリを開発したときの流れや参考になった記事・動画を紹介します!

自分自身、何度も壁に打ち当たり、心が折れそうだったので、少しでも役に立てればと思います!
あと日本語のドキュメントが少ないため、英語のサイトを見ることも多くなりますが、英語の良い勉強の機会だと思って、めげずに頑張りましょう!!

作った外国語用単語帳アプリ"lankiage"はこちら(App Store)
ソースコードはこちら(Git Hub)

そもそもReact/React Nativeとは?

Reactは、Facebook社が開発した、WebアプリケーションのUIを構築するためのライブラリです。
JavaScript三大フレームワーク・ライブラリとして、他にVue.jsやAngular.jsがありますが、世界的にReactが最も利用されているため、フロントエンドとしては最も身につけておくべきスキルだと思います。

一方、React Nativeは、JavaSciptでiOSアプリ・Androidアプリを、同時に、かつReactライクに開発できるフレームワークで、いわゆるクロスプラットフォームフレームワークの1つです。
従来は、OSに合わせて言語を使い分ける必要がありましたが、これによってコード1つで両方のアプリが開発できるわけです。
現在は、Flutterというフレームワークの方が注目されているという記事もありますが、DartというGoogleの独自言語ということもあり、React NativeはJavaScirpt経験者がさらっとスマホアプリに手を出すにはもってこいのライブラリだと思います。
※React Native勉強中にFlutterの勢いを知ったのはちょっとショックでした(汗)

1. 開発環境構築編

1-1. 開発プロジェクトのインストール

coming soon...

1-2. Xcode

coming soon...

2. 実装編

今回、実際に使用したライブラリについてご紹介します。

2-1. Realm(モバイルデータベース)

世はクラウドブームですが、今回は全てデータをモバイル(ローカル)で保存することにしました。(バックエンドも開発するとなると、かなりの負荷があるので、やめました。)
その際に使用したライブラリがこのRealmです。
他にCoreDataやSQLiteというのがあるようですが、realmが話題のようなので、これを使いました。
※React Native Expoを使っている方は、Realmが利用できないので、ご注意ください。

2-2. react-native-vector-icons(アイコン)

豊富なアイコンが使える便利なライブラリですが、かなりビルドに苦労しました。。。

特にXcodeの設定を変更するこのステップを見落としがちなので、丁寧にやっていきましょう!

2-3. React Navigation(画面遷移)

画面間の遷移を扱うライブラリです。
似たライブラリとしてReact Native Navigationもありますが、設定ファイルであるAppDelegate.mをいじる必要があった上に、使い勝手が良くないと感じたので、今回はReact Navigationを使うことにしました。

2-4. react-native-picker-select(セレクトボックス)

React Nativeのセレクトボックスライブラリは中々少ないようで、今回はこれを使いました。ちょっと微妙ですが。。

2-5. その他

その他、使用頻度は高くないかもしれませんが、今回使用したライブラリはこちらです。

3. アプリ申請編

基本的には、[2020年版]AppleにiOSアプリを申請する方法を参考に進めましたが、
まず第一にやるべきは、Apple Developer Programへの登録です。
(年間税込¥12,980と高額ですが、目を瞑りましょう。。)
進めていく中で一番厄介だったのが、スクリーンショット登録だったので、それについて解説します。

スクリーンショット登録

App Storeのプレビューに表示される、いわばそのアプリの顔ともなる重要な部分です!
下の「スクリーンショット」の3枚がこれに当たります。

画面のスクリーンショットを全面にしても申請は通りますが、そのアプリのメリットや特徴をできるだけ一目でわかるように工夫しましょう。

2021年3月現在、iPhone 6.5インチ、iPhone 5.5インチ、iPad 12.9インチの3サイズのスクリーンショットをそれぞれ3つ登録する必要があるので、厄介ですが丁寧にやりましょう!

では、どのように良い感じのスクリーンショットを作るかですが、、無料でやるのにあまり参考にできるサイトがなかったので、簡単に私が行ったやり方を共有します。

  1. 登録すべきスクリーンショットサイズのシミュレータ(3種類)を起動し、スクリーンショットを撮る。
  2. AppLauchPadでGLOBAL SCREENSHOTから1.で撮ったスクリーンショットを選択(5.5インチでやると、iPhoneのフレームに余白がありますが、無視して大丈夫です。)
  3. ダウンロード画面から、エクスポートしたいサイズのスクリーンを右クリックで「画像をコピー」
  4. keynoteでスライドサイズをスクリーンショットのサイズにカスタマイズし、3.でコピーした画像を貼り付けて、編集する。(keynoteのインスタントアルファ機能を使うと、背景の白地がなくなり、綺麗にフレームだけを切り出せます!)

4. 一通り開発をやってみて...

一番の感想は、ビルドのエラーが多すぎて辛いということ(泣)
こればかりは初心者のうちは仕方ないですかね、、
ただ徐々に耐性がついてきたので、ある意味エンジニアとしてスキルアップしたと思います。笑

あとは、進めていく中でReactのコーディングのトレンドを知っていき、自分がいかにレガシーな書き方をしていたかということに気づかされました、、
途中で引き返しにくかったので、諦めましたが、最低限以下くらいは知っておいた方が良いと思います。
・TypeScript > JavaScript
・関数コンポーネント > クラスコンポーネント
・Stateの管理はReduxでやるべし
記事を調べる場合は、できるだけ新しいものに触れることをオススメします。

【参考記事】

5. 実際に開発したアプリ

coming soon...

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スマホアプリをビジュアルプログラミングで開発できる MIT App Inventor を使い Androidスマホ・iPhone から LINE に通知を送る

はじめに

これまで以下の記事などを書いてきた、スマホアプリをビジュアルプログラミングで開発できる MIT App Inventor。

今まで、Androidアプリ開発のみに対応していたのですが、作ったものを iOS・iPadOS上で動かすアプリもリリースされました(Android だと APKファイルを生成することもできるものの、 iOS・iPadOS だとそれに相当することはできなそうですが...)。

●‎MIT App Inventor on the App Store
 https://apps.apple.com/us/app/mit-app-inventor/id1422709355
MIT App Inventor(iOS・iPadOS).jpg

今回作ったものが動作している様子

今回は、MIT App Inventor の HTTPリクエストを使うブロックを試したのですが、それをせっかくなので Androidスマホ上だけでなく iPhone上でも動かしてみます。結果から掲載すると、このようなものができてます。

作ってみる

では、ここから作り方の概要を書いていきます。

LINE Notify の準備

今回、LINE に通知を送る処理を行いますが、それを手軽に行える LINE Notify を用います。
LINE Notify の公式の APIドキュメントは以下がありますが、とりあえず使ってみる場合は、使い方を記載した記事を別途探すほうが簡単かもしれません。この記事では、詳細な部分は省略します。

●LINE Notify API Document
 https://notify-bot.line.me/doc/ja/

利用のための準備を行うと、トークンが発行されますので、そのトークンをメモしておいてください。

画面を作る

LINE Notify の準備ができたところで、App Inventor で画面を作っていきます。
過去の記事では、公式の英語版と日本語化プロジェクトで準備されたものの両方を試していますが、今回は日本語化されたもののほうを使って試していきます。

画面構成の全体は以下のとおりです。
画面構成.jpg

配置したコンポーネントは以下の 4つです。

  • ユーザーインターフェース > ボタン
  • ユーザーインターフェース > ラベル
  • 接続 > Web
  • センサー > 加速度センサー

「接続 > Web」の部分が、今回の重要な部分である HTTPリクエストを送る部分です。
なお、ボタンを押したら通知される、という動作のみを試すなら、上記で最低限必要なものは「ボタン・Web」の 2つです。今回、LINE Notify のレスポンスをデバッグ用に表示させるためにラベルを使っています。また、加速度セナーは、端末を振ったら通知されるという動作をさせるために追加しています。

上記のコンポーネントは、コンポーネントの名前や表示するテキストを変えています。デフォルトのままでも動作に支障はないですが、分かりやすくするために適宜変更しておくのをオススメします。

プログラムを作る

ここから、プログラムを作っていきます。作ったもの全体は以下のとおりです。
ブロックのプログラム.jpg

主な構成要素は 3つです。

ユーザーのアクションに反応させる部分

以下の部分は、ユーザーがボタンを押した時と、ユーザーがスマホを振った時(※ 加速度センサーが関係する処理)に実行する処理を設定したものです。
アクションが起こされた時.jpeg

内容は、LINE Notify に通知をする処理なのですが、ここではそれらの共通部分を関数に切り出して、その関数に通知するテキストを渡すだけの形にしています。

LINE Notify用の処理(関数にしたもの)

上記で設定したユーザーのアクションが行われた時、LINE に通知を送る部分を作ったのが以下です。
関数.jpeg

まず、LINE Notify のアクセス先URL として https://notify-api.line.me/api/notify を設定しています。

また、その後に LINE Notify に必要な HTTPリクエストのリクエストヘッダと、送信するテキストを設定しています。それぞれ、以下のフォーマットになるように設定しています。

  • リクエストヘッダ(※ リストを使って、以下の内容をまとめる)
    • Content-Typeapplication/x-www-form-urlencoded
    • AuthorizationBearer 【LINE Notify の設定で発行されたトークン】
  • POST するテキストの内容
    • message=【送信するテキスト】

上記のリクエストヘッダの Authorization では、 Bearer の後に半角のスペースを入れて、その後に「LINE Notify の設定で発行されたトークン」を書きます。
送信するテキストは、関数の引数として取得されたものを用います。その際に、 message=という書き方にする必要があるため、テキストの結合を使っています。

このあたりの話は、以前、ビジュアルプログラミングの UIFlow を使って M5Stack社のデバイスから LINE Notify を使った時、似たようなことを試したことがあったため、その時の内容を活用しました。以下の記事に、その内容はまとめています。
 ●LINE Notify を利用して UIFlow のプログラムで LINE に通知を送る(日本語テキストも送信) #M5Stack - Qiita
  https://qiita.com/youtoy/items/76586479c2d4c5893c5b

HTTPリクエストのレスポンスを受け取った後の動作

以下はデバッグ用で、HTTPリクエストに対するレスポンスを表示させるために作ったものです。
レスポンスを受け取って表示.jpeg

単純な内容で、レスポンスとして受け取ったテキストのうち、レスポンスコードをラベルに表示するだけのものです。

以下にあるように、成功すれば 200 が返ってきて表示されるはずです。
 ●HTTP レスポンスステータスコード - HTTP | MDN
  https://developer.mozilla.org/ja/docs/Web/HTTP/Status

動作させた時の様子

記事のはじめのほうの「今回作ったものが動作している様子」の部分で掲載したとおりの動作となりました。

今回、Android のコンパニオンアプリ・iPhone用のコンパニオンアプリの両方で動作させましたが、どちらも問題なく動作しました。1点、「端末が振られた時」という条件をつけた部分で、端末を画面と水平な方向に振って試した時に Android のほうは簡単に反応したのですが、iPhone のほうは反応しない感じでした。動画を見ていただくと分かるのですが、iPhone のほうは端末の画面と垂直な方向にも動きをつけたものもためしました。そのやり方だと、反応しやすかった感じがしました。

終わりに

今回、App Inventor の HTTPリクエストを送る機能を使い、作ったアプリから LINE に通知を送るものを作りました。HTTPリクエストの処理は、今回使った POST だけでなく GET や他のものをつけるようですので、今回使った LINE Notify 以外の API も試せればと思います。

なお他の事例で Googleスプレッドシートと連携させるものを試された記事がありますので、そちらを紹介して終わります。
 【 @fukumura_kaigo さんの記事 】
 ●MIT App InventorのWebコンポーネントでSpreadsheetに値を書き込む - Qiita
  https://qiita.com/fukumura_kaigo/items/8d233dbc56a3defb0c2d

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactiveSwiftのActionで発火したSingalProducerをDisposeする方法の検討

問題

下記のようなActionがあるとします。

class ViewController: UIViewController {
    private let action = Action<Void, Void, Never> {
        SignalProducer { observer, lifetime in
            let workItem = DispatchWorkItem {
                observer.send(value: ())
                observer.sendCompleted()
            }

            DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: workItem)

            lifetime.observeEnded {
                workItem.cancel()
            }
        }
    }
}

下記各パターンでviewDidAppearで発火して、完了する前にViewControllerdeinitしても、disposeされません。
Action.swift#L143のソースからもdisposeを管理していないため、disposeされないことがわかります。

    private let disposable = CompositeDisposable()

    override func viewDidLoad() {
        super.viewDidLoad()
        // パターン1
        self.action <~ self.reactive.viewDidAppear

        // パターン2
        self.action <~ self.reactive.viewDidAppear.take(during: self.reactive.lifetime)

        // パターン3
        self.disposable += self.action <~ self.reactive.viewDidAppear
    }

    // パターン4
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        self.action.apply().start()
    }

    deinit {
        self.disposable.dispose()
    }

disposeする方法

Action内で生成したSignalProducerを直接disposeするしかなさそう

パターン1

<~のバインディング演算子が使えず、また入力が必要な場合もスマートに値を入力できなさそうです。

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        self.action.apply().take(during: self.reactive.lifetime).start()
    }

パターン2

<~のバインディング演算子が使えて、ソースから値を入力できるので、パターン1よりスマートそう。
ただ、余分に拡張関数を追加する必要があるので、コード量が必要。

    override func viewDidLoad() {
        super.viewDidLoad()
        self.reactive.action <~ self.reactive.viewDidAppear
    }

private extension Reactive where Base: ViewController {
    var action: BindingTarget<Void> {
        self.makeBindingTarget { base, _ in
            base.action.apply().take(during: base.reactive.lifetime).start()
        }
    }
}

パターン3

入力に合わせて、Lifetimeも送ってみる。
Lifetimeしか対応できないため、他のdisposeパターンに対応しきれない。

class ViewController: UIViewController {
    private let action = Action<(Void, Lifetime), Void, Never> {
        SignalProducer { observer, lifetime in
            let workItem = DispatchWorkItem {
                observer.send(value: ())
                observer.sendCompleted()
            }

            DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: workItem)

            lifetime.observeEnded {
                workItem.cancel()
            }
        }
        .take(during: $0.1)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        self.action <~ self.reactive.viewDidAppear.map(value: ((), self.reactive.lifetime))
    }
}

パターン4

SignalProducerActionの入力値にしてみる。

class ViewController: UIViewController {
    private let action = Action<SignalProducer<Void, Never>, Void, Never> {
        $0
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        self.action <~ self.reactive.viewDidAppear.compactMap { [weak self] in
            guard let self = self else { return nil }
            return SignalProducer { observer, lifetime in
                let workItem = DispatchWorkItem {
                    observer.send(value: ())
                    observer.sendCompleted()
                }

                DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: workItem)

                lifetime.observeEnded {
                    workItem.cancel()
                }
            }
            .take(during: self.reactive.lifetime)
        }
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Fire Storageを監視してサイレントプッシュ通知を送信

概要

Fire Storageに画像ファイルのアップロードがなされるとCloud Functionsを通してサイレントプッシュ通知を送信します。

今更感はありますが、ググってもpayloadの記述に統一性がなかったりして割と苦戦しました:sweat:

Stack OverFlowの方でも同じ悩みを抱えていた人がいたので参考になれば幸いです。

なお、本記事ではFirebase CLIや証明書周りについては触れません。
下準備ができている前提で進めます。

環境

  • Xcode 12.4
  • Swift 5
  • nodejs 12

Fire Storage

デフォルトバケットを使用

クライアント

Capability

TARGETS > CapabilitiesよりBackground ModesとPush Notificationsを有効にします。
Background ModesはRemote notificationsにチェックを入れます。
(バックグラウンドで通知を受け取りたい場合はBackground fetchもチェック)

Swift

今回はTopic購読で実装します。

AppDelegate.swift
import Firebase

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        FirebaseApp.configure()

        // 1. 通知許可
        let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
        UNUserNotificationCenter.current().requestAuthorization(
            options: authOptions,
            completionHandler: { (granted, error) in
                // todo
            })
        application.registerForRemoteNotifications()
        return true
    }

    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        // 2. Topic購読
        Messaging.messaging().subscribe(toTopic: "hoge") { _ in
            // todo
        }
    }

    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                     fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        // 3. 画像ファイルの名称を取り出す
        guard let imageName = userInfo["imageName"] as? String else {
            return
        }
        print("image name: \(imageName)")
    }
}

Cloud Functions

onFinalize関数でバケットに画像がアップロードされたことを検知します。

index.js
'use strict';

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

exports.sendSilentNotificationWithTopic = functions.storage.object().onFinalize((object) => {
  const topic = 'hoge';
  const payload = {
    data: {
      imageName: object.name, // 画像名をセット
    },
  };
  const options = {
    contentAvailable: true // サイレントプッシュはtrueにする
  };
  return admin.messaging().sendToTopic(topic, payload, options)
    .then(function(response) {
      return console.log("Successfully sent message:", response);
    })
    .catch(function(error) {
      return console.log("Error sending message:", error);
    });  
});

firebase deployを実行してデプロイします。

実行

  1. Xcodeに実機をつないでRun
  2. Fire Storageに画像ファイルを追加

Xcodeのコンソールログに画像名が表示されれば成功です。
自分はこれで成功したのですが、失敗したらすいません。。。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む