ユニファ開発者ブログ

ユニファ株式会社プロダクトデベロップメント本部メンバーによるブログです。

会社でスナックを立ち上げた話

~お品書き~

ご挨拶

あらこんばんは、私「スナック洋子」というスナックのママをやらせていただいております洋子と申します。
昼間は ディレクター 兼 QA をやっておりまして、二足の草鞋を履かせていただいております。 あ、 ディレクター 兼 QA 兼 ママ なので三足ですわねおほほ。(足は三本もない。)

すみませんちょっとつらくなってきたので、ここからはいつもの感じで書きますね。

今回は社内コミュニケーションを活性化させる場としてこういう方法もあるよーってことで紹介できたらいいなーと思っております。

ゆるい話なのに、思ったより長くなりましたすみません。

「スナック洋子」について

8月にオープンしたばかりでして、ママとしてはまだまだ駆け出しです。
「スナック洋子」は社内向けと社外向け、それぞれで開店しています。

今回は「社内向け」のお話がメインです。
(社外向けは最後にチラっとだけ話します。)

社内では月に一度、最終金曜日に開催しています。
開催場所はオフィスの一角でやっています。

やりたかったこと

社内みんなのオフな場所をつくりたい

会社もなかなか大変なフェーズなので、社内はドタバタ。もう本当にドタバタ。

そんな中、みんなにとって「気晴らしの場」でも「ただ楽しい場」でもなんでも良いのでつくりたかったという感じです。

「あー疲れたなーちょっとスナック寄って帰るかー」
「お、なんかやってる楽しそうー」

ってみんなに思ってもらえていたら大成功です。

社内コミュニケーションの活性支援

ユニファはものすごい勢いで社員が増えています。
(私が入社した4年前は15人で、今や150人以上…!10倍やばい…!)

会社の規模がおおきくなってくると一度も話したことがない!という人が出てきます。
私の体感ですが、100人を超えたくらいから発生しはじめる気がします。

部署をまたいでのコミュニケーションが減り「あそこの部署何やってんだ状態」や、「もはや正社員かパートさんか委託さんなのかがわからない状態」や「役員と一度も直接しゃべったことない状態」ですね。

それを少しでも減らすために、会話をするきっかけをつくれたらなーと思っています。

「最近何してんのー?」
「お、はじめましてー!」
「土岐でございます」←CEO

って会話が生まれていたら大成功です。

私が楽しみたい

今までの話全部忘れてもらっていいくらいここです!(笑)

ママが一番楽しまなくてどうするんですかくらいに思っています。
何事も続けるには運営している側が楽しまなきゃ続かないですよ。(仕事も同じだと思う。)

私は会社のみんなが好きなので、みんなが楽しそうにキャッキャウフフしているのを見ながらお酒が飲めればそれが私にとっての最高です。

スナックをつくる

といっても、自分でつくってないんですよねこれが(笑)

私個人的な話をしますと、私は「人と酒が好き」って理由でスナックをやるのが夢だ!ってみんなに宣言していました。
そんな中、オフィス移転をしまして、カウンターが設置されました。
これはスナックいけるのでは…!って思っていたところ、
みんながロゴをつくり、名刺をつくり、看板をつくり、勝手にスナックが出来上がりました(笑)

目的や運用ルールを決める

さて、スナック自体はみんなの力でできました。
次は運用ルールや集客です。

1. Googleドキュメントで目的・ルールを作成

叩きはスナック洋子の後援者の方が作成してくれました。(みんなで作っている感じ良いね。)
叩きを元にこんなのもあったほうがいいよねーって話をして決まった内容が以下です。

趣旨と目的

  • 趣旨(開催日と開催場所など)
  • 運営目的
  • ルール策定の目的

ルール

  • 片付けの徹底
  • 深酒の禁止
  • 残業している方への配慮
  • ボトルキープ方針(誰がどれだけ飲んでもOKなものだけキープできる)
  • 出禁ルール(年間でルール違反3回で1年間出禁)

これができたら役員や部長陣へ「本当にスナックやるよー!」って宣言と総務の了承を得てきます。

ありがたいことに、会社としてGOGOでした。
こういう活動の背中を押してくれる会社は良いなーと思いました。(いつか予算もぎとりたいと思っている。)

2. お客さまへの情報発信の場をつくる

Slackにチャンネルを作成して、お客さまへの連絡や活動報告をする場としました。

