decochのブログ

フリーランスのiOSエンジニア decoch のブログです

CircleCIの定期実行を使って、毎日の作業を効率化しよう

目次

  • 効率化したかった作業
  • CircleCIの定期実行とは
  • 設定方法

効率化したかった作業

こちらのブログでまとめたが、日々のタスク管理とポモドーロをKanbanFlowでまとめていて、作業時間はTogglにまとめているので集計作業を毎日行う必要があった。 decoch.hatenablog.com

毎日ポモドーロの集計結果をTogglに入力するの大変なため、自動でスクリプトを実行して業務の効率化を行った。

CircleCIの定期実行とは

CircleCIでは2.0から定期実行をすることができるようになった。

詳しくは公式で紹介されている。

ジョブの実行を Workflow で制御する

設定方法

設定方法は非常に簡単で、以下のようにワークフローを1つ追加すれば実現できる。

workflows:
  version: 2
  xxx_workflow_name: # Workflow名
    triggers:
      - schedule:
          cron: "0 0 * * *" # UTCの日時
          filters:
            branches:
              only:
                - master
    jobs:
      - xxx_job_name  # 設定したJob名

振り返り

cronで実行するときは、自分のPCで設定をすると起動していないと実行されなかったり問題が発生するばCircleCIを使えば無料で確実にcron実行をできるので、テスト実行以外の用途でも使ってみると良いと思った。

f:id:decoch:20200708222424p:plain

理想のポモドーロ環境を手に入れたのでまとめてみた

はじめに

以前からポモドーロでタイムマネジメントをしていて、以前読んだSOFT SKILLSで紹介されていたKanbanFlowが理想的なアプリだったためずっと使っている。

ただ、KanbanFlow集計機能が弱く、振り返りのときになんとかならないかなーと思っていたところ、以前使っていたTogglの集計機能が非常によかったのを思い出して連携することはできないかと思いいたったのが今回の始まりである。

SOFT SKILLS ソフトウェア開発者の人生マニュアル

f:id:decoch:20200629235813j:plain
kanbanflow

利用するツール

今回はKanbanFlowとTogglを利用する。

2つのツールを比べると

KanbanFlow Toggl
タスク管理 ×
ポモドーロタイマー
作業時間の集計

KanbanFlowでタスク管理をし、集計結果をTogglに連携すると理想のPomodoro環境が作れるのではないかと考えた。

連携方法

日々のタスク管理にKanbanFlowを使い、振り返りにTogglを使うため、KanbanFlowのデータをAPIで取得し、Togglに保存できる形式に変換して保存する必要がある

スクリプトを書くとこんな感じで実装できた。

github.com

KanbanFlowのAPIドキュメントは設定ページから、 Togglはこちらで確認できる

github.com

定期実行するために

CircleCIでは2.0からworkflowsのトリガーとして、cronを指摘できるようになったため今回をCircleCIで定期実行を実現した。

workflows:
  cron:
    triggers:
      - schedule:
          cron: "0 0 * * *" # UTC
          filters:
            branches:
              only:
                - master
    jobs:
      - integration

https://github.com/decoch/kanbanflow2toggl/blob/master/.circleci/config.yml

終わりに

昨今ではいろいろなタスク管理アプリがあるのですが、いまいち自分に合うアプリに出会うことができなかったため、 2つのツールを連携してみたところ自分には非常にある環境を構築できた。 もしKanbanFlowやTogglを使っていて私と同じような課題を感じていたら作ったツールを使ってみてください。

fastlaneを使ったiOSアプリのCI/CDにおける最低限作っておいた方が良いワークフロー

目次

  • はじめに
  • CIのワークフロー
  • CDのワークフロー
  • DSYMのアップロード
  • 終わりに

はじめに

普段iOSアプリの開発をしておりGitHubActionとfastlaneを使ってCI/CD環境を作る機会があったため、そのときに最低限あった方が良いと思ったワークフローをまとめました。

f:id:decoch:20200618170146p:plain

※ 証明書周りの管理はそれだけで文量が多くなるので今回は割愛します。

CIのワークフロー

CIとは

CIとは、Continuous Integrationの略で、継続的インテグレーションと呼ばれています。 CI(継続的インテグレーション)では、開発者が自分のコード変更を頻繁にセントラルリポジトリにマージし、その度に自動化されたビルドとテストを実行します。

このときに主に必要なワークフローは以下の2つです

1 lint設定

lane: lint do
  swiftlint(strict: true)
end

swiftlintだけではCIが失敗しないため注意が必要です。
CIで静的解析をちゃんと行いたい場合は strict: true のオプションをつけましょう。
その他のオプション: https://docs.fastlane.tools/actions/swiftlint/

2 Test設定

lane: test do
  scan
end

その他オプション: https://docs.fastlane.tools/actions/scan/

途中から導入しようとすると修正が必要なところが出てきて無駄に時間がかかってしまうため、
CIは開発を始める前に作ることを強くおすすめします。

CDのワークフロー

CDとは

