サンフランシスコで転職活動をしました

f:id:jeffsuke:20151127023424j:plain

要約

サンフランシスコで hired.com を主に用い、ニューヨークの会社から、転職しました。RedditHere’s How to Prepare for Tech Interviewsという記事を参考に、技術面接への準備をし、大小様々な企業20社程を受けて、自分のミッションとマッチする会社を見つけました。

背景

ニューヨークにある会社で働いていたのですが、諸々の事情があり転職活動をすることになりました。*1

アメリカでの一般的なソフトウェアエンジニア採用プロセス

アメリカでの一般的な採用プロセスは以下の通りです。

  1. レジュメのスクリーニング
  2. 人事との電話面談
  3. 技術者との電話面接(1~2回)
  4. オンサイト面接

人事との電話面談は、志望動機や過去の経験を聞かれます。会社の求める人材との最低限のマッチングを確認している場合が多いです。 技術者との電話面接は、電話で会話しながらオンラインのコーディングツール等を使って問題を解く場合がほとんどです。4~8時間で終わるような宿題が出される場合もあります。

オンサイト面接は、実際にオフィスに出向きフェイストゥーフェイスで行う面接です。4~6時間と長丁場であることがほとんどで、一般的には3回のホワイトボードコーディング、1回の一般的な面接が行われます。

採用までの道のり

アメリカでの採用活動と日本の採用活動は、外国人であることを差し引いても、私の肌感覚からすると大きく違うものでした。常に競争の激しさにさらされると感じました。ただ、ちゃんと準備をして挑めば不可能ではないということもわかりました。

就労ビザ(H-1B)や、グリーンカードを持っていないと、よほどのスタープレイヤーでないと面接すらしてくれないことがほとんどです。

約一ヶ月半の転職活動

実際に転職活動を始めてから仕事が決まるまでは、約一ヶ月半から二ヶ月でした。

* 1週目:応募
* 2週目〜5週目:人事面接+技術面接
* 6週目〜8週目:オンサイトインタビュー

転職活動自体にとにかく時間がかかりました。また、創業間もない小さい会社を除き、人事が時間外に面接をしてくれることは非常に稀なので、スケジューリングがとても難しかったです。私は、 Calendly を使い相手企業側に時間を選んでもらうことで解決しました。

応募編

まずは、レジュメの準備からです。レジュメとCVは違うらしいです。レジュメは一般的には1ページで収まる自分の経歴のまとめで、CVは数ページにわたる経歴のようで、長いものがCVという説明も目にしました。人によっては、レジュメを会社によって書き換えるそうですが、iOSエンジニア職にしか応募しなかったので私は同じものを使いました。

次にポジションに応募します。

最も効果が高かったのが、知人の紹介です。日本でもそうですがリファレンスがあると、書類選考を飛ばせるので先に進める可能性が一気に高まります。 次に効果があったのは、hired.com というスカウト型のテック業界向け求人サイトです。自分の基本情報を入力すると、興味を持ってくれた企業から声をかけてもらえる仕組みになっています。ただし、グリーンカード就労ビザといった、アメリカで就労できる権利を持っていることが条件になっていることが多いので注意が必要です。

この hired.com で会社は、居住地域をもとに、人材を探すそうです。引越しを考えている人には注意が必要です。 最も効果がなかったのが、企業のWebページからの応募です。正直に書くと、この方法では一社も選考を進めることができませんでした。

人事面接

書類選考を通過した後の志望動機などの基本情報を確認する場です。これは電話で行われます。

大体の場合以下のテンプレートで会話が始まります。

人事 「こんにちわ、〜〜さんですか?」

私「はい」

人事「調子はどうですか」

私「いい感じですよ。そちらはどうですか。」

人事「はい、良いです。今お時間大丈夫ですか?」

私「もちろんです。」

人事「では、あなたについてお話ししてください。」

私「はい。私は。。。」

日本語にするといたって普通の会話なのですが、日頃人と会話をしないエンジニアである私は慣れるまで時間がかかりました。過去のプロジェクトから使えるネタを数個用意し、数分で話す練習を繰り返しました。また、3分くらいの自己紹介は、面接を繰り返していくうちにアップデートされていきました。

技術面接

技術者としての力が試される技術面接です。基本的には、イントロとしてiOS の基本知識を10分程度確認し、40分ほどコンピュータサイエンス(CS)関連の問題を解き、5分のQandAセッションがあります。

特に難しいのが、CS関連の質問。データ構造、アルゴリズムについての問題を出されて、Google Docsのようなオンラインのテキストエディタを使ってコーディングします。オートコレクションはなく、シンタックスハイライトもない場合があるので、実際にプレーンテキストエディタを使って練習するのがいいです。 RedditHere’s How to Prepare for Tech Interviewsという記事が準備をする上で必要な内容をリストアップしていて便利でした。ここにリストアップしている内容について最低限理解している、できれば実装もできる状態になっているのが理想的です。

学習に関しては、 CoderustCracking the Coding Interviewを使いました。私はデータ構造やアルゴリズムの勉強を本格的にしたことがなかったので。Coderustの良いところはビジュアルでアルゴリズムを説明してくれている点です。類似サービスに、 Interview Cake もありますが、お値段が高めです(さらに個人的にはCoderustの方が好きだった)。

練習に最適なのは、LeetCode Online Judge。これは、コーディング問題がリストアップされているオンラインフォーラムで、出題された有名企業がタグされていたりもします。実際にコードを実行できるので、便利でした(swiftにも対応)

オンサイトインタビュー

会社にもよりますが、約4時間の技術面接とランチ面接が基本です。問題の難易度は、電話技術面接と大差ないことが多いです。一番の違いは、ホワイトボードを使うこと。簡単に修正ができないので、ボトムアップではなくトップダウンでコーディングをしていく必要があります。

