SHOWROOM Arena(β)お披露目+Arenaが目指したいもの

こんにちは。サーバーサイドエンジニアの齋藤です。
先日プレスリリースが出て、実際のライブで使用されたSHOWROOM Arena(β)について今日はお話ししたいと思います。

prtimes.jp

そもそもSHOWROOM Arena(β)とは

SHOWROOM Arena(β)がどういったものかというと
配信を視聴している皆様の映像を集めて
ステージでパフォーマンスをしているアーティストの元に届ける

ためのものです。
発想自体は特に新しいものでもなく、Web会議システムを使っての試みがテレビ番組や無観客ライブ配信などで既に行われています。

これを配信プラットフォーム1つで、ライブ会場で実現したい、と考えました。
以前の記事にも書きましたが、SHOWROOMは「仮想ライブ空間」です。
ステージがあり、観客がいるのがSHOWROOMです。
アーティストが観客を感じられなくなったのであれば、仮想ライブ空間がその代わりを担えばいい。
そこをテクノロジーで解決するのがエンタメテックカンパニーでSHOWROOMの役目だと思っています。

実際のライブでの利用風景

開発的な話は後ほどにするとして
先日実際にSHOWROOM Arena(β)を使用したライブの様子がこちらです。

Zepp Hanedaで 9/11 9/12に行われたHYDEさんの「HYDE LIVE 2020 Jekyll & Hyde」で採用されました!

「HYDE LIVE 2020 Jekyll & Hyde」9月11日公演
「HYDE LIVE 2020 Jekyll & Hyde」9月11日公演の様子1

「HYDE LIVE 2020 Jekyll & Hyde」9月11日公演
「HYDE LIVE 2020 Jekyll & Hyde」9月11日公演の様子2

実際のライブの様子は、ナタリーさんが初日のレポートも書いていただているのでそちらを貼っておきます。

natalie.mu

「Arena」という名称は、SHOWROOM上にアリーナ席を作るつもりで命名しました。
スタンド席があって、アリーナ席があって、SHOWROOMアリーナ席がある。目指した世界観はそこにあります。 当事者なので贔屓目もかなりあるとは思いますが、SHOWROOMアリーナ席の皆様はとても楽しそうにライブに参加されているように見えました。 その光景が見れただけで、まずはこの試みは成功に終わったのではないかと思っています。

もちろん全てがうまく行ったわけではなく、改善点も多く出ました。
それは今後に向けて解決していかなければならないと思っています。

さて、ライブの振り返りはこれくらいにしておいて、開発に関わるお話に移ろうと思います。

プロジェクト立ち上げの経緯

以前プレミアムライブ機能をリリースした際にこんなブログを書きました。

tech.showroom.co.jp

この記事の最後に書いた内容がこちら。

観客席からの熱量を配信者の方に返すことが
これでは実現できていません。
双方向性が用意できてこそSHOWROOMです。
もしも無観客でのライブだったとしても、
配信で見ている人たちの想いが届けられるとしたら、
それはきっと素晴らしいことだと思うのです。
次はそこを目指したいと思います。

SHOWROOM Arena(β)は、この時の想いを具体化すべく立ち上げました。 本当の理想形を実現するにはまだまだ先は長いですが、この時思い描いていた世界に一歩近づけたのではないか、と思います。

開発のポイント

今回開発する上で一番のポイントになったのは
今までSHOWROOMがやってきたことをこんな感じで応用したらできるのでは?
という気づきがいくつもあったことだと思います。

アバターとコメントとギフトで作られた仮想ライブ空間があって、
どうしたらその空間をもっと楽しい場所にできるのか、ずっと試行錯誤してきました。
今まで積み重ねてきた経験をちょっと延長させてみたら、
その上にArenaの仕組みが乗っかってきたような、そんな感じがします。
SHOWROOM Arena(β)は2020年9月11日がローンチ日となりましたが
今までの積み重ねと優秀なエンジニアの腕によって、無事この日を迎えることができたと思います。

SHOWROOM Arena(β)配信システム

実はこの仕組み、今までの配信とは関わる人の数が全く異なります。
今までは配信する側がいて、そこから送られてきた動画が視聴者に届けばOKでした。
今回は視聴者側も映像を送る必要がありますし、それを配信している側で受ける必要があります。

簡単な図で表すとこんな感じになります。(これとは別に、会場自体の映像をSHOWROOMの映像配信サーバ経由でSHOWROOM配信しています)

f:id:saitoryc:20200914185915p:plain
SHOWROOM Arena(β)イメージ図

SHOWROOM Arena(β)配信運用 

このシステムは通常のライブ配信と違い、配信者のライブ会場側に大量の映像ストリーム(ファンの皆様の映像)の受信が発生します。

そのため

  • サーバサイド:ライブ会場の回線限界や受信PCの処理負荷などを避けるために、様々な処理をサーバサイドで行いました。

  • 配信技術チーム: 配信状況のチェックや現場での見え方、ライブ自体の演出との整合性、モニタ配置や調整などを行いました。

  • クライアントサイド: ファンの皆様のPCから迷わずにSHOWROOM Arenaに参加出来て、会場に熱気を届けられるようなUI設計、映像送信部分を新規で開発しました。

このサーバサイド、クライアントサイド、配信技術チームそれぞれのチームワーク無しにはなかなか成功できなかったはずです。

そして、この新しい試みに対して お茶の間からライブを一緒に作り上げてくれたHYDEさんのファンの方々が居てこそ、成立したプロジェクト だと思っております(SNS上で連携して盛り上げている様子を見ていて、開発チームは深く感動しました。茶の間コールレスポンス最高でした!)

配信実験を行ったりアーキテクチャ図を起こすまでは、多くの人が想像できるとおりですが、実際のライブ会場からの配信でこれを実現しようとすると、上記のように様々なパワーが必要になります。