CDとは、Continuous Deliveryの略で、継続的デリバリーと呼ばれています。 継続的デリバリー(CD)は、継続的インテグレーション(CI)を拡張した手法で、ビルドやテストだけでなく、リリースプロセス全体を自動化します。

Stagingアプリの配布

lane :deploy_staging do
  gym
  # deploygate や firebase_app_distribution など
end

ビルドをし、できたipaファイルを Deploygate や Firebase App Distribution でテスターに配布をします

Productionアプリの配布

lane :deploy_production do
  gym
  deliver
end

ビルドをし、できたipaファイルを AppStoreConnectにアップロードします。

DSYMのアップロード

昨今のiOSアプリではクラッシュレポートとして、FirebaseCrashlyticsを使っていると思います。
その場合どこでクラッシュが起きたのかわかりやすくするために、AppStoreConnectにアップロードした後にFirebaseにdSYMファイルをアップロードする必要があります。

いままでプリの運用をしてきましたがアップロードは手動だと忘れがちなのでCIサービスを使って自動化をするべきです。

デプロイのフローに入れるとまだdSYMファイルがダウンロードできない場合があるため、CIサービスの設定などで1日1回定期実行すると失敗しなくなります。

lane :refresh_dsyms do |options|
  download_dsyms(
    app_identifier: 'xxxx',
    version: 'latest', 
    username: 'xxxx'
  )
  upload_symbols_to_crashlytics(
    gsp_path: './xxxx/GoogleService-Info.plist'
  )
  clean_build_artifacts
end

終わりに

最低限必要なワークフローとしては以下の5つがあるとよいです

  • Lint実行
  • テスト実行
  • Stagingアプリの配布
  • Productionアプリの配布
  • dSYMファイルのアップロード

これだけ設定するだけでも開発体験が大幅に改善します。

CIサービスには特に触れませんでしたが、iOSアプリではBitriseを使うと証明書のアップロードができたり、手順が丁寧にドキュメントにまとめられているのでおすすめです。 f:id:decoch:20200618170311p:plain

N予備校の大規模Webアプリコースを修了しました

目次

  • N予備校とは
  • なぜやったのか
  • 感想
  • 終わりに

N予備校とは

N予備校(エヌよびこう)はドワンゴが提供するeラーニングサービス(インターネット予備校)である。主に学校法人角川ドワンゴ学園が運営するN高等学校の生徒向けに提供されているが、主要なコースは一般向けにも提供されている。

※ 出典: N予備校 - Wikipedia

www.nnn.ed.nico

なぜやったのか

元々Scalaでのアプリケーション開発に興味があり、Scalaで有名なコップ本を読んだことはあったのですが実際にWebアプリを開発したことはなかったです。 そこにコロナウイルスの影響でN予備校で「Scalaを使った大規模Webアプリコース」が無料開放されていたので、これは良い機会だと思って始めてみました。(2020年4月6日現在)

感想

非常に良かったです。

流れとしては、Scalaの基礎から応用まで学んだのちに、Akkaを使った並行処理を学び、最後にPlayFrameworkを使ってWebアプリの開発をしました。 特にAkkaの章では、ライブラリをただ使うだけでなく、並行処理の基礎から説明してくれるのでいままで並行処理に触れてこなかった人は一度やってみると良いと思います。

ただWebpackの説明を省いていたり、他の言語との比較が出てくるので、今までまったくWebアプリケーションの開発をしたことがない人は最初に入門コースをやったほうが良いと思います。

HHKB (Happy Hacking Keyboard Professional HYBRID Type-S) を買ってみた

ワイヤレス接続できるキーボードを探していて、ネットで色々調べていたらHHKBにワイヤレスのものがあると知り思わず購入しました。

www.pfu.fujitsu.com

f:id:decoch:20200222150953p:plain

f:id:decoch:20200222151026p:plain キーボード

触ってみて

打ち心地は非常によいです。静電容量無接点方式は他のタイプのものと全然違いますね、売っていて気持ちが良いです。

入力音については、Type-S か普通のものにするか悩みましたが職場で使うことも考えてType-Sにしました。
入力した感じは Type-S も普通のものもあまり変わらないので、音が気になる人は Type-S にすると良いかと思います。 逆に音があった方が気持ち良いという人は通常のモデルが良いかとw(あくまで個人的な感想)

またワイヤレス接続については、HYBRIDだとケーブルなしで使えて HHKBが小さいので机の上が非常にスッキリします。
特にMac Book ユーザだとUSBケーブルをつなぐのに変換アダプターが必要なのでワイヤレスだと非常に助かりますよね。
接続もいまのところタイプしてからの遅延もなく快適に使えています。
お値段的に有線タイプのものにしようか悩みましたが、ワイヤレスにしてよかったと思います。

不満があるとすれば、電池式になるので充電が切れると電池交換する必要があることです。 普段電池式の製品をあまり使わないため充電が切れたときに新しく購入しにいくのがちょっと面倒だなと...

ちょっと高かったですが、総じて買ってよかったなと思います。