ここら辺のテクニックに関しては、 Cracking the Coding Interviewや著者の講演等を聞いて対策を練りました。私は基本的に、疑似コードからはじめ、メソッドを定義、面接官と会話をしながら必要に応じてメソッドの内部を実装するという方法をとりました。

感想とか

スタートアップのメッカと言われるだけあって、サンフランシスコは革新的なことに取り組んでいる人や会社に出会える可能性は高いなと感じました。世界中から優秀な人材が集まっているので、その競争の激しさを肌で感じることもできます。

仕事探しやカルチャーフィットという意味では、言語や環境の違いはあれど日本での転職活動と変わらないと感じています。不思議と、「いいな」と思う会社の選考は順調に進み、「何かイマイチだな」と思う会社は最終面接までほとんど行きませんでした。

アメリカで働くには、競争の激しさのみならず、就労ビザという高い壁があります。エンジニアとして世界の最前線で働く選択肢などに詳しく書かれていますが、基本的にビザがないとアメリカで転職するのはハードルが高いです。

個人的には、アメリカである必要はないけど海外で働きたいという人にはベルリンをお勧めしたいです。技術者は比較的ビザが出やすかったり、物価もアメリカと比べてとても安く、住み心地も抜群です。ベルリンでの転職活動についてはベルリンにて転職しました。 - zakisan’s blogが良かったです。

その他メモ

  • とある大会社でシニアエンジニアのポジションに応募したら、5年以上の経験がないとシニアと認識できないと言われた。
  • 紹介された中規模のスタートアップに二度も予定をドタキャンされた。
  • 感覚として、シードラウンドのスタートアップ、シリーズAのスタートアップ、急成長中のスタートアップ、大企業の4カテゴリで雰囲気が大きく変わる。急成長中のスタートアップは、人材の質にばらつきがあったりと、面接中にも成長の痛みが垣間見えることがあり辛い。
  • 今注目のテキサス州Austinの企業にも応募したんですが、イノベーティブな会社はサンフランシスコやニューヨークに多い印象です。Austinは住みやすくて、良い街なんですけどね。
  • 英語力に関しては、国際企業で英語を主言語として働き、エンジニアとして口頭の議論ができていれば問題はないと思います。それよりも、普段使わないようなコンピューターサイエンスの基本知識が私はネックになりました。
  • アメリカでの転職を考えている人は、エンジニアとして世界の最前線で働く選択肢という書籍がオススメです。より包括的に書かれています。

まとめ

一ヶ月半普通に働きながら職探しをしていたので、疲れたというのが正直な感想です。

国内に時差があるので、基本的にニューヨークに滞在しながら、サンフランシスコやオースティンの会社と面接するだけでも割と大変でした。

国外で働いてみることは、新しい価値観に触れる一番の機会だと思っています。また、現地の企業で働くことは、日系企業の海外支社で働くのとは大きく違うと思っています。どちらも良い点悪い点があるので、どちらの意見も聞いてみるのが良いと思います。

そして、海外で働くという意味では、ベルリンなどの移民受入れが盛んな地域の方が個人的にはオススメです。

*1:私は婚約者がアメリカにいるのでアメリカに残りたかったのですが、前職のエンジニアリングチームが本格的にベルリンにオフィスを移転することに決定したため。

CallKitを用いたSystem Calling Screenの実装

背景

WWDC16でVoIPアプリでもiOSネイティブのUIを使えるようになりました。これまでは、Push通知からユーザーにアプリを開いてもらう必要がありましたが、サードパーティ製アプリでもネイティブアプリのUIを開けるようになったことで、より一貫した体験を提供でき通知にも気づきやすくなるメリットがあります。

個人プロジェクトでいろいろ試してみたので、まとめようと思います。やること自体はシンプルなのですが、私は証明書関連でつまずきました。

やることざっくりまとめ

  1. Amazon SNSを設定する
  2. アプリ側の初期設定。PKPushRegistryを用いて、VoIPプッシュ通知の初期設定を行う
  3. Amazon SNSから、VoIPプッシュ通知を送る
  4. CallKitを用いてネイティブ通話画面を表示

Amazon SNSを用いて、VoIPプッシュ通知を受け取る

Amazon SNSを設定する

ネイティブ通話画面を表示するためには、アプリをバックグラウンドで起動する必要があります。そのためにVoIPプッシュ通知を送る準備をする必要があります。

VoIP push notificationを用いると、アプリがバックグラウンドで起動されることが保証され、バックグラウンドで起動している場合でも処理を実行するための時間が確保されています。私はこの点に気づかず、通常のプッシュ通知やローカル通知で実装できないか試行錯誤していました。 より詳しい違いについては、VoIPプッシュ通知(PushKit)と標準プッシュ通知の違いについてがとても参考になります。

Parseや、Firebaseは実装時(2016年8月頃)には、VoIP プッシュ通知をサポートしていなかったため、AWSのプッシュ通知サービスを使うことにしました。

基本的に、通常のプッシュ通知をSNSから送る場合と、オプションと証明書が変わるだけで、手順は変わりません。

まずは、証明書の作成です。通常のプッシュ通知と同様にApple Developerウェブサイトから、証明書を作成することができます。Apple push notification service SSLではなく、VoIP Service Certificateを選択し、証明書を取得し、P12ファイルを書き出します。iOSプッシュ通知用証明書の更新方法が参考になります。

次に、AWS管理画面から、SNSを選択します。

f:id:jeffsuke:20160930165152p:plain

次に、Applicationを選択し、Create platform applicationからアプリケーションを作成します。

f:id:jeffsuke:20160930164451p:plain

表示された、プラットフォーム設定画面にて、必要な情報を入力します。

  • Application name: 任意のアプリ名
  • Push notification platform: Apple development もしくはApple production
  • Push certificate type: VoIP push certificate
  • Chose P12 file: Apple Developerウェブサイトで作成したVoIPプッシュ通知用P12ファイルを選択
  • Enter Password: P12ファイル作成時のパスワード

