読者です 読者をやめる 読者になる 読者になる

GEEK GARAGE iOS vol1 に行ってきた@秋葉原

AWS Goalist Xcode iOS swift デバッグ モバイルアプリ

こんちは。渡部です。
先日久しぶりiOSの勉強会に行ってきたのでレポートを書きたいと思います。

f:id:watabe1028:20170224152511j:plain

会場はMAMORIOさんのオフィス、GEEK GARAGE
会社から徒歩10分くらいなので非常に参加しやすい。

MAMORIOさんは
「なくすを、なくす」をモットーにしたIoT企業です。
最近また資金調達をして絶好調みたいです。

www.mamorio.jp

代表の増木さんとは以前、何度かお会いしてお話ししました。
勉強会とかやりたいねーとか言ってたら本当にやってくれました。


今回の勉強会はこちら。

geekgarage.connpass.com

すでにvol2の応募が始まってます。
興味ある方は是非!


勉強会のLTはこの3本。
・みんな使おう!AWS Device Farm
・ぼくがかんがえたさいきょうのIoTゲートウェイ(うろ覚え)
・EDIT BREAKPOINT


「みんな使おう!AWS Device Farm」

MAMORIO iOSエンジニア佐藤さん

AWS のモバイルアプリ試験用のサービス、
AWS Device Farm の使い方をデモしながら見せてくれました。

aws.amazon.com

最初の250分は無料だとか。
端末やOSを選択して、Xcodeでアーカイブして、テストコード作って、
AWSにアップすると勝手に試験をしてくれる。
しかも途中のスクリーンショットや動画も見れる。
これはUI試験のエビデンスにぴったりな気がする!

次のアプリのリリースの際はちょっと試してみようと思います。


「ぼくがかんがえたさいきょうのIoTゲートウェイ」(うろ覚え)

MAMORIO CTO高野さん

IoTでハードを含めた開発を本気でやってるとこって少ないよね。と嘆いてました。
IoTゲートウェイはMAMORIOにはすごく重要な役割なんだろうな。

GEEK GARAGE内にMAMORIOが1000個はあるらしく
この空間はBLEが入れ乱れてるらしい。。。

やっぱりサービスの全てを一企業で賄うのは大変で
協力しあえるパートナーは大切なんだなって思います。

こういったビジネスサイドの話もすごく勉強になる。
MAMORIO、まだまだ色々な展開ができそう。。。


「EDIT BREAKPOINT」

ORIGAMI iOSエンジニア玉城さん

iOSDCのパーカーを着て登場。
iOSDCに落選した身としてはすごく羨ましい。

iOSでも苦手と(思っている)される決済を提供しているORIGAMIさん。

origami.com

LTはこちらもテストについて。
デバッグする際のブレークポイントって編集できるんですと。
iOSの試験で嫌なとこは、初期起動の時だけチュートリアル見せる場合の試験は
いちいちアプリ入れ直したり、ビルドし直したりと超面倒。

でもEDIT BREAKPOINTでブレークポイントを編集すれば
ビルドし直さなくてもそのまま試験が続行できる。

これは便利。
テストコード書くの面倒だなって思ってたけどこれは便利。
設定だけで色々できる。

EDIT BREAKPOINTについては、実際にやって見て改めてブログに書こうと思います。

いい感じに紹介しているサイト

d.hatena.ne.jp


その後の懇親会

話題はそれとなくテストについて。

やはりiOSも端末が増え、OSのバージョンが増え
対応しなくてはならないことが非常に増えました。

そんなとき、みんなはどうやって解決しているのか?
そんな情報共有ができたいい勉強会でした。

ガレージの奥にはバーカウンターが新設されていて、
そこで増木さんやフリーランスの工藤さんと色々話せました。

工藤さんはフリーランスだけどフリーランスのチームを作って
ギルドみたいな組織を作りたいって言ってました。
勉強会も沢山主催していたり、とてもアグレッシブでした。
こういった人たちと話してると刺激になります。
俺、このままじゃまずい。もっと色々作りたい。ってなります。