SwiftUIで書くiOSアプリハンズオン(QiitaClientを作ってみよう)

SwiftUIを使ったハンズオン用に記事を作成しました

使用するAPI

今回はQiitaAPiを題材とします。(ドキュメント)

登録不要で使えて、例えば、 https://qiita.com//api/v2/items で、投稿を取得できます。

[{
    rendered_body: " <h1> <span id="
    プロローグ " class="
    fragment "></span><a href="# % E3 % 83 % 97 % E3 % 83 % AD % E3 % 83 % AD % E3 % 83 % BC % E3 % 82 % B0 "><i class="
    fa fa - link "></i></a>プロローグ</h1> <p><a href="
    https: //camo.qiitausercontent.com/6c659e011f46abdb3a08bf3bac0a5d82418cbac3/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f31356433383165642d343436312d633235392d633831312d3062373163393133646364652e706e67" target="_blank" rel="nofollow noopener"><img src="https://camo.qiitausercontent.com/6c659e011f46abdb3a08bf3bac0a5d82418cbac3/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f31356433383165642d343436312d633235392d633831312d3062373163393133646364652e706e67" alt="20171109214610.png" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/76843/15d381ed-4461-c259-c811-0b71c913dcde.png"></a></p> <p>参照: <a href="http://r-dimension.xsrv.jp/classes_j/sine_cosine/" class="autolink" rel="nofollow noopener" target="_blank">http://r-dimension.xsrv.jp/classes_j/sine_cosine/</a></p> <p><strong>うげ〜。いやだこの図。。</strong>はい、こんにちは。。。<br> こちらの図見るとオエ〜ってなりますよね。僕はなります。<br> なのでお口直しに、小松菜奈さんの画像貼っときますわ。</p> <p><a href="https://camo.qiitausercontent.com/1ce1b5e53bb6405ccc06ed7fe0ce189cad70e816/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f64383133376461362d656337362d633435322d663637392d6266306130646566343164642e6a706567" target="_blank" rel="nofollow noopener"><img src="https://camo.qiitausercontent.com/1ce1b5e53bb6405ccc06ed7fe0ce189cad70e816/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f64383133376461362d656337362d633435322d663637392d6266306130646566343164642e6a706567" width="200" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/76843/d8137da6-ec76-c452-f679-bf0a0def41dd.jpeg"></a></p> <p>最近ネバヤン(バンド)に胸熱で。。お別れの歌のMVやばい。<br> <a href="https://www.youtube.com/watch?v=ZFI-Hqeu_Ag" class="autolink" rel="nofollow noopener" target="_blank">https://www.youtube.com/watch?v=ZFI-Hqeu_Ag</a></p> <h1> <span id="本題" class="fragment"></span><a href="#%E6%9C%AC%E9%A1%8C"><i class="fa fa-link"></i></a>本題</h1> <p><strong>円運動とかゆらゆら上下運動とかってsin()とかcos()が必要じゃないですか?<br> メディアアート系のプログラミングって。oFとかprocessingとか、Unityも?。</strong></p> <p>なのでなるべく優し〜く入門勉強してきたいと思います。</p> <p>参考サイト<br> <a href="http://yoppa.org/ma2_10/1739.html" class="autolink" rel="nofollow noopener" target="_blank">http://yoppa.org/ma2_10/1739.html</a><br> <a href="http://r-dimension.xsrv.jp/classes_j/sine_cosine/" class="autolink" rel="nofollow noopener" target="_blank">http://r-dimension.xsrv.jp/classes_j/sine_cosine/</a></p> <h1> <span id="入門します" class="fragment"></span><a href="#%E5%85%A5%E9%96%80%E3%81%97%E3%81%BE%E3%81%99"><i class="fa fa-link"></i></a>入門します!</h1> <blockquote> <p>さて、プログラムによって図形を描く場合に避けて通れないのが三角関数(サイン、コサイン)です。<br> 円などに代表される曲線を描く際によく使われます。<br> タンジェントは使用頻度が低いので、ここでは省略します。</p> </blockquote> <p>だそうです。。。</p> <p>まず先ほどの図。<br> 大丈夫だから、ちょっとみてみてください。</p> <p><a href="https://camo.qiitausercontent.com/6c659e011f46abdb3a08bf3bac0a5d82418cbac3/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f31356433383165642d343436312d633235392d633831312d3062373163393133646364652e706e67" target="_blank" rel="nofollow noopener"><img src="https://camo.qiitausercontent.com/6c659e011f46abdb3a08bf3bac0a5d82418cbac3/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f31356433383165642d343436312d633235392d633831312d3062373163393133646364652e706e67" alt="20171109214610.png" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/76843/15d381ed-4461-c259-c811-0b71c913dcde.png"></a></p> <p>これを勇気を出して、単純に読み解くと、</p> <h2> <span id="r--円の半径" class="fragment"></span><a href="#r--%E5%86%86%E3%81%AE%E5%8D%8A%E5%BE%84"><i class="fa fa-link"></i></a>R → 円の半径</h2> <h2> <span id="x--y-円上の座標" class="fragment"></span><a href="#x--y-%E5%86%86%E4%B8%8A%E3%81%AE%E5%BA%A7%E6%A8%99"><i class="fa fa-link"></i></a>(x , y) →円上の座標</h2> <h2> <span id="θ--角度" class="fragment"></span><a href="#%CE%B8--%E8%A7%92%E5%BA%A6"><i class="fa fa-link"></i></a>θ → 角度</h2> <p>らしいです。</p> <p><a href="https://camo.qiitausercontent.com/67043a3190bfd14f3ed76443dc06bafe7c52a117/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f31343937306263642d303762632d656361322d306138632d3730363130393662323235372e6a706567" target="_blank" rel="nofollow noopener"><img src="https://camo.qiitausercontent.com/67043a3190bfd14f3ed76443dc06bafe7c52a117/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f31343937306263642d303762632d656361322d306138632d3730363130393662323235372e6a706567" alt="20171109214219.jpg" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/76843/14970bcd-07bc-eca2-0a8c-7061096b2257.jpeg"></a></p> <p>雰囲気がある。いい。<br> 溺れるナイフは漫画最高だったけど映画最悪だったなー笑</p> <p>そして(x,y)の座標の求め方は、</p> <h2> <span id="x--r--cosθ" class="fragment"></span><a href="#x--r--cos%CE%B8"><i class="fa fa-link"></i></a>x = R * cos(θ)</h2> <h2> <span id="y--r--sinθ" class="fragment"></span><a href="#y--r--sin%CE%B8"><i class="fa fa-link"></i></a>y = R * sin(θ)</h2> <p>でOKだそうです。お、意外といけるのでは?<br> ではこの式を使ってoFで円を描いてみます。<br> まぁofDrawCircle()で描けるのですが、、、勉強に。</p> <p>できたぞ〜!!!!!<br> <a href="https://camo.qiitausercontent.com/af4ce1ca438fff2abb9967032271b63454ecd513/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f36316235636230612d346234642d326533322d353264342d3938303938663566303362622e706e67" target="_blank" rel="nofollow noopener"><img src="https://camo.qiitausercontent.com/af4ce1ca438fff2abb9967032271b63454ecd513/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f36316235636230612d346234642d326533322d353264342d3938303938663566303362622e706e67" alt="スクリーンショット 2017-11-09 22.37.08.png" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/76843/61b5cb0a-4b4d-2e32-52d4-98098f5f03bb.png"></a></p> <p>動画<br> <a href="https://www.youtube.com/watch?v=JTVPKTFheS8" class="autolink" rel="nofollow noopener" target="_blank">https://www.youtube.com/watch?v=JTVPKTFheS8</a></p> <p>コード</p> <div class="code-frame" data-lang="cpp"> <div class="code-lang"><span class="bold">ofApp.cpp</span></div> <div class="highlight"><pre><span></span> <span class="cp">#include</span> <span class="cpf">"ofApp.h"</span><span class="cp"></span> <span class="kt">float</span> <span class="n">phase</span><span class="p">;</span> <span class="c1">// 段階の意味</span> <span class="k">const</span> <span class="kt">float</span> <span class="n">R</span> <span class="o">=</span> <span class="mi">200</span><span class="p">;</span> <span class="c1">// 円の半径</span> <span class="c1">//--------------------------------------------------------------</span> <span class="kt">void</span> <span class="n">ofApp</span><span class="o">::</span><span class="n">setup</span><span class="p">(){</span> <span class="n">ofSetFrameRate</span><span class="p">(</span><span class="mi">60</span><span class="p">);</span> <span class="n">ofSetBackgroundAuto</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span> <span class="c1">// 毎回背景を塗りつぶしてリセットしない!</span> <span class="n">ofBackground</span><span class="p">(</span><span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">);</span> <span class="p">}</span> <span class="c1">//--------------------------------------------------------------</span> <span class="kt">void</span> <span class="n">ofApp</span><span class="o">::</span><span class="n">update</span><span class="p">(){</span> <span class="c1">// 段階を更新</span> <span class="n">phase</span> <span class="o">+=</span> <span class="mf">0.01</span><span class="p">;</span> <span class="p">}</span> <span class="c1">//--------------------------------------------------------------</span> <span class="kt">void</span> <span class="n">ofApp</span><span class="o">::</span><span class="n">draw</span><span class="p">(){</span> <span class="c1">//原点を画面の中心点に</span> <span class="n">ofTranslate</span><span class="p">(</span><span class="n">ofGetWidth</span><span class="p">()</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="n">ofGetHeight</span><span class="p">()</span><span class="o">/</span><span class="mi">2</span><span class="p">);</span> <span class="c1">//点の座標を三角関数で計算するぞ x = R * cos(θ) y = R * sin(θ)</span> <span class="n">ofVec2f</span> <span class="n">pos</span><span class="p">;</span> <span class="c1">// 円の位置</span> <span class="n">pos</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">R</span> <span class="o">*</span> <span class="n">cos</span><span class="p">(</span><span class="n">phase</span><span class="p">);</span> <span class="n">pos</span><span class="p">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">R</span> <span class="o">*</span> <span class="n">sin</span><span class="p">(</span><span class="n">phase</span><span class="p">);</span> <span class="c1">//色を黄色に</span> <span class="n">ofSetColor</span><span class="p">(</span><span class="mi">256</span><span class="p">,</span><span class="mi">256</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="c1">// 4ポイントの円を描画</span> <span class="n">ofDrawCircle</span><span class="p">(</span><span class="n">pos</span><span class="p">.</span><span class="n">x</span><span class="p">,</span> <span class="n">pos</span><span class="p">.</span><span class="n">y</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span> <span class="p">}</span> </pre></div> </div> <p><strong>どうやら 先ほどの公式のcos(θ)のθにあたる変数phase(段階)をupdateで増やしていくのがキモみたいっすね。</strong></p> <p>これに時間経過とか入れてやればいいのかな<br> そんくらいの理解でいいかな。。</p> <h1> <span id="浅くてすんません" class="fragment"></span><a href="#%E6%B5%85%E3%81%8F%E3%81%A6%E3%81%99%E3%82%93%E3%81%BE%E3%81%9B%E3%82%93"><i class="fa fa-link"></i></a>浅くてすんません。。</h1> <p>次は、</p> <h2> <span id="y--r--sinθ-1" class="fragment"></span><a href="#y--r--sin%CE%B8-1"><i class="fa fa-link"></i></a>y = R * sin(θ)</h2> <p>の公式を取得したので上下ゆらゆらやってみます!!</p> <p>おっできた</p> <p><a href="https://camo.qiitausercontent.com/8a931fa630600b58c095bbc10aae74e23045e908/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f64373435653365632d633033382d303038662d653062322d6430366564386234336330642e706e67" target="_blank" rel="nofollow noopener"><img src="https://camo.qiitausercontent.com/8a931fa630600b58c095bbc10aae74e23045e908/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37363834332f64373435653365632d633033382d303038662d653062322d6430366564386234336330642e706e67" alt="スクリーンショット 2017-11-09 22.43.45.png" data-canonical-src="https://qiita-image-store.s3.amazonaws.com/0/76843/d745e3ec-c038-008f-e0b2-d06ed8b43c0d.png"></a></p> <p>動画<br> <a href="https://www.youtube.com/watch?v=pQptfrukBRE" class="autolink" rel="nofollow noopener" target="_blank">https://www.youtube.com/watch?v=pQptfrukBRE</a></p> <p><strong>コードはこちら。今回はRがゆらゆらさせる範囲で、変数phase(段階)の増える値がゆらゆらする速度になってるんですね。</strong></p> <div class="code-frame" data-lang="cpp"> <div class="code-lang"><span class="bold">ofApp.cpp</span></div> <div class="highlight"><pre><span></span> <span class="kt">float</span> <span class="n">phase</span><span class="p">;</span> <span class="c1">// 段階の意味</span> <span class="k">const</span> <span class="kt">float</span> <span class="n">R</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span> <span class="c1">//ゆらゆらさせる範囲</span> <span class="c1">//--------------------------------------------------------------</span> <span class="kt">void</span> <span class="n">ofApp</span><span class="o">::</span><span class="n">setup</span><span class="p">(){</span> <span class="n">ofSetFrameRate</span><span class="p">(</span><span class="mi">60</span><span class="p">);</span> <span class="n">ofBackground</span><span class="p">(</span><span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">);</span> <span class="p">}</span> <span class="c1">//--------------------------------------------------------------</span> <span class="kt">void</span> <span class="n">ofApp</span><span class="o">::</span><span class="n">update</span><span class="p">(){</span> <span class="c1">// 段階を更新</span> <span class="n">phase</span> <span class="o">+=</span> <span class="mf">0.01</span><span class="p">;</span> <span class="p">}</span> <span class="c1">//--------------------------------------------------------------</span> <span class="kt">void</span> <span class="n">ofApp</span><span class="o">::</span><span class="n">draw</span><span class="p">(){</span> <span class="c1">//原点を画面の中心点に</span> <span class="n">ofTranslate</span><span class="p">(</span><span class="n">ofGetWidth</span><span class="p">()</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="n">ofGetHeight</span><span class="p">()</span><span class="o">/</span><span class="mi">2</span><span class="p">);</span> <span class="n">ofVec2f</span> <span class="n">pos</span><span class="p">;</span> <span class="n">pos</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// 左右は運動なし</span> <span class="n">pos</span><span class="p">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">R</span> <span class="o">*</span> <span class="n">sin</span><span class="p">(</span><span class="n">phase</span><span class="p">);</span> <span class="c1">// 上下ゆらゆら</span> <span class="c1">//色を黄色に</span> <span class="n">ofSetColor</span><span class="p">(</span><span class="mi">256</span><span class="p">,</span><span class="mi">256</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="c1">// 30ポイントの円を描画</span> <span class="n">ofDrawCircle</span><span class="p">(</span><span class="n">pos</span><span class="p">.</span><span class="n">x</span><span class="p">,</span> <span class="n">pos</span><span class="p">.</span><span class="n">y</span><span class="p">,</span> <span class="mi">30</span><span class="p">);</span> <span class="p">}</span> </pre></div> </div> <h2> <span id="x--r--cosθ-1" class="fragment"></span><a href="#x--r--cos%CE%B8-1"><i class="fa fa-link"></i></a>x = R * cos(θ)</h2> <p>を使えば左右ゆらゆらも表現できると思います^^。<br> ちょっとは三角関数と仲良くなれた気がします!<br> 今回で基礎をかじった気がするから、sin波も学んでみたいです。</p> <p>完。</p> <p>本家のブログはこちら 超雑多です。w🍺<br> ホンキートンク・スーダラブルース<br> <a href="http://www.sudara-bluse.tokyo/entry/openframeworks_5" class="autolink" rel="nofollow noopener" target="_blank">http://www.sudara-bluse.tokyo/entry/openframeworks_5</a></p> ",
        body: " #プロローグ ![20171109214610.png](https://qiita-image-store.s3.amazonaws.com/0/76843/15d381ed-4461-c259-c811-0b71c913dcde.png) 参照: http://r-dimension.xsrv.jp/classes_j/sine_cosine/ **うげ〜。いやだこの図。。**はい、こんにちは。。。 こちらの図見るとオエ〜ってなりますよね。僕はなります。 なのでお口直しに、小松菜奈さんの画像貼っときますわ。 <img src="
    https: //qiita-image-store.s3.amazonaws.com/0/76843/d8137da6-ec76-c452-f679-bf0a0def41dd.jpeg" width="200"> 最近ネバヤン(バンド)に胸熱で。。お別れの歌のMVやばい。 https://www.youtube.com/watch?v=ZFI-Hqeu_Ag #本題 **円運動とかゆらゆら上下運動とかってsin()とかcos()が必要じゃないですか? メディアアート系のプログラミングって。oFとかprocessingとか、Unityも?。** なのでなるべく優し〜く入門勉強してきたいと思います。 参考サイト http://yoppa.org/ma2_10/1739.html http://r-dimension.xsrv.jp/classes_j/sine_cosine/ #入門します! >さて、プログラムによって図形を描く場合に避けて通れないのが三角関数(サイン、コサイン)です。 円などに代表される曲線を描く際によく使われます。 タンジェントは使用頻度が低いので、ここでは省略します。 だそうです。。。 まず先ほどの図。 大丈夫だから、ちょっとみてみてください。 ![20171109214610.png](https://qiita-image-store.s3.amazonaws.com/0/76843/15d381ed-4461-c259-c811-0b71c913dcde.png) これを勇気を出して、単純に読み解くと、 ##R → 円の半径 ##(x , y) →円上の座標 ##θ → 角度 らしいです。 ![20171109214219.jpg](https://qiita-image-store.s3.amazonaws.com/0/76843/14970bcd-07bc-eca2-0a8c-7061096b2257.jpeg) 雰囲気がある。いい。 溺れるナイフは漫画最高だったけど映画最悪だったなー笑 そして(x,y)の座標の求め方は、 ## x = R * cos(θ) ## y = R * sin(θ) でOKだそうです。お、意外といけるのでは? ではこの式を使ってoFで円を描いてみます。 まぁofDrawCircle()で描けるのですが、、、勉強に。 できたぞ〜!!!!! ![スクリーンショット 2017-11-09 22.37.08.png](https://qiita-image-store.s3.amazonaws.com/0/76843/61b5cb0a-4b4d-2e32-52d4-98098f5f03bb.png) 動画 https://www.youtube.com/watch?v=JTVPKTFheS8 コード ```cpp:ofApp.cpp #include "ofApp.h" float phase; // 段階の意味 const float R = 200; // 円の半径 //-------------------------------------------------------------- void ofApp::setup(){ ofSetFrameRate(60); ofSetBackgroundAuto(false); // 毎回背景を塗りつぶしてリセットしない! ofBackground(255, 255, 255); } //-------------------------------------------------------------- void ofApp::update(){ // 段階を更新 phase += 0.01; } //-------------------------------------------------------------- void ofApp::draw(){ //原点を画面の中心点に ofTranslate(ofGetWidth()/2, ofGetHeight()/2); //点の座標を三角関数で計算するぞ x = R * cos(θ) y = R * sin(θ) ofVec2f pos; // 円の位置 pos.x = R * cos(phase); pos.y = R * sin(phase); //色を黄色に ofSetColor(256,256, 0); // 4ポイントの円を描画 ofDrawCircle(pos.x, pos.y, 4); } ``` **どうやら 先ほどの公式のcos(θ)のθにあたる変数phase(段階)をupdateで増やしていくのがキモみたいっすね。** これに時間経過とか入れてやればいいのかな そんくらいの理解でいいかな。。 #浅くてすんません。。 次は、 ## y = R * sin(θ) の公式を取得したので上下ゆらゆらやってみます!! おっできた ![スクリーンショット 2017-11-09 22.43.45.png](https://qiita-image-store.s3.amazonaws.com/0/76843/d745e3ec-c038-008f-e0b2-d06ed8b43c0d.png) 動画 https://www.youtube.com/watch?v=pQptfrukBRE **コードはこちら。今回はRがゆらゆらさせる範囲で、変数phase(段階)の増える値がゆらゆらする速度になってるんですね。** ```cpp:ofApp.cpp float phase; // 段階の意味 const float R = 100; //ゆらゆらさせる範囲 //-------------------------------------------------------------- void ofApp::setup(){ ofSetFrameRate(60); ofBackground(255, 255, 255); } //-------------------------------------------------------------- void ofApp::update(){ // 段階を更新 phase += 0.01; } //-------------------------------------------------------------- void ofApp::draw(){ //原点を画面の中心点に ofTranslate(ofGetWidth()/2, ofGetHeight()/2); ofVec2f pos; pos.x = 0; // 左右は運動なし pos.y = R * sin(phase); // 上下ゆらゆら //色を黄色に ofSetColor(256,256, 0); // 30ポイントの円を描画 ofDrawCircle(pos.x, pos.y, 30); } ``` ## x = R * cos(θ) を使えば左右ゆらゆらも表現できると思います^^。 ちょっとは三角関数と仲良くなれた気がします! 今回で基礎をかじった気がするから、sin波も学んでみたいです。 完。 本家のブログはこちら 超雑多です。w🍺 ホンキートンク・スーダラブルース http://www.sudara-bluse.tokyo/entry/openframeworks_5 ",
        coediting: false,
    comments_count: 0,
    created_at: "2017-11-09T22:50:51+09:00",
    group: null,
    id: "fe412a67b64e793d138b",
    likes_count: 0,
    private: false,
    reactions_count: 0,
    tags: [{
            name: "processing",
            versions: []
        },
        {
            name: "openFrameworks",
            versions: []
        }
    ],
    title: "【openFrameworks 初心者冒険記5】逃げまくってた...三角関数の扉を再ノック。sin() cos() サインコサイン....。メディアアートで避けては通れない?最初の壁",
    updated_at: "2017-11-09T22:50:51+09:00",
    url: "http://qiita.com/39_isao/items/fe412a67b64e793d138b",
    user: {
        description: "鮭とメロンパンが好物な奥田民生になりたいボーイです。 最近はてなブログ始めました http://sudara-bluse.hatenablog.com/",
        facebook_id: "",
        followees_count: 52,
        followers_count: 44,
        github_login_name: null,
        id: "39_isao",
        items_count: 103,
        linkedin_id: "",
        location: "",
        name: "",
        organization: "",
        permanent_id: 76843,
        profile_image_url: "https://qiita-image-store.s3.amazonaws.com/0/76843/profile-images/1508030106",
        twitter_screen_name: "sudara_bluse",
        website_url: "http://sudara-bluse.hatenablog.com/"
    }
}, 
{,,,}
]