SNS側の設定は以上で完了です。*1

VoIPプッシュ通知を受け取るための、アプリ側での設定

次に、アプリ側のプロジェクト設定をします。プロジェクトファイルを選択し、VoIPプッシュ通知を受け取りたいターゲットの、Capabilityから、Background Modeを有効にします。Voice over IPと、Backgrond fetchを選択し、VoIPプッシュ通知を受け取り、任意の処理を実行できるようにします。*2

f:id:jeffsuke:20161001181637p:plain

次に、PKPushRegistryを用いて、デバイストークンを取得します。以下のように、PKPushRegistryインスタンスに、VoIPプッシュ通知を受け取るように指定します。デリゲートで指定したインスタンスが、VoIPプッシュ通知関連のコールバックを受け取ります。

#import <PushKit/PushKit.h>

@interface JSKPushKitManager()<PKPushRegistryDelegate>
@property (nonatomic, strong) PKPushRegistry* voipRegistry;
@end

@implementation JSKPushKitManager

-(void)setupPushKit {
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    self.voipRegistry = [[PKPushRegistry alloc] initWithQueue: mainQueue];
    self.voipRegistry.delegate = self;
    self.voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
}

この、setupPushKitAppDelegate内で呼び、PKPushRegistryのコールバックを受け取れるようにします。

-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [self setupPushKit];
}

デバイストークンを取得するために、JSKPushKitManagerに戻り、PKPushRegistryのデリゲートメソッドを実装します。

// トークンが更新されたときに呼ばれる。
-(void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type {
    NSString *tokenString = [self tokenStringWithData:credentials.token];
    NSLog(@"Token String: %@", tokenString);
    // トークンをサーバーに送る処理
}

-(NSString *)tokenStringWithData:(NSData *)data {
    const unsigned *tokenBytes = [data bytes];
    NSString *hexToken = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x",
                          ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),
                          ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),
                          ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];
    
    return hexToken;
}

今回は、テストのために、NSLog()で出力したデバイスにユニークなトークンを手動でSNSで登録しますが、通常はサーバーに送信し処理を実行するのが良いと思います。*3

VoIPプッシュ通知を受け取ると以下のデリゲートメソッドが呼ばれます。

// VoIPプッシュ通知を受け取ったとき
-(void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type {
    NSLog(@"Received a VoIP push notification.");
}

Amazon SNSからVoIPプッシュ通知を送る

取得したデバイストークンを、テストのためにSNSに登録します。SNS管理画面から、作成したApplicationを選択し、Create platform endpointからデバイストークンを追加します。デバイストークンが追加されると、各デバイスのEndpoint ARNが作成され、各端末に配信する際にこの値を指定します。

f:id:jeffsuke:20161001184519p:plain 

デバイストークン登録画面では、Device Tokenの他にUser Dataを入力することができます。ユーザーID等を登録することで、サーバーから任意の端末にプッシュ通知を送れるようになります。

さて、実際にVoIPプッシュ通知を送信してみましょう。送信したい端末にチェックを入れ、Publish to EndpoitからVoIPプッシュ通知を送ることができます。iOS側の設定が完了していれば、VoIPプッシュ通知が届いたログが見れるはずです。

CallKitを用いてネイティブ通話画面を表示

VoIPプッシュ通知が送れるようになれば、あとはネイティブ通話画面を表示するだけです。 登場するクラスは以下の通りです。

  • CXProvider:通話画面を呼び出すクラス
  • CXProviderConfiguration: アプリ固有の通話画面共通の設定
  • CXCallUpdate: 受け取った通話に関する情報

JSKSystemCallProviderというクラスに、通話に関する機能を実装していきます。

@interface JSKSystemCallProvider() <CXProviderDelegate>
@property (nonatomic, strong) CXProvider *provider;
@property (nonatomic, strong) CXProviderConfiguration *configuration;
@end

@implementation JSKSystemCallProvider

-(instancetype)init {
    self = [super init];
    if (self) {
        _configuration = [self newConfiguration];
        _provider = [[CXProvider alloc] initWithConfiguration:_configuration];
        [_provider setDelegate:self queue:nil];        
    }
    
    return self;
}

-(CXProviderConfiguration *)newConfiguration {
    CXProviderConfiguration *configuration = [[ CXProviderConfiguration alloc] initWithLocalizedName:@"Sample Phone Call"];
    configuration.supportedHandleTypes = [NSSet setWithObject:@(CXHandleTypePhoneNumber)];
    return configuration;
}

CXProviderCXProviderConfigurationを用いて初期化します。CXProviderConfigurationでは、着信音や、アプリ表示名、アプリアイコンの指定などができます。

通話開始

通話を開始するには、CXProvider- (void)reportNewIncomingCallWithUUID:(NSUUID *)UUID update:(CXCallUpdate *)update completion:(void (^)(NSError *_Nullable error))completion;を呼びます。

-(void)reportIncomingCallWithHandle:(NSString *)handle
                            success:(void (^)())success
                            failure:(void (^)(NSError * error))failure {
    CXCallUpdate *update = [self newCallUpdateWithHandle:handle];
    self.callId = [NSUUID UUID];

    [self.provider reportNewIncomingCallWithUUID:self.callId update:update completion:^(NSError * _Nullable error) {
        if (error) {
            if (failure) failure(error);
        } else {
            if (success) {
                success();
            }
        }
    }];
}

ユーザーが、通話開始ボタンを押すと、success()が呼ばれます。VoIP通話を開始する処理をBlocksとして渡し、通話を開始させます。

この時、CXCallUpdateインスタンスを用いて通話に関する情報を渡しています。このクラスでは、動画をサポートするか、グループコールをサポートするか等の情報を付与することが出来ます。

-(CXCallUpdate *)newCallUpdateWithHandle:(NSString *)handle {
    CXCallUpdate *update = [[CXCallUpdate alloc] init];
    update.remoteHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:handle];
    update.supportsGrouping = NO;  // グループコールサポートしない
    update.supportsHolding = NO;  // 通話のホールド不可
    return update;
}