開催後にはこんな感じで報告しています。

f:id:unifa_tech:20191220180344p:plain
スナック活動報告

お客さまのお金を預かる以上、ちゃんと報告はしないとですよね。

3. お客さまの集客

全社員がいるSlackチャンネルで活動開始のご連絡をし、チャンネルへ入ってもらいました。

2回目以降も同じ感じで、全社員へ周知→チャンネル誘導→チャンネルで詳細をご連絡、という形です。

いよいよスナック開店!

開店までにやったことを箇条書きします。

  • お客さまの人数把握
  • お客さまのスケジュール登録と、目的・ルールの周知
  • 食べ物・飲み物の確保
  • 開催日が近くなったらリマインドと人数再確認

得られたもの

そりゃあもうみんなの笑顔ですよね!(なんつって)

仕事やプライベートな話から、ボードゲームやったり、そりゃもうキャッキャウフフですよ。
実際みんなの話を聞いていると、狙った部分の会話もしてくれていたりと、最高でした。
また、役員もよく遊びに来てくれるのでみんな役員とたくさん話せて楽しそうでした。

実際の効果がどれくらいあったのかはどうやって測ろうかなぁは考えていますが、
それよりもみんなが楽しんでくれていればもう大成功なのではって正直思っています。

f:id:unifa_tech:20191220183149j:plain
看板をつくった張本人
f:id:unifa_tech:20191220183209j:plain
黒服担当
f:id:unifa_tech:20191220183119j:plain
オープン初日の様子
f:id:unifa_tech:20191220183155j:plain
ロゴをつくった漁師(太刀魚を捌き中)
f:id:unifa_tech:20191220183204j:plain
ボードゲーム盛り上がり中

カイゼン

もちろん課題もあるのでそれは今後運営しながらカイゼンしていき、より良いスナックにしていきたいと思っていきます。

  • 正社員じゃない方への周知方法(チャンネルに気軽に入れないなど)
  • 他部署が何をやっているかわからない問題は解決していないので、LT大会とかやりたい
  • お酒の管理(スナック開催日の夕方から冷蔵庫がパンパンになってしまう)

CEOに大きい冷蔵庫をねだろうかと思っています。
(土岐さーーーん見てますか!?冷蔵庫欲しい!冷蔵庫冷蔵庫!!!)

まとめ

社内でスナックをオープンさせると楽しいよ!

場をつくるのはちょっとたいへんだけど、それ以上に測れない何かを得られるよ!

ということで以上です。
思っていたより長くなりました、最後まで読んでくれたあなたはもうスナック洋子のお客さまです!

ちなみに社外向けの「スナック洋子」については、採用活動の一環としてオープンしております。

  • ユニファのプロダクトを知ってもらう
  • QAチームやディレクターの働き方やプロダクト開発について、こんな感じでやってるよーの紹介

などなど、ユニファに興味があるかたはどうぞご来店ください。
(TwitterとかFacebookとか、なんとかしてママに連絡とってもらえれば日程調整して開催します!)

看板設置するだけでどこでも「スナック洋子」になるので簡単なのです(笑)

それではみなさまのご来店を心よりお待ちしております。ママより

Bloom Filterについて

こんにちは、プロダクト開発部のちょうです。最近だいぶ寒くなってきて、こたつからなかなか出られません。こたつとみかんがあれば一日いきれると思いつつ、普段自分が学んでいる内容をすこし紹介したいと思います。

今回の内容はBloom Filterです。Bloom Filterは一言でいうと、空間効率のいい確率的データ構造です。要素が集合のメンバーであるかどうかのテストに使われます(wikipediaより)。確率ですので、使えるものではないと思う人が多いかもしれないが、Bloom Filterの特徴を理解すれば問題なく利用できます。

要素が集合のメンバーであるかどうかを確認するには、一番普通の方法はHash関数を元にするHashSetというデータ構造を使います。HashSetは基本 O(1) の速さで判断できます。もちろん、Hash関数による衝突の可能性があります。最悪の場合、HashSetの内部tableのサイズを倍にする必要があります。それに、要素数が非常に多い領域では、普通のHashSetだと空間効率が悪いと予想できます。ここで、Bloom Filterの登場です。

Bloom Filterは空間効率のいいデータ構造です。なぜなら、利用するメモリー領域を倍にする必要がありません。最初から固定してもいいです。それと、Bloom Filterの要素の単位は bit です。32bitマシンでinteger型だと最大32個の要素が入れます。