今回は、以下のデータを使ってiOSアプリを作ってみます。

  • 投稿タイトル
  • ユーザアイコン
  • URL

完成したアプリ

f:id:decoch:20200221170444g:plain

プロジェクトを作成する

f:id:decoch:20200221164044p:plain f:id:decoch:20200221164048p:plain f:id:decoch:20200221164052p:plain

使うライブラリをインストールする

file -> swift package から使用するライブラリを追加します https://github.com/dmytro-anokhin/url-image.git

f:id:decoch:20200221164505p:plain f:id:decoch:20200221164509p:plain

通信周りのクラスを作成する

JSONを変換するための構造体を作成

import Foundation

struct Item: Decodable, Identifiable {
    let id: String
    let title: String
    let body: String?
    let url: String
    let user: User
}

struct User: Decodable {
    let profileImageUrl: String
}

APIからデータを取得するクラスを実装

SwiftUIの画面から参照するためObservableObject を継承し、画面に表示する値を @Published にする

import Foundation

class ApiFetcher: ObservableObject {
    @Published var items = [Item]()
    
    private let baseURL = "https://qiita.com/api/v2"
    
    func fetchItems(query: String) {
        let url = URL(string: "\(baseURL)/items?query=\(query)&page=1&per_page=50")!
    
        URLSession.shared.dataTask(with: url) {(data,response,error) in
            do {
                if let data = data {
                    let decoder = JSONDecoder()
                    decoder.keyDecodingStrategy = .convertFromSnakeCase
                    let items = try decoder.decode([Item].self, from: data)
                    DispatchQueue.main.async {
                        self.items = items
                    }
                }
            } catch {
                // ignore
            }
            
        }.resume()
    }
}