通話の終了

通話を開始してから、アプリを開くことができるため、通話終了処理は、アプリから終了した場合と、ネイティブ通話画面から終了した場合と両方考慮する必要があります。

アプリから通話を終了する場合は、以下のようにCXProvider- (void)reportCallWithUUID:(NSUUID *)UUID endedAtDate:(nullable NSDate *)dateEnded reason:(CXCallEndedReason)endedReason;を用いて、ネイティブ通話画面を終了させます。

-(void)reportCallDidEnd {
    NSDate *callEndDate = [NSDate date];
    [self.provider reportCallWithUUID:self.callId endedAtDate:callEndDate reason:CXCallEndedReasonRemoteEnded];
}

ネイティブ通話画面から通話を終了する場合は、以下のデリゲートメソッドが呼ばれます。

-(void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *)action {
    // 通話終了の処理
    [action fulfill];
}

[action fulfill];を呼ぶことで、ネイティブ通話画面での表示も通話終了処理が完了した状態になります。

ネイティブ通話画面を使う上での注意点

画面ロック時と、非ロック時の挙動が違う。

画面ロック時は、ユーザー側の視点だと、通話開始ボタンを押すと、通話中画面になり通話が開始します。バックグラウンドでは、アプリが起動し通話に必要な処理を実行しています。画面非ロック時には、ユーザーが通話開始ボタンを押すとアプリが直接起動されます。つまり、通話開始によってロックが解除されるわけではないので、ユーザーがロック解除するまでアプリ画面を提供することができません。

そのため、現時点ではパーミッション取得のアラートが表示できないため、マイクとカメラのパーミッションを通話開始前に取得しておく必要があります。通話が開始しても、相手の声が聞こえても、自分の声が聞こえない状態になってしまします。*4

また、通話開始のために特定の画面遷移を持つアプリは注意が必要です。画面非ロック時には通話開始と同時にアプリが起動するため、一貫した体験のために他の画面を見せずに通話画面を見せる必要があります。

VoIPプッシュ通知受信時のライフサイクル

VoIPプッシュを受信すると、バックグラウンドでアプリが起動され、まず、

-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;

が呼ばれ、