こうして 配信実験で終わらせずに、実際に本番運用まで実現できた ことを、とても嬉しく思います。

SHOWROOM Arena(β)今後の展望

テックブログのくせにほとんど技術の話を書いてませんが
偶然のようにいろんな点が線になって出来上がり、チームワークによって本番運用を成功させた、本当にそう感じてます。

こうして無事デビューを迎えたSHOWROOM Arena(β)。
「(β)」がついている通りまだ発展途上の機能です。
まだまだ追加したい機能はたくさんあります。

コロナ禍は未だ完全な終息の目処は立っていません。
「新しい生活様式」という言葉もすっかり馴染みのあるものになりました。
「早く収まってほしい」と指をくわえて待っているだけではなく
よりエンターテイメントにあふれた仮想ライブ空間を作るために
SHOWROOMはさらなる挑戦を続けるつもりです。

そんなSHOWROOMでは引き続きエンタメに熱くなれる人 を募集してます。
ご興味ある方はぜひ!
https://recruit.showroom.co.jp/

プレミアムライブ機能をリリースしました

こんにちは。サーバーサイドエンジニアの齋藤です。
このたびプレミアムライブ機能
(チケット購入者のみ視聴可能な全画面配信機能)
をリリースしたので、開発に関わる話を書こうと思います。

prtimes.jp

なお、プレミアムライブ機能自体は
SHOWROOM単体の機能なので
バーチャル配信だけでなく
リアルライブの配信もご利用いただけます。

開発に至った経緯

僕はもともとエンタメ大好き人間なので
週末はほとんどスポーツ観戦とライブ参戦で埋まっています。
そんな僕の楽しい週末の予定たちは
2020年2月の終盤に差し掛かると
ほぼ全てが中止となってしまいました。

エンターテイメントとは娯楽です。
緊急事態での優先順位が低くなることは仕方ない。
でも週末の予定が全てなくなって思ったのは
もしこの世界からエンタメが消えたなら
こんなにも味がしない毎日が続くんだな、ということでした。

SHOWROOMはエンタメテックカンパニーです。
技術を持ってエンターテイメントを切り開くための集まりです。
僕らにしかやれないことがあるのではないか、
そういう思いからすぐにプレミアムライブの構想を立ち上げ、プロト版の開発に着手しました。

実現したかったこと

視聴者向け

SHOWROOMの配信画面は
「仮想ライブ空間」をイメージしています。
そのため配信の動画だけではなく
ステージがあり、観客がいます。

この「仮想ライブ空間」を使って
中止になったライブを配信で楽しめるようにしたい。
数ある動画配信プラットフォームの中で
「ライブ空間をインターネット上で再現しよう」
というコンセプトを持っているのはSHOWROOMだけだと思います。

視聴者には、まるでライブを現場で見てるような楽しみ方を提供したい。
そのために必要だと考えたのが
・フルスクリーン視聴
・高画質化
の2点です。

仮想ライブ空間のコンセプトは大事にしたいものの
臨場感のあるライブ視聴のためには
どうしても画面の小ささがネックになってしまう。
そこでプレミアムライブの機能としては
まずは大きな画面で見れるということを最優先にし、
フルスクリーンモードを搭載することにしました。

また、フルスクリーンで視聴する場合に気になるのが画質です。
さすがに従来と同等の画質では
十分な満足度は得られないと判断し
高画質配信・視聴が可能なサーバーを用意しました。
スマートフォンからの配信は従来どおりですが
パソコンを用いた配信であれば
HD画質で配信した映像をそのままのクオリティで
視聴者にお届けすることができるようになりました。

ちなみにありきたりではありますが
フルスクリーンモードと通常モードの切り替え方法は
動画右下のボタンによって行うようにしてます。

f:id:saitoryc:20200323195945p:plain
フルスクリーンモード切り替えイメージ

配信者向け

ライブとは本来有料で行われるものです。
チケットやグッズの売上は大事な収入源です。
それが中止になり続けてしまうことで
そもそもの活動継続が困難になってしまいます。
「今耐えていれば必ずまたやれる時が来る」
というわけではありません。
ここで耐えきれなかった場合
再起するのは簡単な話ではないと思います。

世の中が落ち着いて、元に戻ろうとする時まで
活動が可能な状況を作らなければならない。
となると、単にライブを配信するだけではなくて
それを使って収益を上げてもらわなければならない。

SHOWROOMでの視聴は基本的に無料です。
それはそれでより多くの人が観ることができますが
それとは別にしっかりと配信者の方々へ
収益を還元できる仕組みを作る必要があります。

そこで今回は視聴そのものを有料にする機能を用意しました。
本気のパフォーマンスを配信して、
それをチケットを購入して視聴する、
本来のライブのあり方に近い形を実現することを目指しました。

これらの機能を備えた配信は
通常のSHOWROOM配信とは完全に別物になるので
これらを「プレミアムライブ」と呼ぶことにしました。

開発のポイント

フルスクリーンモードと高画質向けサーバーの用意。
この2点をどれだけスムーズに入れられるか。
それがこの開発のポイントになると思っていました。
しかし幸運なことに優秀なエンジニアに恵まれ、
Web、iOS、Androidいずれも
「やろう」と決めたその日のうちには
フルスクリーンに対応してくれました。
配信サーバーも同じく
その日のうちに用意が完了しました。

高画質化の際のポイントとして
「通信環境が高画質に耐えられない視聴者のケア」
という観点が重要だったりします。
低画質モードに切り替えても
一定のレベルで視聴できなければならない。
そのための対応も必要になったのですが
これも迅速に対応してくれました。