Bloom Filterは複数のHash関数を利用します。一つのHash関数だと衝突による実際集合のメンバーではないのにメンバーですという結果になる可能性が高いので、複数Hash関数で可能性を減らします。

サイズは8のBloom Filterを例にします。

| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |

最初すべてのbitが0です。要素 foo を入れます。Hash関数を3つにしましょう。Hash1、Hash2とHash3。3つのHash関数の結果は2, 4と7とします。

| 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 |

foo を入れたBloom Filterです。indexが2、4と7の位置を1にセットします。次は要素 bar を入れます。3つのHash関数の結果は1, 2と3とします。

| 0 | 1 | 1 | 1 | 1 | 0 | 0 | 1 |

同じく、indexが1、2と3の位置にセットします。

では要素をテストしましょう。要素 foo でテストすると、3つのHash関数の結果で、場所2、4と7にすべて1にセットされ、メンバーであるということになります。同じく要素 bar でテストしてもメンバーであることもわかります。

続いてメンバーではない要素をテストします。 要素 aaa にたいして、Hash関数の結果を2, 5, 7とします。index 2と7に1がセットされましたが、5がセットされないです。ゆえ、 aaa がメンバーではありません。要素 bbb にたいして、Hash関数の結果は2、3と4とします。すべての位置に1がセットされ、メンバーであるという結果になりますが、実際はメンバーではありません!

ここでわかったことは、Bloom Filterはメンバーではない要素でもメンバーであるという結果になる可能性があります。これはfalse positiveということです。こういう問題があって、Bloom Filterは使えものにならないと思う人がいるかもしれないが、Bloom Filterはメンバーではない要素をメンバーであるという結果にならない(つまり、メンバーではない結果でしたら絶対メンバーではない)です。まとめてみると、

要素 事実 テスト
foo メンバー メンバー
bar メンバー メンバー
aaa メンバーではない メンバーではない
bbb メンバーではない メンバー

要素 aaa はメンバーではないの結果なので、メンバーではないということが断言できます。逆に、メンバーであるという結果になったら、メンバーではない可能性があります。

こういう特徴を受け、Bloom Filter + DBの使い例を考えみましょう。

DBに100万のレコードがあり、サイズ200万(メモリーやく244KB)のBloom FilterをDBの前にします。毎回レコードを取得するとき、まずBloom Filterを通して、もし存在しないなら、レコードはかならず存在しないので、存在するレコードだけDBと問い合わせします。

こうすることで、Bloom Filterでいらない問い合わせを減らすことができます。Bloom Filterの確率的という特徴もカバーできます。実際、Bloom Filterを利用するシーンはほとんど、レコードは非常に多く、例えばメールボックスのSpam Filter、Bloom Filterなしだと裏側のデータソースへものすごく負荷をかかることが予想できます。

Bloom Filterの特徴を理解できると、以下のような簡単なBloom Filterが書けます。

import java.util.*

interface Hasher<K> {
    fun apply(key: K): Int
}

class SimpleStringHasher(
        private val seed: Int, 
        private val max: Int
) : Hasher<String> {
    override fun apply(key: String): Int {
        return key.fold(seed) { acc, c -> acc * seed + c.toInt() } and (max - 1)
    }
}

class SimpleBloomFilter<K>(
        private val bitSet: BitSet, 
        private val hashers: Collection<Hasher<K>>
) {
    fun mightContains(key: K): Boolean {
        return hashers.all { bitSet.get(it.apply(key)) }
    }

    fun put(key: K) {
        hashers.forEach { bitSet.set(it.apply(key)) }
    }
}

fun main() {
    val size = 2048
    val bloomFilter = SimpleBloomFilter(BitSet(size), listOf(
            SimpleStringHasher(1, size),
            SimpleStringHasher(7, size),
            SimpleStringHasher(23, size)
    ))
    for (i in 1..1000) {
        bloomFilter.put(i.toString())
    }
    println((1..2000).count { bloomFilter.mightContains(it.toString()) })
    // 1024 -> 1582
    // 2048 -> 1000
}

SimpleBloomFilterのソースコードをすこしみてみましょう。mightContainsはすべてのHash関数が計算した位置に1がセットされればtrueを返します。putはすべてのHash関数が計算した位置に1をセットします。

