# プロジェクト内容
会員制SNSの開発・Web / iOS
# チーム
デザイナー1人・エンジニア3人・スクラムマスター1人・PO1人
# 担当工程
## 簡単なチャット画面を作成
### 内容
React初心者として本業務に入る前に同期のバックエンドエンジニアとFirebase Realtime Databaseを利用したリアルタイムチャット画面を作成を試みた。
### 担当
- フロントエンド(React)
### 工夫・苦労
**プログラミンング経験が少なかった。**
新しい技術など勉強すること、実際に試すという学習方法が自分に合っていると感じていた為、集中して学ぶことが出来、結果、予想以上に早くReactの基準を理解することが出来た。
---
## デザインを分析しUIコンポーネントを作成
### 内容
- Atomic Designのガイドライン通り、新規機能のUIデザインを organisms -> molecules -> atomsのカテゴリーに分け実装を行った。
### 担当
- フロントエンド(React)
### 工夫・苦労
**色の変更や更新をしやすくした。**
修正前の仕様としては、デザインガイドラインで設定されている色やテキストサイズを実装上で使用したい場合 `00.js` というまとめファイルから色をimportする必要があった。こうした仕様によってlintのエラーやimport忘れが発生したことにも関わらず、やり方としては今時ではなかったため、修正すべきだと感じた。
```javascript
import { gray01, ... } from 'color.js'
import { w1, ... } from 'fontSize.js'
const themedComponent = styled.div\`
font-size: ${w1};
color: ${gray01};
`)
```
デザインガイドラインをまとめたテーマファイルを作成し、プロジェクトで利用されているスタイルを簡単に一つの場所から取れるように修正を行った。
```javascript
import color from 'color.js'
import fontSize from 'fontSize.js'
export const theme = {
color,
fontSize,
}
```
こうして、色やテキストサイズを含めた `theme` オブジェクトを `styled-components` の `ThemeProvider`の値に渡し、プロジェクトの上レイヤーでアプリケーションのコンテンツをラップした。
```javascript
export { theme } from ...
const App = () => {
return (
<ThemeProvider theme={theme}>
// アプリケーションのコンテンツ(routerなど)
</ThemeProvider>
)
}
```
結果、テーマの更新や変更はimportなどせず利用出来るようになった。
```javascript
const themedComponent = styled.div(({ theme }) => `
font-size: ${theme.fontSize.w1};
color: ${theme.color.black};
`)
```
---
## iOSログインSDKをReact Nativeアプリに組み込み
### 内容
- 所属事業部で提供しているプラットフォームのリーチをより拡大するため、ネイティブアプリを開発することになった。ネイティブエンジニアの居ないチームで利用可能な言語として選ばれたのはReact Nativeであり、自分に与えられた初タスクはログインSDKのiOS版を利用したログイン情報をReact Native側で処理できるようにすることだった。
### 担当
- アプリフロントエンド(Objective-C, React Native)
### 工夫・苦労
**SDKの仕様書がなかった**
SDKがレガシーだったことにより、仕様ドキュメントやコメントがあまりにも少なかった為、Objective-Cを学習しながらSDKのソースを読み、理解向上に努めた。ネイティブ開発初心者だったため、学習コストが高かった。
一定の理解を得てからReact NativeのNative Moduleを利用し、SDKとJS側を繋げた。
ネイティブ側で定義した関数や値をJavaScript側に渡すことができる仕組みとして用意されている `Native Bridge` モジュールを使い、ログイン情報を取得する関数をReact NativeのiOS側で定義した。
本体に用意されている `Promise` 型として使える `RCTPromiseResolveBlock` と `RCTPromiseRejectBlock` を使い、SDKのログイン処理の段階をJavaScript側からも監視できるようにした。
```objectivec
RCTPromiseResolveBlock Resolver;
RCTPromiseRejectBlock Rejector;
RCT_EXPORT_METHOD(isLogin: (RCTPromiseResolveBlock)resolve rejector:(RCTPromiseRejectBlock) reject) {
Resolver = resolve;
Rejector = reject;
AccessToken *accessToken;
NSDictionary* dic;
// アクセストークンを取得
accessToken = Auth.sharedAuth.accessToken;
dic = [self dictionary:accessToken];
// RNのBridgeを通ってJS側に渡す
Resolver(dic);
}
```
最後、ログインSDKから取得した情報を `NSDictionary` 型の変数に保存し、JavaScript側でparse出来る形に変換した。
```objectivec
- (NSDictionary *) dictionary:(AccessToken *)token {
return [NSDictionary dictionaryWithObjectsAndKeys:
token.accessToken,@"accessToken",
// 残りの情報を上記と同様に変換
nil];
}
```
こうして、`Native Modules` ライブラリーを使用し、JavaScript側のReduxレイヤーでログイン情報受け取りを達成した。
```javascript
const token = yield call(NativeModules.AuthModule.isLogin)
```
---
## カメラ機能作成におけるアイコン制作
### 内容
- Figmaを使いカメラ関連の新規機能に必要なsvgアイコンの作成を行った。
### 担当
- UI/UXデザイン(Figma)
- アイコン作成(Figma)
### 工夫・苦労
**カメラ画面のデザイン案を作成した。**
全体との一体感を確認するため、作成したアイコンが実際に使われているところをデザイン上で用意し、細かい修正を行った。
確認が出来、さらにアイコンを入れた画面をチームのデザイン担当に提案したところ、高く評価され、提案した画面はそのまま本番利用されることになった。
----
## ライブ配信再生機能を作成
### 内容
提供しているプラットホーム(web, iOS)でコミュニティ内ライブ配信を視聴する機能のデザイン及び開発を担当。
### 担当
- フロントエンド(React, React Native)
- UI/UXデザイン(Figma)
### 工夫・苦労
**HLS.jsを利用し、AWS MediaLiveからのライブ配信を視聴できるようにした。**
MediaLiveで行われているライブ配信をWeb上で視聴できるため、HLS.jsというライブラリを採用することとした。Reactで作成されている担当プラットフォームでより簡単に使えるようにコンポーネント化も実施した。
HLS.jsをラップするクラスを用意し、React側で初期化。本サービスのAPIを叩き、暗号化された再生用URLを取得したところ、HLSラッパーへ渡し、再生に伴う初期化を行うように作成した。
```javascript
// ラッパーコンポーネント(React)
this.player = new HLSVideo(this.videoElementId)
init = async () => {
try {
// 再生用URLを取得
const { url } = await fetchUrl(this.id, { accessToken: this.accessToken })
this.player.load(url)
} catch (error) {
// エラー処理
}
}
// render
render() {
return (
<Video id={this.videoElementId} />
)
}
```
ラッパー内ではHLSのインスタンスを初期化し、設定などを加えたらイベントのバインド及び用意されている関数でHTMLのvideoタグをHLSのインスタンスを紐付けるようにした。
```javascript
import Hls from 'hls.js'
// HLS.jsラッパークラス(生JS)
init = (videoElementId) => {
this.player = document.getElementById(videoElementId)
}
load = (url, options = {}) => {
const hlsOptions = {
// HLS.js用設定(bufferSizeなど)
}
this.hls = new Hls(hlsOptions) // インスタンスを生成
this.bindHlsEvents() // HLS用イベントをバイド
this.hls.attachMedia(this.player) // HTMLのvideoタグに
}
```
HLSインスタンスのイベントリスナーを使用し、イベントが発行された際に親コンポーネントから渡された再生用URLをロードし、成功したらeventEmitterを使い、親にイベントを送るようにした。
```javascript
bindHlsEvents = () => {
// hls.attachMediaが成功した時に発行されるイベント
this.hls.on(Hls.Events.MEDIA_ATTACHED, () => this.hls.loadSource(this.url))
// AWS MediaLiveからの情報のparseに成功した時発行されるイベント
this.hls.on(Hls.Events.MANIFEST_PARSED, () => this.emit('MANIFEST_PARSED'))
// ...他イベントのバインド
}
```
最後、親コンポーネントの方で発行されたイベントを受け取り、配信データのparseに成功した場合、ライブ配信の再生を開始するように作成した。
```javascript
// ラッパーコンポーネント(React)
this.player.event.on('MANIFEST_PARSED', () => {
this.player.play()
})
```
**Laravel Echoを関数コンポーネント内で使えるカスタムHookを作成した。**
JavaScriptでLaravelのweb socket通信を受け取りやすくしてくれるLaravel Echoを利用。
リアルタイム性が求められるライブ配信環境で少しでも表示スピードを上げるため、Reduxのステートで管理するよりコンポーネントのローカルステートで各データの管理を行う方針に決定した。
そのため、コンポーネントレイヤーでの非同期broadcast送信受け取りができるように修正する必要があった。解決策として、ReactのHooksを利用し、Laravel Echoのインスタンスを関数型コンポーネント内でもアクセスできるようにした。
Socketサーバーへのコネクションを行うもの及びbroadcast用イベントリスナーという二つの関数を用意し、初期化した時に受け取る引数としては接続に必要なチャンネル情報を定義した。
```javascript
import useEcho, { EchoChannel, EchoEvent } from 'useEcho'
const [connect, listen] = useEcho(EchoChannel.hogeChannel(hogeId))
```
より理解しやすくなるため、プロジェクトで利用されているsocketサーバーの情報をオブジェクト化し、作成したカスタムHookに含めることを試みた。
```javascript
export const EchoChannel = {
hogeChannel: (hogeId) => ({ channelName: `hoge.${hogeId}`, method: 'join' }),
}
export const EchoEvent = {
hogeChannel: {
eventCreated: 'event.created',
},
}
```
コンポーネント側で任意のチャンネルに対してlistenをかけ、broadcast通信が届いた際にその場で処理が可能になった。
```javascript
useEffect(() => {
connect()
.then(() => listen(EchoEvent.hogeChannel.eventCreated, (data) => {
// broadcastされた内容を処理
}))
}, [connect, listen])
```
本プロジェクトの場合、コメントをローカルステートに入れ表示する一方、リアクションを長く保存する必要がないため、カスタムの一時保存が可能なローカルステートに入れ、表示させている。
# その他
2020年1月〜2020年7月の間、徴兵制度参加のため母国に戻り休職していました。