一覧画面を作る

一覧のセルを作成する

新しく ItemRow.swift というファイルを作成します

f:id:decoch:20200221165232p:plain

import SwiftUI
import URLImage

struct ItemRow: View {
    let item: Item
    
    var body: some View {
        HStack {
            URLImage(URL(string: item.user.profileImageUrl)!) { proxy in
                proxy.image
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .clipped()
            }.frame(width: 60.0, height: 60.0)

            Text(item.title)
        }
    }
}

struct ItemRow_Previews: PreviewProvider {
    static var previews: some View {
        ItemRow(item: Item(
            id: "d040cf8b2d15bd7e507d",
            title: "[Angular] Angular アプリの構成をみる",
            body: "Angular に関する自身の勉強の復習がてらの備忘録記事。",
            url: "https://qiita.com/ksh-fthr/items/d040cf8b2d15bd7e507d",
            user: User(
                profileImageUrl: "https://qiita-user-profile-images.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F193342%2Fprofile-images%2F1500888159?ixlib=rb-1.2.2&auto=compress%2Cformat&lossless=0&w=300&s=9a22e880d804d67db66a33ac2e7671b5"
            )
        ))
    }
}

一覧画面を作成する

新しく ItemRow.swift というファイルを作成します

import SwiftUI

struct ItemList: View {
    @ObservedObject var fetcher = ApiFetcher()
    @State var keyword: String = ""
    