main関数では、サイズ2048のBloom Filterを作ってました。それと1から1000までという要素をBloom Filterに入れます。最後は1から2000までの要素でテストします。サイズは2048だと、最後はきれいに1000とカウントされますが、サイズ1024の場合は1582の結果になります。ぜひ試してください。

最後に、false positiveつまり誤判断を減らすにはサイズどれぐらいにすればいいでしょうか。その質問にたいして、Bloom Filterの論文に答えがありますが、ここで結論を出します。

Pfpをfalse positiveの確率とします。nは入れようとする要素の数です。mはBloom Filterのサイズです。

m = - (n * ln Pfp) / (ln 2) ^ 2

おまけに、Hash関数の数kも

k = (m / n) * ln 2

いかがでしょうか。すこしBloom Filterに理解していましたでしょうか。普段の開発に使わないかもしれないが、こういうものがあると覚えていただければ幸いです。

GitHub Actionsで「さくらのレンタルサーバ」にFTPしようと思ったらドツボにハマって3日3晩寝不足になった話

TL; DR

「国外IPアドレスフィルタ」をOFFにしましょう。

はじめに

みなさんこんにちは。

ユニファ初のアドベントカレンダーの12月22日を担当します。サーバーサイドエンジニアの柿本です。

知り合いに頼まれてホームページを作ることがあります。まあ本職ではないしデザインセンスの問題もあるのですが、それほどのレベルは求められないのでお小遣い稼ぎと思って作ります。
*ちなみにユニファは副業OKです!ユニファの数ある良いところのうちの一つですね。

静的サイトを作る時もソースコードをGitHubで管理しますが、ホスティングサーバーにFTPでアップして、それとは別にGitHubにpushするという二度手間をなんとかしたいと思っておりました。

GitHub Actions があるじゃないか!

GitHub Pagesを使えばpushするだけで反映されますが、サーバーサイドスクリプトが使えません。つまり、問い合わせフォームを置くのが難しくなります。

そこでGitHub Actionsの登場です。 https://github.blog/jp/wp-content/uploads/sites/2/2019/08/ActionsHero.png?w=1200

GitHub ActionsはGitHubのリポジトリにビルトインされているのでお手軽に使えるし、レンタルサーバーにFTPすればPHPも使えます。

GitHub Universe 2019で正式版リリースが発表されたばかりですね。早速使ってみます。

セットアップ

GitHub Actionsでやりたいことはたった一つ

masterブランチにpushされたら「さくらのレンタルサーバ」にFTPする

GitHub Actionsのセットアップの仕方をここにつらつらと書こうと思ったのですが、ネットで検索すればたくさん情報が出てくるので割愛します。

最終的な私の成果物はこちら↓↓↓

github.com

GitHub Actionsがいかに簡単に使えるかはご理解いただけると思います。

長い旅の始まり

FTP-Deploy-Action というFTP関係のものでは比較的人気のあるサードパーティ製のactoinsを使って実行したのですが、待てども待てども終わりません。

f:id:unifa_tech:20191219022740p:plain
20分ほど待ちましたが終わりません

lftpを直に使ってもダメでした。

f:id:unifa_tech:20191219024212p:plain
ログすら見れません 泣

curlでftpを使ってもダメでした。

f:id:unifa_tech:20191219024414p:plain
`curl: (56) response reading failed` になります

SFTPにしてみても、ダメでした。

f:id:unifa_tech:20191219023711p:plain
`Permission denied` になります

その他100通りくらい(は大袈裟ですが 汗)の手段を試しましたが、どれもダメでした!!

自分のmacでubuntu:latestをdockerで走らせて、そこからは問題なくFTPできるのですが、なぜかGitHub Actionsでは動きません。

IP制限でした

気づいてしまえば、「まあそうだよね」て話なのですが、FTPサーバーのIP制限が原因でした。 さくらのレンタルサーバはデフォルトで「国外IPアドレスフィルタ」がONになっています。 help.sakura.ad.jp

GitHubのサーバーが海外であることは想像に難くないですね。このフィルタ機能をOFFにしましょう。

f:id:unifa_tech:20191219030030p:plain
ついに成功!!

学んだこと

1晩頑張ってうまくいかなかったらテクニカルサポートに問い合せしよう。

おそらく一瞬で解決したと思います。

ユニファでは、一緒に「保育をハックする」エンジニアを募集中です!

3日3晩寝不足になっても技術が好きな人を募集中です。(寝るのも大事)