まとめ

やっぱり勉強会はいいですね。
ゴーリストのエンジニア陣のモットーは「技術は手段」。
この手段で限りなく効率的で現実的なものをチョイスするために
幅広い知識が必要です。
知らなきゃ延々と遠回りしてしまうようなことも沢山あります。
「こんなのがあるよ!」「これ便利だよ!」と人の話を聞くのはとても勉強になります。
割とこのブログでは初心者向けの記事を書いてますが
これからはゴーリストで取り入れた技術的なことなんかも
書けていけたらな、と思います。
乞うご期待!

Angular2でFlexのViewStackを作ってみる

Angular2 TypeScript Flex

全国のFlexファンの皆様、お久しぶりです!
ゴーリスト開発の盛次(モリツグ)です。

前回のAngular2とFlexを比較してみたの影響で全国に90人はいると思われるFlex愛好者たちがAngular2に目覚めてくれたことだと思います。

今日は以下のような方に向けた内容になります。
・Angular2で動的にコンポーネントを追加したい方
・Angular2でもaddChildしてselectedIndexしたくて我慢できない方

ViewStackのデモを触ってみる

Flex経験者でない方にViewStackの説明が必要ですね!
ViewStackとはコンポーネントを格納するためのコンテナです、複数コンポーネントを格納できますが、表示するのは現在選択中のものだけです。

Angular2では標準で画面遷移の仕組みが用意されており、遷移時にServiceを差し込めたりと非常に便利です。
しかし、動的にaddChildしてselectedIndex=0したい人は少なくないと思います。
むしろFlex愛好家は「なんでViewStackないん?」と思っているはずです。
とりあえず完成品をご覧ください。ViewStackデモ

リンク先のplunkerでは以下のような画面が表示されます。

f:id:t-moritsugu:20170222175904p:plain

動作は以下のようになっています。
・ViewStackには初期化時に3つのコンポーネントを格納
・左側のボタン3つに対応したコンポーネントを追加
・selectで選んだコンポーネントだけを表示
・removeボタンを押すと選択中のコンポーネントを削除

コンポーネントを動的に追加する方法

  1. 動的に追加したいコンポーネントを@NgModuleのdeclarationsだけでなくentryComponentsにも追記する。
  2. ComponentFactoryResolverに追加したいコンポーネントを渡して目的のクラスのComponentFactoryを作成する。
  3. ComponentFactoryをViewContainerRef.createComponentの引数にする。

src/app.ts

@NgModule({
  declarations: [.....],
  imports: [....],
  bootstrap: [App ],
  // 動的に追加するコンポーネントはここに指定
  entryComponents: [ChildA, ChildB, ChildC]   
})
export class AppModule {}

src/app/viewstack/ViewStack.ts

@Component({
  selector: 'view-stack',
  providers: [],
  template: `
    <div #container>
    </div>
  `
})

export class ViewStack implements AfterViewInit {
  
  @ViewChild('container', {read: ViewContainerRef}) 
  private container:ViewContainerRef;
  
  public constructor(
    private _componentFactoryResolver: ComponentFactoryResolver
    //, private _viewContainerRef: ViewContainerRef
  )
  {}

  public addChild<T>(childClass: Type<T>, index:number=-1):void {
    
    const componentFactory:ComponentFactory<T>
      = this._componentFactoryResolver.resolveComponentFactory(childClass);

    const componentRef:ComponentRef<T> 
      = this.container.createComponent(componentFactory);
    
  }
}

ViewContainerRef.createComponentがどの要素に対して作成したコンポーネントを追加するのかが重要です。
このソースを見る限りthis.container.createComponentしているために以下のようなhtmlになりそうです。

<app>
  ...
  <view-stack>
    <div>
      <child-a>...
      <child-b>...
      <child-c>...
    </div>
  </view-stack>
  ...
</app>

しかし、実際は以下のようになります。