    var body: some View {
        NavigationView {
            VStack {
                TextField("Search", text: $keyword) {
                    self.fetcher.fetchItems(query: self.keyword)
                }.padding(16)
                
                List(fetcher.items) { item in
                    ItemRow(item: item)
                }
            }
            .navigationBarTitle(Text("投稿一覧"))
        }
    }
}

struct ItemList_Previews: PreviewProvider {
    static var previews: some View {
        ItemList()
    }
}

ここまで作成するとプレビューが以下のように表示されます

f:id:decoch:20200221165533p:plain

起動画面を一覧画面にする

もともと作成されている ContentView を修正して一覧画面を表示するようにします

struct ContentView: View {
    var body: some View {
        ItemList()
    }
}

起動する

ここまで作ったら一度起動して入力した文字で検索できることを確認してみましょう

詳細画面を作る

詳細画面は一覧でタップした投稿をWebViewで表示します 新しくItemDetail.swiftを作成します

import SwiftUI
import WebKit

struct ItemDetail: View {
    let title: String
    let url: String

    var body: some View {
        WebView(url: URL(string: url)!)
        .navigationBarTitle(Text(title), displayMode: .inline)
    }
}

struct WebView : UIViewRepresentable {
    var url: URL

    func makeUIView(context: Context) -> WKWebView  {
        return WKWebView(frame: .zero)
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        let req = URLRequest(url: url)
        uiView.load(req)
    }
}