-(void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type;

が呼ばれます。アプリ起動時に非同期で何らかの処理を実行している場合は注意が必要です。

まとめ

Amazon SNSによるVoIPプッシュ通知と、CallKitによるネイティブ通話画面の実装をまとめました。クライアントでVoIPプッシュ通知を受け取り、ネイティブ通話画面を表示し、バックグラウンドでアプリを起動し通話を開始することで、iOSの通話と同様の体験を提供することができます。

実装にあたり、Appleが公開しているサンプルプロジェクトが、とても参考になったので、見てみてください。

参考文献

*1:実際の運用には、サーバー側の実装ももちろん必要

*2:この設定に気づかず、VoIPプッシュ通知からバックグラウンドで起動しないなと、数時間ハマりました

*3:クライアントから直接AWSに登録することもできますが、おすすめはしません

*4:執筆時に、某有名アプリでこの挙動を確認して辛い気持ちになった

「よくわかるAuto Layout」を執筆した話

先日、私の執筆した「よくわかるAuto Layout」が発売されました。とても貴重な経験ができたと思うのでまとめようと思います。

経緯

はじまりは、2015年元日。新しいメモ帳に「本を書く」と目標を書いたことでした。他にも目標があって、ベンチプレスの自己記録更新や、シックスパックを手に入れるとかそのうちの1つです。

きっかけは、前職のGunosy第13回potatotipsを開催したときに突然訪れました。懇親会で@tokoromさんにアイデアを話したところ、「お、いいね!」となり出版社の方を紹介してもらえることになりました。その後、出版社の方との顔合わせミーティング、企画の打ち合わせなどがあり、執筆することになりました。このときはまだ、これから待ち受ける困難には気付いていないのでした。

開発体制・フロー

  • 著者(私)
  • 監修(@tokorom)
  • 出版社
  • 編集の方々

マークダウンで記述し、1章ごとに、Github上でPull Requestを作るというスタイルでした。今考えましたが、通称GMSフロー(Github+Markdown執筆フロー)です。私にとって、はじめての執筆だったため、私が書く、編集がチェック、修正依頼を送るみたいなプロセスを複数回繰り返しゲラ*1ができ、その後赤入れ*2を繰り返しやるという形でした。*3

執筆中抱えた課題

どこから手をつけたらいいかわからず、過去のブログ記事と関連する第6章「実装基本パターン」から手をつけることに。この章は、Auto Layoutを用いたレイアウトをパターンに分けるという試みの章です。時間をかけて書いたブログ記事を元に書いたので上手くいくと思っていましたが、このファーストドラフトはひどいものでした。まず、長文を書くということができませんでした。また日本語や全体の設計にも問題がありました。*4

長文が書けない

最初の問題は、内容が表面的すぎたことです。十分な深掘りができておらず、結果的に最初に書いた章のドラフトは短すぎました。それでも、この時書いた量は、気合の入ったブログ記事位の長さでだったので、「俺頑張った」と思っていました。

日本語の問題

日本語らしい書き方ができていないこと。昔から国語が大の苦手だった私。中学の時、4分の1か4分の2と言いたくて、4分のちょっとって言ってたくらい、国語力は低いです。さらに、文章の書き方は留学中に学び*5、学術論文を通して学んだので、特徴的な文章となっていました。日本語は英語よりも接続詞をよく使うとか、主語をはっきりさせるとかいった、当たり前のレベルからはじめる必要がありました。

全体設計

200ページ以上あるので、全体を見てメッセージを発信するのが非常に難しかったです。私は、まず目次を作ることにしました。しかし、書き進めていくと、内容の追加、入れ替えが必要になったり、書籍の目的を考えなおすなんてこともありました。また、コンテクストがある程度共有されているブログと違って、前提条件から説明し、かつ深掘りする必要があります。苦手だからこの内容は飛ばそうみたいなこともできません。一歩引いて全体を見ながら、時には必要以上に近づいて対象技術を観察する必要がありました。

対策

これらの課題はエンジニア的思考で解決することができました。

長文が書けない

これは書くことでしか解決されませんでした。コードと一緒です。ただ、長文が書けないので、書きはじめることができません。「鶏が先か、卵が先か」です。そこで、私はまず口語で書くことにしました。例えばこんな感じ。

iOS 8 ではオリエンテーションという考え方がなくなった。むしろ考えないで欲しい。デザイナーと縦横で話すのはやめよう。sizeでやろうぜ。 UITraitCollectionを使おうよ。やばいんだって、こいつ。

口語で書くと、まだ書くことができたので、アウトプット量を増やすという意味と自分の思考を整理するという意味で、ひたすら書き続けました。

日本語の問題

小さい単位で、編集の方にレビューしてもらうというのを繰り返しました。Pull Requestは小さい単位で出し、早い段階でレビューをもらい修正してもらう。これもチーム開発と似ています。失敗を多く犯しながら、失敗を元に、少しずつ自分の日本語の問題を解決していきました。

全体設計

本を書いたことがなかったので、先に目次を作るなんてアプローチがまず間違っていました。プログラミングをしたことない人が、クラス設計から入るようなもんです。全体像をざっくり考えた後、プロトタイプをさっと作る、そのプロトタイプを元に全体を修正する、そんなアプローチが必要だったと今では思います(このトピックは中島聡さんの本がおすすめ)。

また、プログラミングと同様に、コードを書き、リファクタリングすることを繰り返し、ようやく良い設計に近づいていけるのだと思います。文章の書き直しもリファクタリングだと思うと、書くことや修正することに対する心理的障壁がぐっと下がります。ソフトウェアだと考えれば、一度動くものを作って、形を整え、必要があれば作りなおすなんてことも、できるようになりました。*6

その他

執筆は、困難が多かったですが、それによって得たことも大きいです。

まず、執筆を通してアウトプットを続けたので、文章に対する取り組み方が変わりました。未だに苦手意識が強いですが、日本語で長文を書くことへの抵抗感は減ったし、ある種のスタイルが確立したのは良かったです。また、技術要素についても網羅的に知識を整理することができたので、取り組み方、見え方が変わり、コードを書くときも少し抽象的な視点から物事を捉えられている気がします。

割りと辛かったのは、1年間、外部世界と切り離されていたことです。これは、アウトプットにエネルギーを注ぐため、自然とインプットの量が減るためです。時間を主に使う技術は執筆内容に関連したものになるため、新しい技術、注目されている技術をこれまで通り深掘りする時間を確保するのが非常に難しかったです。執筆をしている間は、情報の取捨選択、時間の効率化に気をつけていました。また、友人と遊ぶ機会も減るので、修行僧のような気持ちになることもありました。

まとめ

とてもハードなプロセスでしたが、執筆を通して良かったことの方が多いです。学びはとても多かったです。書籍が店頭に並んでいる姿をみたとき、友人達が応援してくれているとき、役に立ったというコメントを見たときに、「あぁ頑張ってよかったな」と心から思えた、本当に良い経験でした。

  • 「よくわかるAuto Layout」という本を執筆しました。多くの人に、iOSアプリ開発のレイアウト手法を伝えるために、全身全霊を注ぎ書きました。
  • 執筆にエンジニア的手法を用いることができ、それはとても有効でした。
  • アウトプットによって圧倒的成長を実感できます

宣伝

よくわかるAuto Layout」はAuto Layoutとサイズクラスについて解説している書籍です。レイアウトの基礎を包括的に紹介し、後半では業務でよく使うパターンをまとめています。アマゾンの紹介文によると対象者は、

過去一度はXcodeを用いてアプリを作ったことがあるが、Auto Layoutとサイズクラスを用いたAdaptive Layoutと言われると、つい尻込みしてしまうアプリ開発者にぴったりの一冊です。

という感じになっています。基本的には、iOSアプリ開発初心者に向けて書いているので、"黒帯エンジニア"みたいな人には向きません*7。書店で見かけたらパラパラと読んでみてください。

*1:文字を印刷用レイアウトにおこしたもの

*2:ゲラに赤ペンで修正を入れていく

*3:各章6往復以上しました

*4:もちろん技術的な困難もありました

*5:日本の国語の授業って文章構造とかあまりやらないですよね

*6:昨日たまたま聴いたdex.fmで似たようなこと話していました。

*7:多くの人に読んでもらえると嬉しいですが、正しい対象読者に届かないとお互い不幸だと思っています

「ニューヨークのスタートアップで働きはじめて6ヶ月の気づき」というタイトルで、GunosyBeerBashにて発表しました

f:id:jeffsuke:20160425193158j:plain

ちょっと前になるが、「ニューヨークのスタートアップで働きはじめて6ヶ月の気づき」というタイトルで、GunosyBeerBashにて発表した。前職Gunosyにて、久しぶりの勉強会登壇だった。

OKpandaという、英語学習アプリを提供するニューヨークにあるスタートアップで働き始めて、約半年が経った。とても多国籍なチームで、東京と京都での過酷な合宿、冬のニューヨークを乗り越え、自分の中でも振り返りたかったのでまとめてみた。

発表資料は以下。

日常編

半分以上ネタである。ニューヨークのスタートアップで働きながら日常的な気づきは以下の3つ。

  1. 寿司が美味しくなくても折れない心
  2. ニューヨークの寒さに折れない心
  3. 議論に折れない心

寿司が美味しくなくても折れない心

美味しいご飯を食べるには、それなりに高いお金を払う必要がある。Netflixで見れる「Jiro Dream of Sushi」というドキュメンタリーがアメリカで人気で、寿司の話をすると、ニューヨークでは大体「Jiroって知ってるか?」と聞かれる。ちなみに、安い寿司は、お米がシャバシャバです。

ニューヨークの寒さに折れない心

気温がマイナスなんて普通。東京以北に住んだことがない私には辛い。ある日には、大雪で15時以降外出禁止、全交通停止、レストランやその他の店も閉店なんてこともあった。ちなみに、凄いのはここからで、次の日の朝には見事に雪かきが終わっている。

議論に折れない心

常に主体性と主張を問われる。「いいアイデアない?」なんて軽々しく聞くと、「君はどう思うの?」と聞かれる。これは、非常に既視感のある光景だ。そう、リクルート。詳しくはリクルートのDNAとかを読んで欲しい。

日常編まとめ

正直な話、食事はアメリカの食事に慣れているので大きな問題ではない。ただ、焼き肉や寿司といったご褒美系ご飯が、ちょっと高いというくらいだ。 あと、発表では書かなかったが、ニューヨークで一緒に働いたり、リモートをしたり、複数のロケーションで働いている。リモートワークは一つのテクニックだと感じた。そして、維持するのはとても難しい。特に時差がある場合は、難しいく、正直ベストプラクティスが知りたい。

技術編

日本では会ったことがないタイプのエンジニアと働いており、綺麗なコード、設計思想みたいなものを常に求められている。ちなみに、この内容は日本のイケてるエンジニアの方々には当たり前すぎる内容だと思うので、読み飛ばしてほしい。

メソッド名は英語で読めるものに

例えば、あるビューコントローラに何かを表示するような以下のメソッド。

- (void)presentFromRootViewControler:(UIViewController *)rootViewControler {
  // Do something
}

修正後こうなった。

- (void)presentToViewController:(UIViewController *)viewController {
  // Do something
}

頂いた指摘は、

  • Controllerのスペルが間違えていた
  • rootViewControlerかどうかわからない
  • Fromではなくて、Toかwidth

よく見ると中学校の英語レベルのミスだ。多分化だからこそ、明確に英語的に正しく読めるようなメソッド名をつける必要がある。

メソッドを分割する

メソッドは細かい単位に分割。意外と忘れがち。

- (id)init {
  self = [super init];
  if (self) {
    self.backgroundColor = [UIColor whiteColor];
    self.layout.cornerRadius = 5;

    UIImageView *imageView = [UIImageView alloc] init];
    imageView.image = [UIImage imageNamedUniversal:@"someImage.png"];

    [self addSubView:imageView];

    imageView.keepHorizontaCenter.equal = 0.5;
    imageView.keepTopInset.equal = 10;  
  }
  return self;
}