意外にも時間がかかったのは有料化の部分です。
プロトタイプはものの30分で作れたのですが
ちゃんと作ろうとすると考慮すべきポイントが多く…
導線はどうするのか
通常の配信ルームとどこを変えるのか
購入してるユーザーと購入済みユーザーの区分け
エラー処理、もちろんテストも確実に。

スピード重視に作りつつも
お金が関わる部分ではあるので慎重に、
そこのバランスを取りながらの開発になりました。

もちろんこれが急に割り込んできたからといって
もともと動いていた施策を止めるわけにも行きません。
そこの兼ね合いが一番大変だったかもしれません…

今後の展望

ひとまず収益還元の仕組みと
フルスクリーンの仕組みは作れました。
今後はこの機能を使って
新しいライブの場をご提供していければ
と考えていますが、まだまだ改善の余地はあります。

何よりまず第一に「仮想ライブ空間」であること。
ここにはやはり拘りたいと思っています。
今回リリースしたバージョンでは
通常モードは普段の配信ルームと同等の見た目なので
ギフトやコメントもご利用いただくことができます。
しかしフルスクリーンで視聴している場合は
ごく普通に動画を見ているだけになります。

パフォーマンスをして、フルスクリーンで観る。
そこまでは今の機能で実現できますが
観客席からの熱量を配信者の方に返すことが
これでは実現できていません。
双方向性が用意できてこそSHOWROOMです。
もしも無観客でのライブだったとしても、
配信で見ている人たちの想いが届けられるとしたら、
それはきっと素晴らしいことだと思うのです。
次はそこを目指したいと思います。

さいごに

熱量高めのブログになってしまいましたが
結局のところ「楽しい土日を取り戻す」というのが
一番のモチベーションだったかもしれません。
そのためにはエンタメ界隈のみなさまが
今後も活動を継続してもらわなければ困るわけで
そこに対してやれることがあるなら今後も全力で挑んでいくつもりです。

SHOWROOMではそんなエンタメに熱くなれる人を募集してます。
ご興味ある方はぜひ!

recruit.showroom.co.jp

SHOWROOM iOS CI/CD周りのお話

こんにちは。SHOWROOM iOSエンジニアの郭(@huiping192)です。 今回はSHOWROOM iOS CI/CD 周りを紹介したいと思います。

CI/CD環境

Bitrise + fastlane + deploygate + Slack bot

Bitrise

Bitrise採用の理由はXcodeバージョン更新の速さです。
今回のiOS13対応もXcode 11 beta早いタイミングでBitriseが対応し、 おかげでiOS13向けての開発CI止めることなく開発できました。

fastlane

iOSチームでは万が一、CIサービスが使えない状況になったとしても、リリース業務に支障が出ないよう、fastlaneに集約しています。
また、他のCIサービス移行も楽にできちゃいます。

DeployGate

iOSチームは元々、TestFailryを使っていましたが、AndroidチームはDeployGateを利用しており、ツールが別れていました。 我々はツールをDeployGateに統一することにしました。

Bitrise Workflow運用

Workflow Trigger

Pull Request

  • Pull Request が作られた際、まずはCIを動かし、buildとテストを走らせます f:id:huiping:20191203200749p:plain:w600