<app>
  ...
  <view-stack>
    <div>
    </div>
    <child-a>...
    <child-b>...
    <child-c>...
  </view-stack>
  ...
</app>

ViewContainerRef.createComponentは親の要素に作成したコンポーネントを追加するようです。
ダミー的に<div #container>を利用しているのはこのためです。
仮にconstructorでinjectしたViewContainerRefを利用した場合、以下のようなhtmlになります。

<app>
  ...
  <view-stack>
    <div>
    </div>
  </view-stack>
 <child-a>...
  <child-b>...
  <child-c>...
  ...
</app>

まとめ

addChildしてselectedIndexしたかったのでAngular2でViewStackを作ってみました。
*ngForと[ngSwitch]を利用すれば別の実装手段もありますが、一番疎結合なのはentryComponentsを使う今回の方法だと思います。
Flex経験者を取り込んでAngular2の人口が増えることを願って書いています。

S3とAPI GatewayとLambdaで動くHRogチャートの開発環境

AWS Amazon データ分析 ビッグデータ データ 開発環境 Lambda Jenkins Gradle API Gateway S3

f:id:suzutt:20170217000629p:plain

こんにちは、鈴木です。

弊社の開発環境はプロジェクト、サービスによってまちまちですが、今回は弊社サービス「HRogチャート」の開発環境についての話です。

HRogチャートとは、求人データを瞬時に分析するBIツールです。興味のある方は是非、下の画像をクリックしてください。

HRogチャートでは、本番環境以外に開発用の環境が2つありますが、 本記事では2の開発環境について扱っていきます。

  1. ローカル開発環境
    開発メンバーおのおの(今は自分ひとりですが)の端末に構築する開発環境です。
    ローカル開発環境で動作確認後、Githubリポジトリにプッシュします。

  2. 開発環境
    リポジトリ上の最新のコードをビルドしたものを配置している環境です。
    当たり前ですがローカル開発環境よりも本番に近いアーキテクチャなので、本番に近い状態で動作確認をします。チームメンバー間で話をするときにも使うかと思います。(今は自分ひとりですが)
    ステージング環境に近いですが、開発メンバーしか使わないのでステージング環境ではないです(と思っています)。

開発環境に最新のコードをデプロイする作業は、Jenkinsを使用して自動化しています。
目新しいことはやっていませんが、同じアーキテクチャを採用していて、これから同じことをやる方の参考になればと思います。

本番環境と開発環境

それぞれ以下のような構成になっています。

本番環境

f:id:suzutt:20170217000741p:plain

開発環境

f:id:suzutt:20170217000944p:plain

開発環境は本番環境と比べて、主に以下の点で異なります。

  • Amazon CloudFrontを使用せず、直接S3で静的ウェブサイトのホスティングをしている(IAMでIP制限を掛けています)
  • API Gateway のURLが異なる
  • API Gateway のAPIキーが異なる
  • API Gatewayから呼び出すLambdaファンクションが異なる

開発環境自動デプロイでは、ざっくりいうとJenkinsでリポジトリをポーリングし、リポジトリが更新されていた場合、S3、Lambda上にプログラムを自動ビルド、デプロイを行い、開発環境を最新化してます。

使用ツール

開発環境自動デプロイには、以下のツールを使用しています。

  • Gradle 3.3
  • Jenkins 2.7.4
  • AWS CLI

ビルド・デプロイ手順

API GatewayのURL、APIキーの変更

デプロイ前に下記の情報を開発環境用に設定します。

  • API Gateway のURL
  • API Gateway のAPIキー

フロントエンドのプログラムをS3へデプロイ

AWS CLI を使って対象のバケットをクリーンした後に、JenkinsのS3 Plugin(0.10.11)を利用してS3へデプロイしています。

S3 Plugin - Jenkins - Jenkins Wiki

設定を抜粋します。

ビルド - シェルの実行

aws s3 rm s3://${バケット名} --recursive

ビルド後の処理 - Publish artifacts to S3 Bucket