unifa-e.com

Rails + Webpacker + Vue.jsの構成でSystem Testを動かしてみる

こんにちは、Webエンジニアのほんまです。

ユニファのAdvent Calendar 2019も21日目ですね。 昨日は、弊社インフラエンジニアのすずきによるAWS Lambda Provisioned Concurrencyに関するエントリでした。 25日間、連続で記事を書き続けられるほどメンバーが集まったんだなぁと思うと、感慨深いものがあります。

それでは本題に入っていこうと思います。 先日、私が所属するチームで担当したルクミーフォトのリニューアルでの変更点を紹介しました。

tech.unifa-e.com

このエントリ内で「フロントエンドではVue.jsを使用したシングルページアプリケーション(SPA)になっている」と紹介しています。 一方、Rails 5.1からは System Test が導入されており、E2E(End To End)の画面レベルでのテストを容易に実現できるようになっています。 画面内で実行されるJavaScriptにも対応しており、この機能を使った自動テストをやってみたいなーと思っていました。

ここで1つ疑問がありました。 「Vue.jsを使ったSPAのアプリケーションも、RailsのSystem Testでテストできるのだろうか?」ということです。

今後、画面レベルでのテストも自動化していきたいと考えており、この機能がリニューアル後のルクミーフォトでも利用可能なのか、今回試してみようと思います。

続きを読む

AWS Lambda Provisioned Concurrencyをゆるく検証

もういくつ寝るとお正月ございます(適当な挨拶)。

ユニファのインフラ見ているすずきです。

ユニファのアドベントカレンダーの20日目となります。

re:Inventの時差ボケも治り快調です。

そして昨日Reproさんでre:Invent報告会で話してきました。

repro-tech.connpass.com

その後半でAWS Lambda Provisioned Concurrencyを検証した結果を話したのですが、 そのことについてブログにしようと思います。

続きを読む

ユニファのQAになってフラットにものづくりに関われた時の思い出

ごあいさつ

こんにちは。

ユニファアドベンドカレンダーをご覧いただきありがとうございます。 QAチームの斉藤と申します。 今回は私がユニファに入社して「ものづくりに関わってるなぁ」と実感した思い出をふりかえります。

技術的なことは全く書いてないので、のんびり読んで頂ければと思います。

読んでほしい人

  • ユニファのQAに興味がある人
  • QAのものづくりってどういうことだろう?って思ってる人

ご注意

言葉ってふしぎなもので、使う人によって意味が全く違います。 あなたの「ものづくり」と私の「ものづくり」、たぶん意味が違う可能性の方が高いです。 ここでは私が考える「ものづくり」とはこういうことで、具体的にはこんなことしたんだな、と読んで頂けたら嬉しいです。

自己紹介

最初に少し自己紹介させてください。

2019年1月にユニファに入社しました。プライベートでは小学生と保育園児のママです。 QA歴は5年と少し。 ユニファでは、比較的規模が大きいプロジェクト内でのテスト設計と実行、探索的テストなどを担当しています

前職でのQA活動

ユニファ入社前は、ファッションECサイトを運営する会社で、主にコーディネートアプリやサイトのQAとして働いていました。

仕様書やデザインはほぼ確定していて、それをテストベースにテスト設計を行い、テストケース作成、テスト実行、不具合があれば報告、 修正されたものがあれば修正確認テストと、いうのが基本的な流れです。

開発とQAは拠点が違うため、基本的にやり取りはslack。

開発者さんは皆いい人ばかりで、定期的にMTGで顔を合わせてはいましたが、 サービスについてQAから気軽にアイディアを出したり、ということはあまりありませんでした。 (これは、QAとしてもっと頑張ればよかったなーという心残りもあります。)

ユニファでのQA活動

ユニファに入社後、ルクミーフォトのリニューアルプロジェクト(園管理画面)にアサインされました。 プロジェクトではスクラム開発を取り入れており、QAもテスト自体はスクラムの外で行いましたが スクラムイベントには参加していました。 私が「ものづくりにかかわれたなぁ」と感じたのは、次の3つです。

思い出① QA視点でデザインルールを変更してもらえた

f:id:unifa_tech:20191217222450p:plain
へっぽこデザインイメージ