みたいな長い初期化メソッドがあったら、以下のように分割してあげると読みやすかったりする。

- (id)init {
  self = [super init];
  if (self) {
    [self configureUI];
    [self layoutUI];
  }
  return self;
}

- (void)configureUI {}
    self.backgroundColor = [UIColor whiteColor];
    self.layout.cornerRadius = 5;

    UIImageView *imageView = [UIImageView alloc] init];
    imageView.image = [UIImage imageNamedUniversal:@"someImage.png"];
}

モジュール化と依存関係の整理

小さいチームで、複数のアプリをメンテナンスするため、各機能をモジュール化し、再利用出来るように心がけている。 例えば、広告を管理するクラスAが、広告SDK Bをimportしているとしよう。クラスA内でこのSDKを用いているため、AはSDK Bに依存していることになる。これは、実行時の依存も、コード的にもどちらもそうである。

f:id:jeffsuke:20160511232916p:plain

そこで、Protocolを用いてこれを解消する。つまり、protocolを定義し、Aをprotocolに依存させ、SDK Bでそのプロトコルを採用する。すると、SDK BをAにプラグインできるようになる。

f:id:jeffsuke:20160511232948p:plain

まとめ

国際的なエンジニア達と近くで働く中で多くの事を学んでいる。日常的には、国際人として働く難しさ、日本の心地よさ。技術者としては、常に最善を求めてリファクタリングし続けることや、基本的な設計に対する考え方を学んでいる。

OKpandaライブ英語 - 先生と会話の練習ができる学習アプリ
OKpandaライブ英語 - 先生と会話の練習ができる学習アプリ
開発元:OKpanda Inc.
無料
posted with アプリーチ

アメリカの会社で三ヶ月仕事して必要だと思った英語力

Rebuild.fmを聴いてて、まだ友人のポストを見て、自分も思うところがあったので、書いてみる。 3ヶ月前からアメリカのスタートアップで働き始めたてなので参考になると思う。

まず働く上で必要な英語力って

  1. 仕事をする上で必要な能力
  2. 同僚との日常会話

の2つ。

面接に通って、仕事を手に入れる上で、英語力という意味では「1.仕事をする上で必要な能力」があれば問題ない。 実際に働く上で痛感したのは「2.同僚との日常会話」の不足。

英語の四技能

どのレベルになっても基本的には以下の能力のどれを集中して伸ばすかという話になる。

  • リーディング
  • リスニング
  • ライティング
  • スピーキング

1. 仕事をする上で必要な能力

これは、正確に物事を表現する能力を指している。具体的には、

  • 正しく喋っている人や、正しい書き言葉を理解する能力
  • ロジカルに物事を英語で正確に説明する能力
  • 技術力による文章理解