Push

  • masterにmergeされた際、自動的にsubmitされます
  • release/* にpull reqeustがmergeされた際、自動的にInHouse buildを作り、slackでプロダクトテストチームへ連絡してくれます

f:id:huiping:20191203200813p:plain:w600

Workflow

Test(dev-fastlane)

Pull Request作ると自動的にこのworkflowを走らせ、buildとunit testを実行します。 ほかのworkflowと違うのは Dangerによる事前コードチェックgithubのPull Request状態の更新です。

f:id:huiping:20191204193648p:plain:h360

Danger

SHOWROOM iOSチームではほかのメンバーがreview入る前にDangerのswiftlintによるチェックが入って、 LGTMもらったらメンバーがcode reviewに入る感じになります。

通ったらこんな図です。笑
f:id:doggy_dog:20191205163408j:plain:w360

InHouse build (inhouse-fastlane)

iOSメンバーシップは年100端末しか登録されない+都度都度の端末登録も面倒なので、SHOWROOMではエンタープライズ版を使ってプロダクトテストチームに渡し、テストしてもらっています。

f:id:huiping:20191204183432p:plain:h360

DeployGate上げたらプロダクトチームに通知。

f:id:doggy_dog:20191205163411j:plain:w360

adHoc build (iap-fastlane)

大体の場合はInHouse buildでテストできますが、InHouse buildは課金のサンドボックステストできないため、adHoc buildを作ってプロダクトチームに課金周りをテストしてもらう場合があります。
ですが、inhouseのworkflowほぼ同じです。

f:id:huiping:20191204183454p:plain:h360

App Store submit (release-fastlane)

master branchがPull Requestされたら自動的にsubmitにフォローが入るようにしています。 submit成功後はslackに通知してくれます。
Bitrise側はfastlaneのscriptを呼ぶだけ、fastlane内部ではsubmit前チェック、本番build作成、metaデータ更新、tagきりなどを行っています。

f:id:huiping:20191204183116p:plain:w360

Slack Bot

プロダクトテストはほぼInHouseで検証していますが、エンタープライズbuildだと課金検証できないため、adHoc build作る必要があります。
Slack Bot作る前には毎回iOSエンジニアに頼んで作るしかなかったのですが、Slack Bot作ることで、エンジニアの作業が必要なくなりました。

f:id:doggy_dog:20191205163414j:plain:w360 f:id:doggy_dog:20191205163853j:plain:w360

最後

SHOWROOMでは絶賛iOSエンジニア募集中なので、CI/CD、自動化周り興味ある方も大歓迎です!
お待ちしております。

recruit.showroom.co.jp

マネージドサービスを使って、キャンペーンページを切り出しました

こんにちは~

SHOWROOMエンジニアのシミズです。

TIFをはじめたくさんのイベントがあった8月も終わってしまいました。 寂しいですね。

いきなりですが、SHOWROOMのWEBアプリケーションはいわゆるモノリスな巨大な一枚岩なシステムとなってます。 配信機能、視聴機能、配信検索、イベント、アバター、トークルームなどの機能全てがほとんど1つのPerlで書かれたシステムで動いています。 5年以上の運用の中でリファクタリング(というかほぼ作り直した機能も...)などもしてきましたが、どうしても開発スピードに課題を感じるようになってきました。 特に、膨大な機能間が密結合になっており、思わぬところで影響があったりするので全体把握がとても困難です。

我々はこの5年間で生み出し続けてきた機能について、全く使われていない機能や、効果をあげていない機能などについて精査をしています。 それによって、一定の開発スピード低下は抑えられますが、スピード低下を抑えるのではなく、スピードをあげたいという思いがあります。

そこで、我々は改修頻度の高い部分について、SHOWROOMの既存システムから切り離して、軽快な開発ができる環境づくりにチャレンジしました。 このチャレンジではAWSのFargateとS3の静的ウェブサイトのホスティングを利用しています。

今回のターゲットはキャンペーンページです

システム構成

f:id:otto13:20190903174106p:plain
キャンペーンページのシステム構成概略図

基本的な方針として、キャンペーンページ(static-pages)はS3でホスティングします。 WEBサーバーはなく、ユーザー認証がないので全てのユーザーに同じ画面が表示されます。 キャンペーンページに関連するルームやイベントの情報についてはAPIリクエストで情報を取得してレンダリングします。 この時、従来のSHOWROOMサーバー(showroom-server)にリクエストをするようにすると、セキュリティ懸念と負荷懸念があったため、 ルームやイベントの情報を返す専用のサーバー(public-api-server)を新しく構築しました。

public-api-server

f:id:otto13:20190903180435p:plain
public-api-serverシステム構成図

Goで書いたアプリケーションをコンテナにしてFargateで稼働させています。 コンテナを動かしたいだけで特別な要件もない時にはFargateはとても楽ができました。 また、SHOPROOMの機能ですでに利用実績があったのも選択理由です。

SHOWROOMの情報はSHOWROOMのサーバーにリクエストして取得するのですが、毎回リクエストをするとネットワークを経由するためレイテンシーが大きくなってしまいます。 これを避けるためにRedisで情報をキャッシュします。

そして、このサービスの受け口にはCDNを配置しています。 これはシステムの特性上、全てのユーザーに同じデータを返すサービスなので、キャッシュできることを前提に配置しています。 キャッシュ以外の恩恵を受けるためにTTLを0としてCDNを配置するサービスもありますが、本サービスはキャッシュをさせています。 これにより、リクエストを大きくオフロードさせることができ、必要なサーバーリソースを少なく維持しています。

S3+CDNで静的ページをホスティング

f:id:otto13:20190903182742p:plain
静的ページをs3ホスティング

s3ホスティングはS3バケットを作って、CDNを配置するだけでいいのでとても簡単でした。 負荷の懸念からも解放され、レスポンス速度も格段に高速になりました。(静的ページを返すだけなので)

またホスティングするS3バケットは単一である必要がないため、S3バケットを分割することで編集権限の管理についてAWSの仕様を利用できるメリットもあります。

得られた恩恵

一番大きな恩恵は、本施策の目的でもありますが、 既存SHWOROOMアプリケーションから独立できたことです。

それぞれのページがそれぞれのニーズに合わせて技術選択をして自由に実装できるようになりました。 cssも軽量にすることができますし、javascriptに関してもvue.jsで実装するページやjQueryだけで実装するページもあります。

ルームの情報やイベント情報、配信中のライブ情報はpublic-api-serverを経由してshowroom-serverから取得します。 これができるようになったことで従来はSHOWROOMの情報を利用したページはshowroom-server上でレンダリングしなければいけなかったのが、showroom-serverの上になくてもできるようなりました。

キャンペーンページの開発担当者は、基本的にWEBサーバーの開発は不要でクライアントのシステム実装だけに集中でき、またそのクライアントでは開発するキャンペーンのみを扱うので、よりキャンペーンページの開発に集中できるようになります。

課題と今後の展望

複雑なキャンペーンページを実装しようとした時にいい感じのJSONを返してくれるWEBサーバーが欲しいなってなることがありました。 これをしようとした時にshowroom-serverやpublic-api-serverに実装すると、結局またそれらの仕様が膨らんでしまうし、 キャンペーンページの実装者もそれらのサーバーの仕様を把握しなければいけなくなるので課題に対して逆戻りしてしまいます。 現在はその問題についてAWS Lambdaを使えば解決できそうということで、本チャレンジを継続しております。

最後に

本投稿では最近のSHOWROOMのエンジニアってどんなことを思ってどんな仕事してるのかが少しだけでもわかればと思って投稿しました。 もちろん、これだけでなくて他にもいろいろなことにチャレンジしています。 他の投稿や下記エンジニア採用サイトも是非ご覧ください。

また少しでも興味をもっていただけましたら、お問い合わせいただくか、なにかのイベントでご一緒した時にお声かけください。

recruit.showroom.co.jp

iOS13で公開予定の「Core Haptics」を使って、Haptic Feedback (触覚フィードバック) するコードを書いてみた

こんにちは。iOS エンジニアの森廣です。
先日行われたAppleのWWDC2019に参加させていただいたことをきっかけに、最新技術を追いかける楽しさに目覚めました。

今回は、その最新技術の中でも自社サービスに取り込めそうな技術を、実際のコードとともに紹介したいと思います。

Haptic Feedback とは

Haptic Feedback とは、ユーザーアクションに対してハードウェアを振動させる触覚フィードバックのことです。
例で言うと、App Storeでアプリを購入する時、間違ったパスワードを入力した時などに発生する振動が「Haptic Feedback」にあたります。

Haptic Feedback を導入するメリットとしては、ユーザーにより自然な感覚を与えられることで、没入感を高められることです。
(特にゲームやVR/AR領域での活用が見込めるかと思います。)

iOS10からは UIFeedbackGenerator を利用することで3種類のフィードバックを導入できましたが、iOS13から利用可能な Core Haptics ではよりカスタマイズ可能な Haptic Feedback を実装することが可能になりました。

ということで、実際に Core Haptics を用いて Haptic Feedback を実装してみたいと思います!

実装の概要

以下の順番で実装していきます。
1, ライブラリ導入
2, 再生準備
3, 再生
4, パターン変更

注意点としては、バージョン11.0以降のXcodeがMacにインストールされている必要があることです。
(2019年6月現在では、Apple Developer Centerからbeta版をダウンロード出来ます。)

もう一点、今回実装する Haptic Feedback は、画像や動画では表現しづらいのを予めご了承ください。
(実機で動かして、確認してみてください。)

実際のソースコード

ライブラリ導入

利用するクラスに Core Haptics を import します。

import CoreHaptics


再生準備

利用するクラスで HapticEngine を定義します。

var hapticEngine: CHHapticEngine?

HapticEngine の利用準備をする関数を書き、呼び出します。

func setupHapticEngine() {
    do {
        hapticEngine = try CHHapticEngine()
        try hapticEngine?.start()
    } catch {
        print("There was an error creating the engine")
    }

    hapticEngine?.stoppedHandler = { reason in
        print("The engine stopped")
    }

    hapticEngine?.resetHandler = { [weak self] in
        print("The engine reset")
            
        do {
            try self?.hapticEngine?.start()
        } catch {
            print("Failed to restart the engine")
        }
    }
}

(参考にしたページ)

Preparing Your App to Play Haptics | Apple Developer Documentation

再生

CHHapticEvent を作成する関数を作ります。

func createHapticEvents() -> [CHHapticEvent] {
    let intensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: 1)
    let sharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: 1)
    let event = CHHapticEvent(eventType: .hapticTransient, parameters: [intensity, sharpness], relativeTime: 0)
    return [event]
}

(intensityは強度、sharpnessは鋭さ)

CHHapticPatternPlayer を再生する関数を書き、呼び出します。

func playHapticEngine() {
    do {
        let hapticPattern = try CHHapticPattern(events: createHapticEvents(), parameters: [])
        let hapticPlayer = try hapticEngine?.makePlayer(with: hapticPattern)
        try hapticPlayer?.start(atTime: 0)
    } catch {
        print("Failed to play")
    }
}

(参考にしたページ)

Playing a Single-Tap Haptic Pattern | Apple Developer Documentation

パターン変更

上で作成した createHapticEvents を修正して、さまざまな Haptic をつくります。

(パターン1) 強くて鋭い Haptic

func createHapticEvents() -> [CHHapticEvent] {
    let intensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: 1)
    let sharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: 1)
    let event = CHHapticEvent(eventType: .hapticTransient, parameters: [intensity, sharpness], relativeTime: 0)
    return [event]
}


(パターン2) 最初は強くて鋭いが、だんだんフェードアウトしていく1秒の Haptic

func createHapticEvents() -> [CHHapticEvent] {
    var events: [CHHapticEvent] = []
    for i in stride(from: 0, to: 1, by: 0.1) {
        let intensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: Float(1 - i))
        let sharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: Float(1 - i))
        let event = CHHapticEvent(eventType: .hapticTransient, parameters: [intensity, sharpness], relativeTime: i)
        events.append(event)
    }
    return events
}


(パターン3) SOS (...---...) みたいな Haptic

func createHapticEvents() -> [CHHapticEvent] {
    let short1 = CHHapticEvent(eventType: .hapticTransient, parameters: [], relativeTime: 0)
    let short2 = CHHapticEvent(eventType: .hapticTransient, parameters: [], relativeTime: 0.2)
    let short3 = CHHapticEvent(eventType: .hapticTransient, parameters: [], relativeTime: 0.4)
    let long1 = CHHapticEvent(eventType: .hapticContinuous, parameters: [], relativeTime: 0.6, duration: 0.5)
    let long2 = CHHapticEvent(eventType: .hapticContinuous, parameters: [], relativeTime: 1.2, duration: 0.5)
    let long3 = CHHapticEvent(eventType: .hapticContinuous, parameters: [], relativeTime: 1.8, duration: 0.5)
    let short4 = CHHapticEvent(eventType: .hapticTransient, parameters: [], relativeTime: 2.4)
    let short5 = CHHapticEvent(eventType: .hapticTransient, parameters: [], relativeTime: 2.6)
    let short6 = CHHapticEvent(eventType: .hapticTransient, parameters: [], relativeTime: 2.8)
    return [short1, short2, short3, long1, long2, long3, short4, short5, short6]
}

(参考にしたページ)

How to play custom vibrations using Core Haptics - free Swift 5.0 example code and tips

コードの全体像

f:id:taketo_morihiro:20190710143409p:plain

おわりに

以上、iOS13で公開予定の「Core Haptics」を用いた Haptic Feedback の実装でした。
iOS13のリリース自体はまだ先でどうなるかわからないのですが、取り敢えずはこれを他のメンバーに提案してみたいと思います!

SHOWROOM は、最新技術を用いたり企画提案したいエンジニアにとってはとてもやりがいのある会社です。お気軽に問い合わせください!

recruit.showroom.co.jp

Scala Matsuri 2019に参加してきました

こんにちは!SHOWROOM Tech Studio 松本です!

今回は、Scala Matsuri 2019に参加してきたので、その様子を松本、相原、中村の三人でレポートしたいと思います。

Scala Matsuri とは?

プログラミング言語、Scalaのカンファレンスです。

年1回お台場で3日間ほど開催されます。 Scalaにまつわる技術的な発表や、Scala入門ハンズオン、LTなどを楽しむことができます。

https://2019.scalamatsuri.org/

6月28日(金)の様子

f:id:poisonotter:20190701181328j:plain

10時に会場入りしました。当日は台風が直撃しており、開催が危ぶまれましたが問題なく開催されました。

この日はカンファレンス形式で、スライドの発表が行われていました。 いくつか弊社エンジニアから見てよかったスライドを紹介します!

コードで理解するPlayFrameworkの脆弱性

コードで理解するPlayframeworkの脆弱性 - Speaker Deck

Scalaでよく利用されるフルスタックWEBフレームワークPlayFramework。

そんなPlayですが、一般的には堅牢でバグが少ないScalaで出来ているため脆弱性が少ないと言われています。

しかし、その実態は、全く脆弱性がないというわけではなく、やはり人間が関わっている以上様々な脆弱性が存在しました。

詳細な内容は発表資料を見ていただければと思いますが、例えば、過去にはソースコード内でWindowsでのファイルパスを考慮してなかったため、Windowsのパス文字「¥」がURL内にあった場合に、アクセスできてはいけないファイルにアクセス出来てしまうパストラバーサルという脆弱性があったりしました。

また、衝撃的だったのは、脆弱性でよく話題になるStrutsというフレームワークがあるのですが、より多くの人に使われているから、脆弱性が発覚しやすいというだけで、Playはマイナーだから脆弱性が発見されにくいだけかもしれないという発言を発表者の方がしていたことです。(あくまで個人の意見です)

ただ、Playだから大丈夫。Scalaだから大丈夫。という意識を持たず、常に危機感を持って、フレームワークのアップデートなどを定期的にしていくことは改めて大切だなと思い出させてくれました。

AWS LambdaでScalaをサクサク動かす

Running Scala on AWS Lambda in a Snappy Way - Speaker Deck

ScalaでAWS Lambda使おうとした場合大きな問題になるのがコールドスタートの遅さで、例えば、ただHello, Worldを返すだけのLambda関数で初回実行時の所要時間はなんと8588msにもなるそうです。

これに対しScalaで記述しながらコールドスタートが高速なLambda関数を実装する方法として以下の3つが提案されていました。

1つ目はScalaJSを使用し出力されたJSをNodeJSのLambdaとして実行するやり方です。利点としてはパッケージのサイズがとても軽量にできる、NodeJSの豊富なライブラリが使えるなどということでしたが、ScalaJSはかなり独自な書き方をしなければならないのでもはやScalaなのか・・・?と思いました。

2つ目はScalaNativeを使いバイナリを生成してカスタムランタイムで実行するやり方です。コールドスタートは高速ですが大きな欠点としてScalaNativeで使用できるライブラリがかなり限定されること、今だにScala2.12に対応していないことなどがあります。

3つ目はGraalVMのSubstrateVM機能を使いバイナリを生成してカスタムランタイムで実行するやり方です。上2つには若干劣るものの十分高速で使用できるライブラリの制約がScalaNativeよりかなり少ないという利点があります。しかし動的なClassLoaderがあると使用できないという問題があり、残念なことにAWS SDKがこれに該当してしまうそうです。

正直な感想としてそこまで無理にScalaでLamdaを使おうとしなくてもいいのでは?とも思いましたが、Scalaに対する愛が感じられて興味深い発表でした。

Scala における型クラス入門

Intro to typeclass in Scala - Speaker Deck

金曜日はBeginnerセッションも多くありScala初心者でも参加しやすかったです。 中でもとても説明がわかりやすく、参考になったのが、 Scala における型クラス入門 のセッションでした。

なかなか説明しづらい型クラス。型クラスはGofのストラテジパターンであると考えると理解できるという切り口でとてもわかり易い説明でした。

Scalaでは避けては通れないimplicit スライド Scalaにおけるimplicitの意味 はカオスでした。

型クラスで利用されるScala2のimplicitは非常に分かりづらかったのですがScala3ではdelegate forやgivenに置き換えられるようです。

Scala3の記述ですがスライド作成後にも変わったようでまだ決定ではないかも、とのことでした。

Cats、Scalazなどの主要なライブラリでも多くの型クラスがあり、今後の理解のためにも参考になりました。

お昼ご飯

今年もお弁当が豪華でした。数種類のお弁当から選ぶことができます。 ローストビーフ丼や、小分けになったおかずがたくさん入った弁当、野菜中心の弁当などなど

私はローストビーフ丼を食べました。大きさの割に密度があって、重くてボリューミーなお弁当でした。

f:id:poisonotter:20190701181640j:plain

ブースの様子

いくつかのブースにお邪魔させていただきました。

中でもビズリーチさんからいただいた扇子に「不変」「型安全」「暗黙」の文字が書かれており、Scalaの特徴を表した素晴らしいノベルティでした!

f:id:poisonotter:20190701182356j:plain

懇親会

お酒が充実のラインナップで、飲兵衛も大満足な懇親会でした。主催者が酒好き説(スーパードライ、ギネス黒、エビス、プレモル、ハイボール、チューハイ等なんでもあった)

また、オードブルも豪華で、中華、寿司、串、たこ焼きなど盛りだくさんで、中でもたこ焼きが最高でした。なぜか外にガチめの屋台があり、そんじょそこらのたこ焼きよりも美味でした!

f:id:poisonotter:20190701182318j:plain

6月29日(土)の様子

f:id:poisonotter:20190701182423j:plain

この日はアンカンファレス形式での発表でした。 アンカンファレス形式とは、発表者が当日まで決まっておらず、来場者が話したい内容を発表し、他の来場者が投票を行い発表内容を決めていくというスタイルの発表です。

色々と魅力的な内容の発表はあったのですが、今回は特に株式会社はてなさんの発表が印象的でした。 実は弊社でも、かつてPerlからScalaにフルリプレースを検討していた時期があり、はてなさんと状況がすごく似ていてこんな未来もあったのかもしれないというのを思った次第です。

いかにして我々は10年もののPerlプロダクトをScalaでリプレースしたか

How we replaced a 10-year-old Perl product using Scala - Speaker Deck

なぜScalaだったのか

Scalaに強いエンジニアがいたというのと、ロジックが巨大なサービスなので、Scalaの負債を生み出しにくい言語仕様との相性が良かったとのことでした。

リプレースに当たっての課題

・ビジネス側の説得が必要だった

現状すでに稼働しているサービスをわざわざリプレースする意味があるというのをエンジニア以外の方に納得していただくことが大変だったそうです。はてなさんの場合は、既存の仕様が複雑になりすぎてリファクタリングが失敗したという事件が起きたり、段々とビジネス側にもリプレースが必要というのが伝わって行ったとのことでした。

・Perlエンジニアが多い中でScalaをどう書いていくのか

メインジョブがPerlのエンジニアがScalaをやるということで、気をつけたことはCatzやScalazなどの難しいライブラリはあえて使わないということでした。会社のポリシーにもよりますが、あえて使わないという選択肢も忘れないことが大切です。一方で負債を残さないためにDDDやLayered Architectureは徹底したらしいです。

リプレースした方法

・一旦全ての機能追加を止めた

これはサービスの内容次第かと思われますが、はてなブックマークに関しては、一度全ての新規機能追加を停止してリプレース作業に専念したとのことです。 弊社でリプレースを検討した時にネックとなり、解決できないなとなった部分の一つです。

Aが既存サービスでBがリプレースしたサービスだとした時に、並行してAの新規開発を進めつつ、Bのリプレースが終わった時点で、Bは古い状態のサービスAと同じ仕様となってしまうため、BがAに追いつけないという問題が発生します。解決するためにはAを一時的に開発を止めるか、Bの開発速度をA以上にする必要があります。(予算や人員の問題で現実的に難しい)

・現行仕様の洗い出しを徹底的にやった

手戻りが起きないように、不要機能の整理、仕様の洗い出しを時間をかけて行ったとのこと。

・並行稼働させながら徐々に移行

エンジニアのモチベーションUPや対外的に進んでいる感を出すために、少しずつ既存サービスをScalaに変えて行ったとのこと。ただしサービスを稼働させながらのデータマイグレーションは辛かったとのことでした。

結果

当初の予定2年のところが4年かかってしまったらしいです。

しかし、サーバー台数を今までの半分にすることができ、レスポンスも早くなり、機能改修・追加も容易になったとのこと。

弊社もリプレースを考えるタイミングに来ており、参考にさせていただきたいと思います。

まとめ

Scala Matsuriは、発表の内容が楽しめるのはもちろん、細かい部分のホスピタリティが高く、行ってよかったなあと思えるカンファレンスでした。素敵なカンファレンスを開催してくれた運営の方、発表者の方、ありがとうございました。

最後に宣伝ですが、このように弊社ではカンファレンス参加費を補助してくれる制度があり、申請をすれば、平日もカンファレンスに行ける環境が整っております。(Slackで上長に申請すればOKです)

SHOWROOMでは、一緒に働いてくれるエンジニアを募集中です!

recruit.showroom.co.jp

初海外+英語が喋れないけど、WWDC2019に参加してきました

Hi there! iOSエンジニアをやっている、植村(@working_doggy) と 森廣 です。
6/3(月)から6/7(金)にかけてサンノゼで開催された、AppleのWWDC2019に参加してきました。 植村と森廣の2人で参加したのですが、両方ともそもそも初海外だったということでハラハラドキドキでした。
そんな2人が体験してきた内容についてお話出来ればと。また、初めてWWDCや海外に行く人向けの内容も書いていこうと思います。 今回は技術的内容ではないため、そちらに関しては別記事として投稿します!

※ SHOWROOM株式会社はカンファレンスや勉強会等の参加費を積極的に支援してくれます!

そもそもWWDCって?

Worldwide Developers Conferenceの頭文字を取って通称:WWDC
ざっくり説明すると

  • 毎年大規模で行われているApple主催のカンファレンス
  • 国籍・性別・年齢問わず大勢の参加者が訪れます
  • 最新OSの発表やそれに含まれる新しい機能などについて説明するセッション
  • デザインに関するセッションも多く、エンジニアだけでなくデザイナーの参加者も居ます

今回の会場は去年と同様、 San Jose McEnery Convention Center でした。

旅程

今回2人とも初海外+WWDCという大きなミッションということで、早め早めのスケジュールで動くことを心掛けました。寝坊なんてしたら大変ですからね。笑

6/1(土)の17時に出発し、フライト時間は大体9時間程度。日本とサンノゼは16時間ぐらいの時差があるので、睡眠を飛行機で取るように調整したらなんとかなりました。
また、現地の保安検査がどんなものか知らなかったので怖かったです。まあ、案の定うまくいかずきょどってしまったんですが...。

そんなこんなでなんとか、アメリカのサンノゼ空港に到着することができました。

f:id:doggy_dog:20190610134253j:plain:w240

WWDCのチェックイン [6/2(日)]

自分の番になり、会場の中に入ると長いテーブルにスタッフが座っており、1on1の様な感じで座わるように指示されます。 担当の方の話しを聞きつつ、指示に従いましょう。

登録が完了するとプレートと参加した人がもらえるジャケットやバッジなどのノベルティが頂けます。 その後は会場から自由解散でまた明日、という感じで終わります。

f:id:doggy_dog:20190610131719j:plain:w240

WWDCのチェックインについて

WWDCに参加するためには参加証を持っている必要があるため、前日に会場に行ってチェックインするのが基本です。 Appleからメールで送られてくる証明証をWalletに入れておくと、当日スムーズに登録することが可能です。

WWDC2019のノベルティ

今年はバッジとジャケットでした。 ジャケットがわりとよく、色も豊富で結構気に入っています。 赤・青・黒・オレンジから選べました。普段も着れそうな感じだったので黒にしました。

初日(Keynote/Session) [6/3]

初日は主にKeynoteを見る感じでした。 やはり朝から並んでいる人がいるようで4:00前後から列が出来ていたようです。 僕たちは7時ぐらいを目安に出発をして8:00ぐらいに到着したのですが、やはり既に人はたくさん並んでおり、真ん中あたりになりました。 待っている間に、ご飯が置かれているのでそれを食べたりだとか、近くの人とコミュニケーションを取りながら待つ感じでした。

そんなこんなで時間がくると入場が開始され、大きなプレゼンをするホール的なところに通され、自由に座る感じでした。 (早ければ早く来ている人ほど前の方に座れるので、生ティムクックが見放題となります。) 僕は半ばあたりだったので、真ん中の方に座わりました。

Keynoteが始まり、ティムクックが入場してくると会場は大盛り上がり、歓声が上がると同時に拍手のラッシュ。 僕の席からは残念ながらティムクックがあまり見えなかったのでちょっと残念でしたね...。

f:id:doggy_dog:20190610134103p:plain:w240

開催日(Session/Lab) [6/4 ~ 6/8]

セッションについて

WWDCではさまざまなセッションが行われていました。

新しく発表された「SwiftUI」や「ARKit3」「SF Symbols」の解説から、盲目の建築家から学ぶデザインまで様々なことを学ぶことができました。

ARKit3では、従来の顔の動き判定だけでなく体の動きも取得できるようになったため、活用方法が大幅に増えました。
体の動きを利用してアプリを操作したり、ゲームを作ったりしたいなと思いました。

「ARKit3」導入の動画はこちらです。
developer.apple.com

印象に残ったセッション

一番印象に残ったセッションは、初日に行われた「Apple Design Awards」です。

2019年の良いデザインのアプリが発表されたのですが、どれもユニークでとても参考になりました。
一番最初に発表された「Ordia」は指一本で遊べるとてもシンプルなゲームです。ステージが進むごとに難しくなり、ゲーミフィケーションの設計がよくできていると感じました。

「Apple Design Awards」の動画はこちらです。
developer.apple.com

Labのお話

Lab(ラボ)では、Appleのエンジニアに、デザインや「Metal」のことから、ヨーロッパ市場への進出のコツ、「HTTP Live Streaming」のことまで直接聞くことができます。

今回僕が参加したのはARKitのLabで、今回発表された「モーションキャプチャ」のことについて聞いてきました。
けして上手とは言えない僕の英語の質問をしっかり理解してくれ、わかりやすい英語で教えてくれたエンジニアのエリックさんには感謝しきれません。

観光

僕たちは前日にチェックインを済ませ、そのままバスやライトレールを使い、サンフランシスコに向かいました。

ベイブリッジを眺めながらスタバを飲んだり、フェリーに乗ってアルカトラズ島を見たり、世界で一番美しいと言われる「ゴールデンゲートブリッジ」も見に行きました。

f:id:doggy_dog:20190610131539j:plain:w480 f:id:doggy_dog:20190610131542j:plain:w480f:id:doggy_dog:20190610131549j:plain:w480

帰国

空港でのチェックイン

今回、僕たちはユナイテッド航空だったのでその場所まで行き、機械でチェックインを済ませました。機械は日本語で表示できるので、スムーズにいけます。しかし、ここで問題が。

僕たちの航空券には座席シートが指定されていない

すかさず慌てググってみると、オーバーブッキングの可能性があるの!?なんて驚きました。 記事を読み進めてみると、搭乗口受付の時間に名前が呼ばれるのでそこから座席が指定される様です。
とは言え、早くきたのに搭乗受付開始まで席が決まらないのはソワソワしてしまいました。

そして...。

受付開始し、担当の方に「これどうするのでしょうか」と聞くと、軽く頷き、隣同士の席の新しい航空券をくださりました。 予想以上にスムーズで、ソワソワしていたのがなんだったのかぐらい、簡単でした。笑

f:id:doggy_dog:20190610145610j:plain:w240

保安検査について

日本の保安検査とは割と違います。 身につけているもの全部・バッグをトレーに入れるのは同じですが、靴まで脱いで全身を見られます。

飲み物も没収されてしまうので、行く前に飲むのが無難です。 僕は知らないで保安に着く直前に立ち寄ったSHOPで5$ぐらいの水をバッグに入れたまま行ってしまい、1口しか飲んでないのに没収されてしまいました。。 その5$を使って、帰国したら牛丼を食べたかった...。

まとめ

初海外&初WWDCということで目一杯、楽しんできました!
お金もかかるし慣れない海外で不安も大きかったですが、勇気出して行ってみたら最高でした!

参加するのに結構なお金がかかってしまいますが、毎年行われているため、参加するためのチケットを応募してみては如何でしょうか
Appleが開催しているカンファレンスということもあり、大規模すぎて感動すること間違いなしです。

また、SHOWROOM株式会社は エンジニア を募集しております。
同じ仲間になって働いてみませんか?お待ちしております!

f:id:doggy_dog:20190605073538j:plain:w240