ルクミーフォトの園管理画面では、ある情報を登録するためのコードを発行する画面があります。 いくつかの項目を選択し、「発行する」というボタンをクリックすると、上のような印刷画面に遷移するためのウィンドウがでます。 当初のデザインルールでは、上のようなウィンドウが表示された場合

  • ウィンドウ内の右上の✕ボタンをクリック
  • ウィンドウ外のグレー背景をクリック

すると、ウィンドウそのものを閉じるルールでした。

スプリントレビュー(ユニファではおさわり会と呼んでます。そこはかと漂うギリギリ感がすてき)で公開後、私が触ってみて感じたことは「グレー背景のとこ、うっかり結構クリックしちゃう」でした。

一応、仕様をしっているQAの私ですらうっかり結構クリック、そして閉じられるウィンドウ、すると項目数は少ないけどもう再度情報入力→クリックという作業を、園の先生に強いてしまう…

これ、ちょっと気になるなぁ…と、おずおず聞いてみたところ、なんとその場で「そうだねーたしかにー」となり、以降

ウィンドウ外のグレー背景をクリック

は撤廃されました。

え?

いいの?

まだ入社1か月の新参者で、みんながここまで作り上げてきた背景とか知らないんだけど、いいの?

とびっくりしたのを覚えています。

思い出② QAが開発者さんにアイディアだしたら採用された

f:id:unifa_tech:20191217222411p:plain
へっぽこ画面イメージ

ある日のデイリースクラム、開発者さんがちょっぴりお悩みをかかえていました。

ルクミーフォトの園管理画面では、ある設定①を登録する時にオプションを選ぶと、区分がちがう設定②も同時に登録することができます。

設定①と設定②は、区分が違う以外はほぼ同じ情報として扱われます。

開発者さんが悩んでいたのは、設定①と設定②を編集する時。DBの都合上、設定①と設定②が連動している構成にはなっていないが、片方を編集した時、もう片方にも反映させたいのです。

プロダクトオーナー、スクラムマスター、開発者さん、デザイナーさん、QA全員でしばしうんうん悩みました。

みんなが悩んでいるので、私も素人ながら頭をぞうきんしぼるような気持ちで考えました。

そして、ふと「登録が同じタイミングなので、IDは連番になるはず。設定①を編集時に、設定①の前後のIDかつ設定①の編集前の各項目で検索かけて、同じものがヒットしたらそれは設定②なので、同時に更新してはどうですか?」とあきれられちゃうかなーと思いながら、話してみました。

結果、開発者さんの方でこのアイディアを元に、もう少し手を加えて実装に採用してもらえることになりました。

採用決定時の開発者さんのおほめの言葉が、すごくつぼなのできいてください(笑)

「一番実現可能な小手先でどうにかする方法だった」

思い出③ QAが仕様書つくってステークホルダーあつめて仕様確定までしちゃったよ

ルクミーフォトのリニューアルプロジェクト、実装完了のバックログがたまってきて、QAもテストで忙しくなってきた2019夏。

新しく入社したテストマネジメントに強いチームメンバー(以下、野生のクマと呼称します)と私は少し困っていました。

なぜなら、リニューアルプロジェクトのテスト範囲のあるアプリの仕様書ができておらず、テスト設計に入れなかったからです。

リニューアルプロジェクトでは、スクラムマスター(ディレクター)が仕様書やデザインワイヤーを作成し、プロダクトオーナーや開発者さんと仕様を詰めるのですが

この時、スクラムマスター(ディレクター)は多忙で 、なかなかアプリの仕様書まで手が回っていない状況でした。

以前の私なら、「忙しいならしょうがない。できるまで待とう」と思っていましたが、この時はWACATE(詳しくはこちら)参加後でやる気が加速モードだったので

「ならQAで作っちゃおう!」となって、

  • 現行のアプリの仕様
  • リニューアル後のデザイン(ペイント)
  • リニューアル後の仕様
  • 未検討事項

などをまとめた仕様書を作ってしまいました。

f:id:unifa_tech:20191217222322p:plain
へっぽこ仕様書イメージ

ついでに、スクラムマスター、プロダクトオーナー、デザイナーさん、アプリの開発者さん、サーバサイドの開発者さんを集めて仕様確定MTGもセットして、そのMTGで仕様ほぼ確定させてもらえました。

まとめ

以上、私がユニファに入って「ものづくり」に関われたなぁと感じた思い出3つでした。

ユニファのQAって、品質をあげるための活動は何でもできるところが魅力だなと思います。