これはどれも書き言葉、つまりリーディング、ライティング能力が鍵となっている。

正しく喋っている人を理解する能力、ロジカルに物事を英語で正確に説明する能力

これは、学校でやるようなリスニングやリーディングを指している。ミーティングや面接という場面では、相手に伝えることを目的としているため、丁寧な言葉を用いた表現をする人が多い。また説得力をもたせるような知的な言い回しや、ロジカルな言い回しが多いため、書き言葉に近くなる。そのため、学校で学ぶような、リーディングやリスニング能力の強化で十分だったりするのだ。

つまり、書き言葉を鍛えること。技術書を英語で読んだり、技術ブログを英語で書いてみたり、さらにそれを添削してもらったり。論文を英語で読めて、自分の力で書けるのであれば、十分だと思っている(添削ほぼなしで書ける人は少ないだろう)。さらに、英語と日本語は文章構造が異なるため、英語のエッセイの書き方を勉強するのも必要だろう。また、議論文化が日本より強いので、ディカッションや、ディベート的な言い回しを使いこなせることも必須だろう。

リーディング、ライティング能力なので、リスニングはTOEFL iBTで20点あれば問題がないと思ってる。

unique-experience.xyz

技術力による文章理解

とても当たり前なのだが、技術力を鍛えること。特にコンピューターサイエンス的な内容。アメリカ人は、自分の正当性を証明することが得意だ。そんな中で、なぜそうであるかを彼らと同じように表現できる日本人は少ないと思っている。

2. 同僚との日常会話

これはいつ必要になるかというと、職場で最大のパフォーマンスを出すためだ。具体的に必要な能力は

  • 砕けた頻出表現や、口語表現を知っていること
  • 正しい音を理解し、正しい音とリズムが再現できること

職場で同僚がぱっといった内容を理解したり、彼らと食事をしているときに関係を深めたり、気になる事をさらっと聞く上で役立つ。こちらは、主にリスニング、スピーキングだ。

砕けた頻出表現や、口語表現を知っていること

英語の口語は書き言葉と全く違う言い回しになることが多い。そのため、そういった訓練を受けていない日本人は、いかに口語的な表現を知っているかによって、日常会話のスムーズ度が変わってくるだろう。例えば、getという言葉。友達とカフェにいるときに、”I got this“と言えば俺が払うよってこと。日常会話は基本的に同じ内容のフレーズを使いまわして会話するので、型をいかに増やすかがキーとなってくる。 また、日常会話ではもちろん口語表現や文化的な表現知識も必要になる。例えば、「うちの卒業生はみんな"Frosted tip"だったよー」ってみんなで笑っていても、私にはさっぱりだったし、多くの日本人には恐らくさっぱりだろう(Frosted tipとは毛先だけブリーチして、ツンツンにした思春期的な髪型)。文化に基づいたこういった表現も鍵となる。

正しい音を理解し、正しい音とリズムが再現できること

個人的にこれは日常会話する上では最も大事だと思っている。職場で、ゴニョゴニョと誰かが言ったことを咄嗟に理解するのはとても難しい。音として慣れてないためだ。これを鍛えるには、耳と口を鍛える必要がある。発音ができる音は聞こえるようになる。

私は、フォニックスを学び、英語特有のリズムを学び、ドラマを徹底的に真似することで体に音を叩き込んだ。音をまずは知る(フォニックス)。日本語でいう「あ」とされる英語の音は3つある。これらは知らないと同じ音に聞こえて、文脈で判断するしかなくなる。

次に英語特有のリズムを学ぶ。私は以下の「American Accent Training」という本を使った。英語では、音がつながったり、場合によっては省略されたりする(地域にもよるのだが)。例えば、Mountainという単語をマウンヌーンと発音する人もいる。Sacramentoだったらサクラメンノーだったり。また、文章になると一定のリズムパターンがあり、知っているとリスニング能力が格段に向上する。

最後に、ドラマを用いた仮想練習をした。フレーズ量を増やし、音の練習をしたのだ。ドラマをディクテーションし、その内容を一人で演じる。私の場合、”How I met your mother”や、”The O.C.”なんかを見て真似したりしていた。また、通常のドラマは実際の日常会話ペースで話をしていないので、リアリティショー(テラスハウスとかがそう、私は"The Hills"というドラマでちょっと練習した)なんかを真似するのも手だ。

まとめ

私は、この7年位を日常会話能力向上に使ってきた(「1. 仕事をする上で必要な能力」のリファレンスが少ないのはそういうこと)。今では、仕事する上ではいかに業務を遂行できるかなので、日常会話能力なんてそこまで必要ではないと思っている。構造的で、ロジカルな文章を英語で書くのであればエンジニアは得意なのではないだろうか。周りの情報に惑わされず地に足の着いた語学力を身につけるのが最短経路だと思っている。

よりWWDC2015を楽しむために

f:id:jeffsuke:20140601133841j:plain

WWDC2015が来週から開催されるので、より楽しむためのTipsをまとめてみる。

サンフランシスコ

f:id:jeffsuke:20140609155218j:plain

この時期は東京よりとても寒い。シリコンバレーの陽気な天気から考えられない位寒いので、ジャケット必須だ。

日本と比べるともちろん治安の悪い地域はある。SOMA、Tenderloinあたりは避けたほうが良い。

f:id:jeffsuke:20150607072259j:plain

カフェ等で荷物を盗まれる事はあるので、私は席を立つ時荷物を置いていく場合は

Would you mind keeping an eye on my luggage.

とか声をかけておく事が多い。

登録

開始前日の9am~7pmに受付が出来る。受付が終わると、入館証とジャケットを2014年はもらえた。 WWDC期間中は受付が出来たと思うので、それほど急ぐ必要はない。 ちなみに、受付時はパスポート必須(もしくは英語の身分証明書)なので注意が必要だ。