struct ItemDetail_Previews: PreviewProvider {
    static var previews: some View {
        ItemDetail(title: "テスト", url: "https://www.apple.com")
    }
}

一覧から詳細画面に遷移できるようにする

ItemList.swift の List を以下のように修正して画面遷移できるようにします

List(fetcher.items) { item in
    ItemRow(item: item)
}

List(fetcher.items) { item in
    NavigationLink(
        destination: ItemDetail(
            title: item.title,
            url: item.url
        )
    ) {
        ItemRow(item: item)
    }
}

実行してみましょう

最後に

完成はこちらにあります。 github.com

おつかれさまでした。

Roost Laptop Stand を購入してみた

目次

  • Roost Laptop Stand とは
  • なんで買ったのか
  • 買ってよかったこと

Roost Laptop Stand とは

超軽量で簡単に持ち運びできるノートパソコン用スタンドで、ノートパソコンの高さを目の高さまで上昇させることができます。
仕事やコンピュータ使用が原因の負傷を防ぐために、大きな助けとなってくれます。
ちなみにすべてのApple製ノートパソコンに対応。

www.therooststand.com

f:id:decoch:20200222175237p:plain 畳んだとき

f:id:decoch:20200222175307p:plain Mac Book Pro 13inch とのサイズ比較

f:id:decoch:20200222175344p:plain 横から

f:id:decoch:20200222175415p:plain 正面から

f:id:decoch:20200222175449p:plain Mac Book Pro を乗せたら

なんで買ったのか

元々外付けのディスプレイを使っていたのですが、引っ越しの際に捨ててしまったため家ではノートPCのまま作業をしていました。
長い時間ノートPC作業しているときの姿勢が気になったのと、最近肩こりがひどくなってきたので購入をしました

買ってよかったこと

最初は半信半疑でしたが、実際に使ってみると外付けのディスプレイを使っているときのように猫背にならず良い姿勢で作業をすることができます。 長時間作業していても疲れなくなりました。肩こりも改善した気がします。

外付けのディスプレイがある人は必要がないと思いますが、普段ノートPCで作業をしていて姿勢が気になるという人は購入してみると良いと思います。

まだカフェで使うのは気が引けますが、軽くて持ち運びも楽なのでそのうちチャレンジしてみたいと思っていますw