ながながとおつきあい頂き、ありがとうございました。

おまけ

ユニファのQA、もしくはユニファに興味のある方は、こんなイベントもやってます! ご興味あるかたはチェックしてみてください!

SwiftUIを使ってみた

こんにちは。iOSエンジニアのキムです。

今回はSwiftUIを使ってみた感想を書きたいと思います。

Xcode - SwiftUI - Apple Developer

SwiftUIが発表されてからしばらく経ちました。最初はすごい革新的で今までStoryboardやXibなどで開発していた時に抱えていた課題(XML分かりづらい、コンフリクト起きやすいのでチーム開発が大変など)を解決してくれるのではないかと思いましたが、新しい文法や技術に関する情報も少なくてなかなか手が出せませんでした。

しかし、今ではかなり情報も増えて、チュートリアルなどもたくさんあるので、私も軽く触ってみることにしました。

準備

SwiftUIを使うためには、macOSは10.15以上でXcode 11以上が必要です。macOS 10.14以下でも開発はできますが、Preview Canvas機能が使えないなど、SwiftUIが完璧にサポートされてないので、10.15以上がおすすめです。

処理内容

今回はSwiftUIのListを作ってみました。既存のUIKitのTableViewのようなものです。おそらくListは一番よく使われるのではないかと思います。 まずはリストで表示するモデルとテストデータを用意します。 

struct RainbowColor: Identifiable {
    var id: String = UUID().uuidString
    let name: String
    let color: Color
    
    init(name: String, color: Color) {
        self.name = name
        self.color = color
    }
}

struct RainbowColorList: Identifiable {
    var id: String = UUID().uuidString
    var rainbowColor: RainbowColor
    var description: String
    
    init(rainbowColor: RainbowColor, description: String) {
        self.rainbowColor = rainbowColor
        self.description = description
    }
}

// テストデータ
struct TestData {
    static let redColor = RainbowColor(name: "Red", color: Color.red)
    static let orangeColor = RainbowColor(name: "Orange", color: Color.orange)
    static let yellowColor = RainbowColor(name: "Yellow", color: Color.yellow)

    static func lists() -> [RainbowColorList] {
        let item1 = RainbowColorList(rainbowColor: redColor, description: "Red Color \nSome description space")
        let item2 = RainbowColorList(rainbowColor: orangeColor, description: "Orange Color \nSome description space")
        let item3 = RainbowColorList(rainbowColor: yellowColor, description: "Yellow Color \nSome description space")
        return [item1, item2, item3]
    }
}

次は、UITableViewCellに当たるRowを作成します。

struct ColorRow: View {
    
    let list: RainbowColorList
    
    var body: some View {
        VStack(alignment: .leading, spacing: 8){
            VStack(alignment: .leading) {
                HStack(spacing: 10) {
                    RoundColorView(color: list.rainbowColor.color, size: 50)
                    Text(list.rainbowColor.name).font(.headline)
                }
                Text(list.description).lineLimit(5).font(.body)
            }
            .padding(.leading, 32)
            .padding(.trailing, 32)
        }
        .padding(.top, 16)
        .padding(.bottom, 16)
    }
}

最後に上記で作ったものを一覧で表示するList画面です。

struct ContentView: View {
    let lists = TestData.lists()
    var body: some View {
        NavigationView {
            List {
                ForEach(lists) { list in
                    ColorRow(list: list)
                }
            }
            .padding(.leading, -20)
            .padding(.leading, -20)
            .navigationBarTitle(Text("Color List"))
        }
    }
}

イメージ

f:id:unifa_tech:20191204145737p:plain
List

感想

既存のUITableViewと比べるとかなりシンプルに書けました。 今までの開発ではStoryboardにUIViewControllerを作ってUITableViewを設置してUITableViewCellを設置してAutoLayoutの設定をする作業などが必要でした。また、SwiftファイルではUITableViewのDelegateを宣言したり、StoryboardのオブジェクトとのOutletの設定をするなど、色々と手間がかかりました。 SwiftUIではこれらが簡単に設定できてコーディングもシンプルになっているので開発にかかる時間も短縮されるのではないかと思いました。 ただ、UIをコードで作成するのでUIが複雑になる場合はコード全体を理解するまで時間もかかりそうだなと思いました。

最後に

今すぐSwiftUIが既存のUIKitを代替できるとは言えませんが、これからSwiftUIの進化が楽しみです。