*私は昨年運転免許証しか持ってなかったが、たまたま日本語を読める韓国系アメリカ人がいたので事無きを得た。

初日

f:id:jeffsuke:20140602091027j:plain

初日はWWDCの目玉イベントKeynoteがある。スケジュールは以下のとおり。

  1. Keynote
  2. Platforms State of the Union
  3. Apple Design Awards

Keynoteが終わった後に発表されたbeta版をダウンロードすることができる。一階の休憩室で有線につなぐことができ、keynote後はとても混む。

keynoteに並ばない

f:id:jeffsuke:20140602012116j:plain

WWDCの目玉イベントであり、私も昨年深夜から並び、一般席の最前列から見た。 しかし、目玉イベントであるが故に、すぐにオンラインで情報を確認できる。 また、ストリーミングビューイングも複数の場所で実施されている。

Apple WWDC 2015 - Keynote Viewing Breakfast

keynote後にbeta版やドキュメントが提供されるので、WWDCを最も有効に活用するためには、ここで体力を温存し開発に集中した方がおすすめである。

セッション

WWDCのアプリがあるのでダウンロードしスケジュールを事前に確認しておく。 ちなみにiPad版の方が見やすいのでiPadを持っている人はそちらをおすすめする。

keynote前には全てのセッションは公開されていない。(昨年だとSwift関連等)

f:id:jeffsuke:20150607072621p:plain

大きい部屋の公演はより一般向けなものが多く、開発者向けでマニアックなものは比較的小さい部屋で行われる事が多い。

全部アップロードされるのだが私は恐らく見ないので、一人だと見なそうな内容を選ぶことにした。

個人的に昨年のセッションで面白かったのは以下のセッション。 Advanced Graphics and Animations for iOS Apps

また、有志によって全文がASCIIwwdcにアップロードされる。

Lab

個人的にはWWDCで最も重要な要素。Appleの開発者に直接質問ができる年に一回の機会だ。

昨年はSwift、新しくなったXcode、Storyboard等多岐にわたる内容を聞くことができた。自ら試し、気になった内容を質問し、その場で解決できるのでおすすめだ。

また、自分のアプリをAppleのUI/UXデザイナーに質問するコーナーもあり、iOSアプリ開発の原理原則を垣間見ることができる。

より楽しむ

bash

Yuerba Buena Gardensにて木曜日に開催されるライブイベント。Appleが選んだ今イケてるアーティストがパフォーマンスする。食事とビールが提供されお祭り感がある。また、Appleで働く人々も来るのでTim Cookと会えたりするかもしれない。

周辺イベント

WWDCに合わせて各社がMeetupイベントを開催している。 世界各国の開発者と出会えるチャンスだ。

WWDC Parties

話す

会場では多くの開発者がいる。 ソファーでくつろいでいると、話しかけてくる人も多い。 これをどう取るかは人によると思うが、日本よりも見知らぬ人と会話する事が自然なので気にせず声をかけたほうがストレスは少ない。

食事

WWDC開催中は、朝食、ランチ、おやつが提供されている。 アメリカ的な弁当だが、割りといける。

Gunosy for Apple Watchができるまで

Gunosyが2015/05/27にApple Watch meetup @ HillsGarageを開催した。

私にとっては初めての参加者100人近いイベントだったので学びが多かった。

Apple Watch meetup @ HillsGarageを開催しました。

f:id:jeffsuke:20130203070950j:plain

発表内容

Gunosy for Apple Watchができるまでに突破した課題達をまとめた内容。 Twitter上で反応があった内容を一部抜粋して紹介する。

Watch App

willActivate()の活用

現在Apple Watch上でのビューが呼び出されるまでの手順は以下のようになっている。

  1. Watch Appが呼び出される
  2. Storyboardが読み込まれる
  3. 対象画面のinit, awakeが走る
  4. Watch上で画面が表示される
  5. willActivate()が呼ばれる

結果的に、initとawakeでの処理を最小限にすることでユーザーの体感スピードが上がる。

// MARK: Life cycle
    override func awakeWithContext(context: AnyObject?) {
        super.awakeWithContext(context)
        if context != nil {
            self.article = context as? ArticleEntity
        }
    }
    
    override func willActivate() {
        super.willActivate()
        if article != nil {
            self.layout(article!)
        }
    }

WKInterfaceGroupの利用

画像を用いる場合はWKInterfaceImageWKInterfaceGroupの中に入れると便利である。Placeholderとしても使えるし、storyboardから角丸の設定もできる。 Placeholderを設定する場合は以下のとおり。

self.imageGroup.setBackgroundImage(UIImage(named:"no_image"))

角丸に設定するためには、WKInterfaceGroupを選択し、Attribute inspectorからRadiusを変更できる。

Glance

複雑な描画を本体で

Apple Watch用のStoryboardはiOSアプリ用と比較してレイアウトに制限が多い。 そこでGunosyアプリを作る際には、Extension上でCATextLayerを使って描画し、NSDataとしてWatchに転送している。その際作ったライブラリは以下で公開している。

GitHub - jeffsuke/GlanceWordRainbowImage

参考:Apple Watch meetup で発表しました

イベント開催する上での学び

個人的には大き目のイベントだったので開催する上での学びも多かった。 今後似たようなイベントを開催する上での参考になれば嬉しい。

  • 食事はピザが大人気だった
  • ビール複数種類あると良い
  • 100人分注文すると余る
  • デザートはいらない
  • 椅子の向きとか、音楽、雰囲気を工夫しないとかたい雰囲気になりがち
  • 会社から4人片付けに参加したが、足りず周りに協力してもらう必要があった
  • HillsGarageはドラムあるし、音響いいし、広いし、最高の会場だった
  • 勉強会の時間は押すもの。バッファを積もう。