項目
Source build/libs/*.jar
Destination bucket ${バケット名}
Bucket Region ${リージョン名}

AWS Lambdaへのデプロイ

Gradleでビルド後に、S3 PluginでS3にデプロイ後、JenkinsのAWS Lambda Plugin(0.5.8)を利用し、Lambdaへデプロイしています。

AWS Lambda Plugin - Jenkins - Jenkins Wiki

こちらも設定を抜粋します。

ビルド - Invoke Gradle script

項目
Tasks build

ビルド後の処理 - Publish artifacts to S3 Bucket

項目
Source build/libs/*.jar
Destination bucket ${バケット名}
Bucket Region ${リージョン名}

ビルド後の処理 - AWS Lambda deployment

AWSコンソールから登録するときと同様、以下の例のように値を入力します。

項目
AWS Region ${リージョン名}
Function Name Lambda
Role ${Lambdaファンクションに割り当てるロール}
Artifact Location ${対象のjarファイルのS3上のパス}
Handler Name ${Lambdaファンクションのハンドラ名 }
Memory Size 1024
Timeout 180
Runtime java8

まとめ

開発環境があることで、常に最新のコードで動く環境で動作確認できるという安心感が得られてたということが大きいです。あとは開発メンバーが増えた時に効いてくるのかなと思っています。

ただ、ジョブが複雑になってくるとJenkinsだと管理が大変になるので、その際は別のCIサービスを使おうかなと考えています。

それでは。

iOSアプリ開発初心者にオススメする入門書

Goalist Objective-C Storyboard Xcode iOS swift モバイルアプリ 書籍

Hello,World。渡部です。
今回はタイトル通りiOSアプリ開発初心者にオススメの本を紹介します。

f:id:watabe1028:20170209151125j:plain

参考書とかたくさんあって何を読めばいいかわからない人の助けになればと思います。
ちなみに渡部はiOS関係の書籍を20冊くらい所有してます。
もちろんほとんど読んでないものから常にデスクに置いてある本まであります。

その中からこれだけは読んどけ&持っとけベスト3を紹介します。

第3位
iPhoneプログラミングUIKit詳解リファレンス

f:id:watabe1028:20170209152351j:plain

www.amazon.co.jp

UIKitのことならこの本を持っておけばまず問題ないのでは?と思わせる一冊。
最初の1冊はこれを買っておけば間違い無いでしょう。

第2位
本気ではじめるiPhoneアプリ作り Xcode 7.x+Swift 2.x対応 黒帯エンジニアがしっかり教える基本テクニック (ヤフー黒帯シリーズ)

f:id:watabe1028:20170209152401j:plain

www.amazon.co.jp

初心者向けだけど、Xcodeの使い方から丁寧に解説されているので
初心者以外にもオススメです。
このブログの記事の参考にもしています。
開発環境からリリースまでの手順も丁寧に説明されています。

第1位
よくわかるAuto Layout iOSレスポンシブデザインをマスター

f:id:watabe1028:20170209152411j:plain

www.amazon.co.jp

もはやリリースするためにはなくてはいけない画面設定AutoLayout。
しかしながらこのAutoLayoutが苦手なエンジニアは結構いるはず。
はい、わたしです。
Storyboardでやるにせよ、コードでやるにせよ知らないとできないことばかり。
この本では一つ、一つ説明してくれています。
ただ、Xcodeのアップデートで多少変わってしまう可能性があります。

まとめ

これ以外にもオススメな本は何冊かありますが
最初に手に取るのであればこの3冊を推薦します。
多分開発からリリースまではこの3冊があればなんとかなります。
ぜひ興味ある方は手にとってみてください。

「違う、これじゃない!」Amazon EC2のリザーブドインスタンスを誤って購入したときの対処法

リザーブドインスタンス AWS Amazon Goalist

f:id:suzutt:20170126152940j:plain

はい、やってしまいました。犯人は僕(鈴木)です。

弊社ではAWSをめちゃくちゃ使わせて貰っています、300台以上のEC2インスタンスが存在します。うち、30台くらいが常時起動です。

先々月、AWSのコスト削減をしようということで重い腰をあげて、まずは数台分のリザーブドインスタンスを購入しました。
しかし、あろうことか購入するインスタンスサイズを間違えてしまったので、意図したとおりに費用削減されていないことが発覚しました。 その際の対応方法をまとめたので、参考になればと思います。*1

ちなみに、本記事に登場するリザーブドインスタンスの仕様は以下の通りです。

  • 東京リージョン
  • 料金は全前払い
  • 1年契約
  • Standardクラス
  • アベイラビリティーゾーンの指定なし

まずは簡単にAmazon EC2と、リザーブドインスタンスあたりの用語の説明から。(知っている方は読み飛ばしてください。)

Amazon EC2とは

Amazon EC2とは、Amazon Elastic Compute Cloudの略で、Amazonが提供する仮想サーバです。
使用するサーバリソースを柔軟に変更することができ、使用するサーバリソースと使用時間に応じて料金が変わる仕組みです。

aws.amazon.com

ひとつひとつのサーバのことをインスタンスといい、インスタンスにはオンデマンドインスタンスとリザーブドインスタンス等があります。*2

オンデマンドインスタンスとリザーブドインスタンス

オンデマンドインスタンスは、起動時間に対してお金を払います。つまり時間に対する従量課金です。一番ベーシックな使い方かと思います。

リザーブドインスタンスは、簡単に言うと長期間使用することを前提に、オンデマンドインスタンスよりも安い料金で使用することができるインスタンスです。弊社では、常時起動予定のインスンタンス4つに対して、1年間分、全前払いで購入しました。

Amazon EC2 リザーブドインスタンス

インスタンスタイプとインスタンスサイズ

インスタンスの性能は、そのインスタンスがどのインスタンスタイプであるかによって決まります。 例えば、m3.largeというインスタンスタイプの性能は下記の通りです。largeの部分をインスタンスサイズと呼ぶようです。

インスタンスタイプ vCPU ECU メモリ インスタンスストレージ 年間料金(オンデマンド) 年間料金(リザーブド)
m3.large 2 6.5 7.5 1 x 32 SSD $1,690.68 $950.00

詳しくはこちらを。

docs.aws.amazon.com

対応手順

お待たせしました。ここから本題です。

1. リザーブドインスタンス適用状況を確認する

まず、購入したリザーブドインスタンスが正常に使われているかを確かめました。

EC2 マネジメントコンソール>レポート>EC2 リザーブドインスタンスの使用状況 から確認できます。

https://console.aws.amazon.com/ec2-reports/?breadCrumb=EC2Console#ReservedInstanceUtilization:

すると、今回の対象は常時起動のインスタンスなので本来100%になっているはずが、そうではないことに気づきます。5%しか適用されていないものもあります。
(とはいえ、オンデマンドインスタンスに比べると、$100程度は安くなっていることがわかります。)

f:id:suzutt:20170126170036p:plain

2. 適用されていない原因を調査する

以下の順に原因を切り分けました。

  1. 対象のインスタンスを起動していない
  2. 対象のインスタンスとリザーブドインスタンスで、リージョンが異なる
  3. 対象のインスタンスとリザーブドインスタンスで、アベイラビリティーゾーンが異なる
  4. 対象のインスタンスとリザーブドインスタンスで、インスタンスタイプが異なる

今回の場合は、a~cは問題なく、dが問題であることがわかりました。
a~cが原因であればリザーブドインスタンスのパラメータを変更すれば容易に対応できるのですが、今回はそうはいかないようです。

3. 変更可能であれば、対象のインスタンスのインスタンスタイプを購入したリザーブドインスタンスと同じインスタンスタイプに変更する

ちなみに、今回は対象のインスタンスは弊社の基幹となるサーバなので、インスタンスタイプを変更することは難しいことがわかりました。
なので変更はおこなっていません。

4. 適用されていないリザーブドインスタンスのインスタンスクラスを調べる

調べたところ、リザーブドインスタンスの提供クラスによっては変更、交換ができるようなので、これができるかを検討してみました。

どうやら

のようです。

今回購入したリザーブドインスタンスは、、、

「Standard」*3

インスタンスサイズしか変更できないようです。

5. 変更可能であれば、リザーブドインスタンスのインスタンスサイズを変更する

インスタンスサイズを変更できる条件は下記サイトに載っていました。

docs.aws.amazon.com

5.1. インスタンスサイズの大きいリザーブドインスタンスを購入してしまった場合

例えば、以下のケースです。

c3.xlargeではなく、c3.2xlarge を購入していた

c3.2xlargeを2つの c3.xlarge に分割できることがわかったので欲しかった c3.xlarge を手に入れることはできます。 ただし、1つの c3.xlarge が余ってしまいますので、手順6を参照してください。

5.2. インスタンスサイズの小さいリザーブドインスタンスを購入してしまった場合

例えば、以下のケースです。

(1) m3.2xlargeではなく、m3.xlargeを購入していた

m3.xlargeをもう一つ購入することでm3.2xlargeとして使えるかと思いましたが、以下の記載の通り、追加購入すると終了日時が一致しないため変更は無理なようです。

インスタンスサイズの変更は、他の属性詳細 (リージョン、使用タイプ、テナンシー、プラットフォーム、終了日時など) が一致し、利用可能な容量がある場合にのみ許可されることに注意してください。

こちらも手順6を参照してください。

6. 常時起動しているインスタンスを調べる

常時起動しているインスタンスの中に購入したリザーブドインスタンスと同じインスタンスタイプのものがあればめでたく解決なのですが、おそらく「1. リザーブドインスタンス適用状況を確認する」で適用されていないことを確認した時点で多分ないんだろうと思います。

その場合、常時起動しているインスタンスの中に購入したリザーブドインスタンスと性能の近いインスタンスがあれば、リザーブドインスタンスタイプに合わせて変更してしまうのが良いかと思います。

例えば、c3.xlargeのリザーブドインスタンスが余っている場合、常時起動のm3.largeのインスタンスをc3.xlargeに変更すれば、スペックを落とさずに料金を安くすることができます。 この場合、年額$200弱のコストを削減できます。

インスタンスタイプ vCPU ECU メモリ インスタンスストレージ 年間料金(オンデマンド) 年間料金(リザーブド)
m3.large 2 6.5 7.5 1 x 32 SSD $1,690.68 $950.00
c3.xlarge 4 14 7.5 2 x 40 SSD $2,233.80 $1,505.00

まとめ

Amazon EC2のリザーブドインスタンスを誤って購入したときの対処法について説明しました。 いろいろ書きましたが、「3. 対象のインスタンスのインスタンスタイプが変更可能であれば変更する」の時点でAWSのサポートの方に問い合わせるのが一番いいです。最適なリカバリー方法を教えてくれるかと思います。

誤ったものを購入すると面倒くさいことになってしまうのでみなさんもお気をつけください、という注意喚起の記事でした。それでは。

*1:最後にも書きましたが、AWSのサポートの方に問い合わせるのが一番いいです。最適なリカバリー方法を教えてくれるかと思います。

*2:スポットインスタンスなんてのもありますが、よく知らないことと本記事には関係ないので省略します。

*3:3年契約のもののみCovertibleクラスが提供されているようです。今回は、1年契約のため、これを選択することはできませんでした。

Debug View Hierarchy でViewの階層構造を確認する

Goalist Objective-C Storyboard iOS swift アイドル モバイルアプリ デバッグ Xcode

ちわっす。
ブログ書いてるメンバーでもっとPV上げたいね!とか話してるのに
かなりマイナーなネタをここぞとばかりに書く渡部です。

f:id:watabe1028:20170126160206j:plain

画像は特に意味はありません。釣りです。
釣られた人残念。

今日はアプリ開発時のデバッグ時に便利なTipsを紹介します。

「Debug View Hierarchy」をご存知でしょうか?
画面のレイアウトの崩れなどを視覚的に確認できるとっても便利な機能です。

UITableViewやUICollectionViewなど、AutoLayoutや複雑な画面のレイヤの確認にもってこいです。
今回は「Debug View Hierarchy」の使い方を書いていきます。

手順

1.「Debug View Hierarchy」の起動
2.気になるオブジェクトを選択
3.オブジェクトの確認、修正

1.「Debug View Hierarchy」の起動

では、サンプル代わりに以前作ったログインサンプルを実行します。
実行すると普通に画面が表示されます。

f:id:watabe1028:20170126160451p:plain

そしてXcodeのこのボタンを押します。

f:id:watabe1028:20170126160508p:plain

本来はブレークポイントを貼ってレイヤを順番に確認することをお勧めしてます。
詳しくはこちら

この画面はピンチイン、アウトで拡大縮小し直感的に使えるはずです。

f:id:watabe1028:20170126160523p:plain

2.気になるオブジェクトを選択

今回はわかりやすくログインボタンを選択します。
選択すると右側にユーティリティエリアにオブジェクトの情報が表示されます。

f:id:watabe1028:20170126160546p:plain

3.オブジェクトの確認、修正

右側のユーティリティエリアでは以下のような項目が確認できます。

Class Name クラスの名前。今回はUIButton。
Address メモリ上のアドレス。

今回はUIButtonなので「State」「Title」「Font」「Text Color」なのでが確認できます。

f:id:watabe1028:20170126160607p:plain

まとめ

画面でつまづいたら「Debug View Hierarchy」を起動しましょう。
ユーザからしたら目に見えるものが全てなので、画面の崩れは致命的です。
なんでオブジェクトがずれるんや?と思ったらレイヤの階層を確認してみましょう。

またもやノンコーディングのネタを書いてしまいました。
面倒くさいわけじゃないんです!
ただただ楽をしたいんです!(冗談)
来週は新メンバーの初投稿があるので乞うご期待!

ささっとカメラアプリを作ってみた@Swift

Goalist Objective-C Storyboard iOS swift モバイルアプリ camera フリー素材 アイドル

コンニチハ。iOSエンジニアの渡部です。
iPhone8の情報がちょいちょい出てきましたね。
そろそろAppleの社員がバーにiPhoneを置き忘れていく時期です。
何それ?な人はググってみてください。

f:id:watabe1028:20170119152445j:plain

画像はフリー素材アイドルさんをお借りしました。

iPhoneユーザで誰もが使う機能、カメラ。
snowのような顔認識やフィルター機能はなかなかハードルが高いですが
カメラ機能を実装するのは結構簡単です。

今日はカメラアプリを作ってみます。

写真を撮って保存するシンプルなカメラです。

手順

1.info.plistでカメラが使えるように設定する
2.必要なオブジェクトを設置
3.ソースをガリガリ

カメラアプリの実行、デバッグは実機でしかできないので注意です。
ソースをガリガリといってもだいたいコピペでできますのでご安心を。
自分はファミマのミニクッキーを食べながら失礼します。

手順にプロジェクト作成は含めません。
今回もSingle View Applicationで作成します。

1.info.plistでカメラが使えるように設定する

これはそのままです。 スクショ通りに追加してください。

f:id:watabe1028:20170119152918p:plain

右側のテキストはカメラ許可を取る際に表示するメッセージです。
ここは好きな文言を設定してください。

2.必要なオブジェクトを設置

Storyboardで設置していきます。

f:id:watabe1028:20170119153153p:plain

UIViewとUIButtonを画像の通りに設定します。
UIViewはわかりやすいようにlightgrayにしてあります。

UIViewをOutletで、UIButtonをActionでソースに紐づけます。

3.ソースをガリガリ

あとはガリガリ書きます。

まずはimport、Outlet、Actionの追加です。
デリゲートの追加も忘れずに。

import UIKit
import AVFoundation

class ViewController: UIViewController, AVCapturePhotoCaptureDelegate {
    
    @IBOutlet weak var cameraView: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    /// 撮影ボタン押下時に呼ばれる
    @IBAction func cameraButtonTapped(_ sender: Any) {
    }

カメラ起動をviewDidLoadに記述します。
本当はviewDidAppearの方が適切だと思われますが今回は(面倒なので)おけーです。
viewDidLoadの前にAVCaptureSessionたちも追加します。
これがないと起動しません。

    var captureSesssion: AVCaptureSession!
    var stillImageOutput: AVCapturePhotoOutput?
    var previewLayer: AVCaptureVideoPreviewLayer?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        captureSesssion = AVCaptureSession()
        stillImageOutput = AVCapturePhotoOutput()
        
        // 解像度の設定
        captureSesssion.sessionPreset = AVCaptureSessionPreset1920x1080
        
        let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
        
        do {
            let input = try AVCaptureDeviceInput(device: device)
            
            // 入力
            if (captureSesssion.canAddInput(input)) {
                captureSesssion.addInput(input)
                
                // 出力
                if (captureSesssion.canAddOutput(stillImageOutput)) {
                    
                    // カメラ起動
                    captureSesssion.addOutput(stillImageOutput)
                    captureSesssion.startRunning()
                    
                    // アスペクト比、カメラの向き(縦)
                    previewLayer = AVCaptureVideoPreviewLayer(session: captureSesssion)
                    previewLayer?.videoGravity = AVLayerVideoGravityResizeAspect // アスペクトフィット
                    previewLayer?.connection.videoOrientation = AVCaptureVideoOrientation.portrait
                    
                    cameraView.layer.addSublayer(previewLayer!)
                    
                    // ビューのサイズの調整
                    previewLayer?.position = CGPoint(x: self.cameraView.frame.width / 2, y: self.cameraView.frame.height / 2)
                    previewLayer?.bounds = cameraView.frame
                }
            }
        }
        catch {
            print(error)
        }
    }

撮影ボタンです。
ボタン押下で撮影します。

     /// 撮影ボタン押下時に呼ばれる
    @IBAction func cameraButtonTapped(_ sender: Any) {
        
        // カメラの設定
        let settingsForMonitoring = AVCapturePhotoSettings()
        settingsForMonitoring.flashMode = .auto
        settingsForMonitoring.isAutoStillImageStabilizationEnabled = true
        settingsForMonitoring.isHighResolutionPhotoEnabled = false
        
        // 撮影
        stillImageOutput?.capturePhoto(with: settingsForMonitoring, delegate: self)
    }

最後に写真の保存です。
今回はベタにJPEG形式で保存してます。

    /// カメラで撮影完了時にフォトライブラリに保存
    func capture(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?, previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?) {
        
        if let photoSampleBuffer = photoSampleBuffer {
            
            // JPEG形式で画像データを取得
            let photoData = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: photoSampleBuffer, previewPhotoSampleBuffer: previewPhotoSampleBuffer)
            
            let image = UIImage(data: photoData!)
            
            // フォトライブラリに保存
            UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
        }
    }

少しソースが多いですがまるっとコピペでちゃんと動きます。

起動すると・・・ f:id:watabe1028:20170119155143p:plain

先ほど「1.info.plistでカメラが使えるように設定する」で設定したメッセージが表示されます。 もちろん許可してください。

画面はこんな感じ。 f:id:watabe1028:20170119155209p:plain

撮影すると保存の許可を求めるメッセージが表示されます。 これも「1.info.plistでカメラが使えるように設定する」で設定したものです。 f:id:watabe1028:20170119155317p:plain

ちゃんと保存されてます。 f:id:watabe1028:20170119155343j:plain

まとめ

今回はカメラの起動から写真の保存まででしたが
売れるアプリにするには一工夫ないとダメです。
いずれ顔認識や色々なフィルターの記事も書こうと思います。

今まさにプライベートでカメラアプリを作っていたので
いい復習になりました。
ファミマのお菓子最高ですね。では。