Goalist Developers Blog

teamLab Jungle in 渋谷ヒカリエ に行ってきた

こんちは。渡部です。

f:id:watabe1028:20170904161106j:plain

今回はイベントレポートです。
勉強会でもなんでもないただの遊びですw

こちらに行ってきました。

www.team-lab.com

ネタバレ注意です。
また使ってはいけない画像があったらご指摘ください。
すぐに削除します。

学ぶ!未来の遊園地

まずは「学ぶ!未来の遊園地」。
遊園地でお絵かき。

この絵が・・・
f:id:watabe1028:20170904160913j:plain

こうなった!
f:id:watabe1028:20170904160953j:plain

どうなるかわかっていても楽しい!
ただし見つけるのが難しい!
弊社のキャラクター「ふろぐん」を描いて見たものの、
見つけることができず。。。
きっとワニに捕食されたのでしょう。。。

Art Night

「Art Night(アートナイト)」(夜の部)が始まりました。
「光のアートに包まれる超幻想空間での体験型音楽フェスティバル」
とのことで光と音楽でテンション上がります!
f:id:watabe1028:20170904161202p:plain

カラフルになったり、
f:id:watabe1028:20170904161230p:plain

ドクロがきたり、
f:id:watabe1028:20170904161300p:plain

お花がきたりしました!
f:id:watabe1028:20170904161333p:plain

きました、チームラボボール
f:id:watabe1028:20170904161414p:plain

すごく綺麗!
みんなのテンションが一気に上がります。
f:id:watabe1028:20170904161447p:plain

すぐにチームラボボールで埋め尽くされます。
我先にと手を伸ばします。
f:id:watabe1028:20170904161531p:plain

そしてでっかいミラーボール!
煽りも入って飛び跳ねたり踊ったり!
f:id:watabe1028:20170904161559p:plain

光が反射してめっちゃかっこいい!
f:id:watabe1028:20170904161632p:plain

感想

入り口には「まだかみさまがいたるところにいたころのものがたり」があって
そこで1時間くらい時間が潰せそうです。
「小人が住まうテーブル」、「つながる!積み木のまち」も大の大人が夢中になって遊びましたw
「すべって育てる! フルーツ畑」のすべり台が結構な角度で軽くビビり、
ラストのライブで感動して帰る、といった感じです。
「RAIZIN BAR」で一杯やりたかったですが時間がないのでスルー。。。
が、帰りにRAIZINを1本もらえました。

仕事帰りに2時間ちょっと行っただけでは味わい尽くせない感じでした。
こちらのイベントは9/10までやっているので皆さんも時間があったら是非行って見てください!
(あと数日しかないww)

バックグラウンド時に画面を隠す方法

こんちは。渡部です。

今回はアプリがバックグラウンドに行った時に表示されていた画面をマスクする方法です。
多分10分あればできちゃいます。

手順

・適当に画面を用意する
・コードを書く
・おしまい

適当に画面を用意する

まず適当なプロジェクトのViewControllerに 弊社サービスを牛耳る(二回目)ふろぐんのimageViewを貼り付けます。

f:id:watabe1028:20170831164204p:plain

普通にこのままバックグラウンドに行くとこうなります。

f:id:watabe1028:20170831164219p:plain

バックグラウンドに行ったことを検知するのはAppDelegateのお仕事です。

バックグラウンドに行った際にマスクするviewを表示し、 アクティブに復帰した際にマスクするviewを非表示にします。

コードを書く

バックグラウンドに行く際に呼ばれるのが
applicationWillResignActive

復帰する際に呼ばれるのが
applicationDidBecomeActive

これらに追加していきます。
AppDelegateにviewと処理を追加します。

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    var backView: UIView? // マスクにするview

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        // viewを黒色に指定
        self.backView?.backgroundColor = UIColor.black
        
        return true
    }

    func applicationWillResignActive(_ application: UIApplication) {
        // アクティブじゃなくなる際にbackViewをaddする
        self.window?.addSubview(backView!)
    }

    func applicationDidBecomeActive(_ application: UIApplication) {
        // 追加したbackViewをremoveする
        self.backView?.removeFromSuperview()
    }
}

すると、バックグラウンドに行く際にこうなります。

f:id:watabe1028:20170831164237p:plain

うぇい

おわりに

むかーしむかし、セキュリティが厳しい会社のアプリを作った時、
この機能をつけた記憶があります。
この機能でセキュリティが担保できるとは思いませんが、
視覚的には効果があるので(簡単ですし)やってみるのも良いかもしれません。

iOS10でプッシュ通知を実装してみた

こんちは。渡部です。

今回はプッシュ通知についてです。
プッシュ通知はリモートだと証明書やらで非常に面倒になります。
なので比較的に簡単な実装方法を紹介します。

f:id:watabe1028:20170831132743j:plain

プッシュ通知とは?

プッシュ通知とは、システム側が外部のサーバーと連携して能動的に情報を取得してユーザーに通知する方式のことである。

www.weblio.jp

要は外部から通知が届く仕組みです。

LINEとかメッセージ届くときますよね?
あれです。

動作環境

・macOS Sierra(10.12.6)

・Xcode8.3.2(古い!)

・Swift3.0(古い!)

・実機(iPhone SE iOS10.2 古い!)

注意点

・シミュレータでは検証できないので実機でおねしゃす!

・AppleDeveloperProgramには加入済み前提で進めます。
・証明書CSRファイルは取得済み前提です。

・今回はAdHocでやります。

・訳あってiOS、Xcodeとも古いままです。

・サーバサイドはノータッチ!

プッシュ通知の種類

リモートとローカル

簡単に言うと、ローカルはアプリ側のプログラムで通知が完結するもの

リモートは外部のサーバを使って通知を可能にするもの

と自分は定義しています。

プッシュ?リッチ?ペイロード?なんぞ?


通常のプッシュ通知のみの実装がプッシュ通知、
プッシュ通知を開くとWebViewが表示できるのがリッチプッシュ、
プッシュ通知からメッセージなどのデータをペイロード取得
する通知があります。

今回は面倒なのでシンプルなリモートのプッシュ通知です。
次回はローカルの記事を書くかもしれません。
書かないかもしれません。
詳しい仕組みは偉人たちの記事を参照してください(投げやり) 。

チャット風メッセージアプリを作ります。
Web側のメッセージ送信時にアプリがフォアグラウンドにない場合に
通知を表示させるだけの簡単な仕様です。
チャット? どうやって作んの?の人はこちら

今回は画像付きで長くなるで!

手順


・APNs証明書作成
・アプリの設定

・コーディング

・実行

APNs証明書作成

AppleDeveloperにログインし、「Certificates,IDs & Profiles」を選択します。
右ペインのメニューから「Certificates」を選択し、右上の「+」ボタンで証明書を作成します。

Apple Push Notification service SSL(Sandbox)を選択し

f:id:watabe1028:20170831152540p:plain

使いたいアプリのApp IDを選択します。

f:id:watabe1028:20170831152601p:plain

このページは華麗にスルー

f:id:watabe1028:20170831152616p:plain

CSRファイルを選択すればおけ!

f:id:watabe1028:20170831152632p:plain

アプリの設定

まずプロジェクトでプッシュ通知を使うための設定をします。
プロジェクトを選択し、「Capabilities」から「Push Notifications」と「Background Modes」をONにします。
f:id:watabe1028:20170831132308p:plain

「Background Modes」は「Background fetch」と「Remote Notifications」にチェックを入れます。 f:id:watabe1028:20170831132324p:plain

コーディング


プッシュ通知の基本ですが通知を受け取るには許可が必要です。

許可をもらった後に、デバイストークンを取得してサーバサイドに渡す必要があります。
いくつか注意点があるのでコメントを読んでください。

まずはインポートとDelegateの設定です。

import UIKit
import UserNotifications  // インポートする

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, networkManagerDelegate, UNUserNotificationCenterDelegate { // Delegateの追加を忘れずに

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

didFinishLaunchingWithOptionsで通知の設定をします。

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
                
        if #available(iOS 10.0, *) {
            
            //ios10
            let center = UNUserNotificationCenter.current()
            center.requestAuthorization(options: [.badge, .sound, .alert], completionHandler: { (granted, error) in
                if error != nil {
                    return
                }
                if granted {
                    debugPrint("通知許可")
                    center.delegate = self
                    application.registerForRemoteNotifications()
                } else {
                    debugPrint("通知拒否")
                }
            })
            
        } else {
            // ios9
            let settings = UIUserNotificationSettings(types: [.badge, .sound, .alert], categories: nil)
            UIApplication.shared.registerUserNotificationSettings(settings)
            UIApplication.shared.registerForRemoteNotifications()
        }
        
        return true
    }

通知が許可された場合の処理です。
ここでデバイストークンを取得します。
以降もずっとこのデバイストークンを使うのでUserDefaults等に保存しておきます。

    //リモート通知
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        
        // そのままだと「32bit」という文字列なので以下の処理を行います
        let deviceTokenString: String = deviceToken.map { String(format: "%.2hhx", $0) }.joined()
        print("deviceTokenString \(deviceTokenString)")
        util.setUserDefaultsObject(value: deviceTokenString, key: udDeviceToken) // これは自前
    }

逆に通知が許可されなかった場合の処理です。

    //リモート通知を拒否したときの動作
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        
        debugPrint("リモート通知の設定は拒否されました")
    }

ここまで書けば一応動きます。
通知が来た時、通知から起動した時は
withCompletionHandlerなどを使います。
もちろん、はしょります。

実行


ビルドして実機にインストールします。
ここでビルド成功後にエラーが出たりしますがそこは頑張ってください。

多分プロビジョニングとかが悪さしてることが多いです。
今回は速攻で通知の許可アラートが表示されるはずです。
f:id:watabe1028:20170831131756p:plain

許可した後にアプリを一度バックグラウンドにします。
Web側でメッセージを送信! f:id:watabe1028:20170831131816p:plain

来た!
f:id:watabe1028:20170831131829p:plain

おまけ


サーバサイドはノータッチとしてましたが
今回はAWSのこちらを使っています。
aws.amazon.com

まとめ

準備から実装まで面倒なプッシュ通知ですが
ユーザーのアプリ復帰率を上げるためには必須と言えます。
通知自体もだんだんリッチになるので
今のうちに簡単な通知ができるようになった方が良いな、と思いこの記事を書きました。
どこかの誰かのお役に立てればと思います。

JenkinsでスタンドアローンなPlayFrameworkアプリをビルド・デプロイする

こんにちは。ゴーリスト開発のイイオです。

今回はJenkinsおじさんを使役します。
とはいえほんとうに手動でぽちぽちやっていたのを置き換えただけなので
もっと便利なプラグインとか使い方があるような気がします。

f:id:y-iio:20170825104518p:plain
よく知らなくてすまんな…おじさん

PlayFrameworkとは入社時から1年弱の付き合いですがいまだにあまり仲良くない。

https://www.playframework.com/documentation/ja/2.4.x

前提

ビルド環境

  • Ubuntu 14.04.5 LTS
  • Jenkins 2.46.2
  • PlayFramework 2.4 Java

実行環境

  • Amazon Linux AMI release 2017.03

JenkinsサーバはbitnamiでEC2上に立てた

なんかbitnami使ったおかげで楽だったりもしましたが、だいたい詰まったのこのせいな気がしなくもない。
それについてはこちらをご覧ください。

developers.goalist.co.jp

Playビルド用の環境整える

qiita.com

qiita.com

Jenkins > Global Tool Configuration > JDK > JDK追加
Java SE Development Kit 8u131

Jenkinsにリモートでシェル実行できるプラグイン入れる

Jenkins でリモートサーバーのコマンドを実行できる SSH pluginを使ってみた | Whaison JUGEM! StudyNoteBook .

Jenkins > 認証情報 > System > グローバルドメイン > 認証情報の追加
でリモートサーバー接続用のSSHユーザー名と秘密鍵を登録

Jenkins > 設定 > SSHリモートホスト
でリモートサーバーを登録

実行環境でアプリとPID置き場を作っておく

sudo mkdir /var/play
sudo mkdir /var/run/play

パーミッションも適当に与える

やってること

  1. Jenkinsでgithub上のソースから任意のブランチを指定してビルド
  2. リモートサーバーで稼働しているアプリ停止
  3. Jenkinsでビルドしてリモートサーバーの成果物更新
  4. リモートサーバーのアプリ再起動

ダウンタイムがある!ので
作業中はELBを別インスタンスに向けるとか、そのへんも自動化するとかあるだろうけど
漢らしく置いておくことにします٩( ‘ω’ )و

詳しく

1. Jenkinsでgithub上のソースから任意のブランチを指定してビルド

ビルド設定
こんなかんじでパラメータにブランチ名直書きでやってます。

f:id:y-iio:20170825104227p:plain

f:id:y-iio:20170825104326p:plain

なんか選択肢で選ぶ方法とかあるらしいけど複雑なのでやらない。

2. リモートサーバーで稼働しているアプリを停止

ビルド手順の追加 > リモートホストでシェルを実行

if test -e /var/run/play/play.pid; then kill $(cat /var/run/play/play.pid); fi
rm -rf /var/play/hrog-map-api-SNAPSHOT.zip
rm -rf /var/play/hrog-map-api-SNAPSHOT

3. Jenkinsでビルドして、リモートサーバーの成果物更新

ビルド手順の追加 > シェルの実行

bin/activator dist
scp -P 22 -i /path/to/key/key.pem ${WORKSPACE}/target/universal/PROJECT_NAME.zip USER@~~~~~~~.compute.amazonaws.com:/var/play

もっとこのへんいい方法あるんだろうな〜〜

4. リモートサーバーのアプリ再起動

ビルド手順の追加 > リモートホストでシェルを実行

unzip -o /var/play/hrog-map-api-SNAPSHOT.zip -d /var/play
nohup /var/play/hrog-map-api-SNAPSHOT/bin/hrog-map-api -Dconfig.resource=application_dev.conf -Dpidfile.path=/var/run/play/play.pid > out.log 2> err.log < /dev/null &

だめだったら

パーミッションを適切に与えればなんとかなる!!!根も葉もない

AWS Marketplaceからbitnamiを使ってEC2インスタンス上にJenkinsサーバーを立てる

こんにちは、ゴーリスト開発のイイオです。

bitnamiのオールインワンパッケージで
超楽にJenkinsおじさんを召喚できるのでは、という企ての話です。
実際のところ超楽だったかというと???なかんじですが…

bitnami.com

手順

bitnami Jenkinsの「See in AWS Marketplace」をクリック
f:id:y-iio:20170824172032p:plain

インスタンスタイプを選択し
f:id:y-iio:20170824172048p:plain

任意のVPC設定して囲み、自動割り当てパブリックIPを有効にする

qiita.com

タグ設定でわかりやすい名前をつけておく
f:id:y-iio:20170824172158p:plain

インスタンス作成ボタンを押してちょっと待つ

初期化が終わったら割り当ててくれたURLにアクセス
f:id:y-iio:20170824172248p:plain

ウェイ

特にポート設定とかしてないけどhttp://wariatehost/にアクセスすれば
http://wariatehost/jenkins/にフォワードしてくれる。
あとインスタンス起動したら勝手にJenkinsも起動してくれる。

user/bitnamiで初回ログイン

f:id:y-iio:20170824172350p:plain
ドキュメントにはインスタンスのシステムログからパスワード見ろって書かれてるけど
システムログに無いしuser/bitnamiでいけた。謎

勧められるがままに「Install suggested plugins」

で再読み込みしたら
f:id:y-iio:20170824172406p:plain
ウェイ

気をつけること

タイムゾーン変更する

インスタンス立てた時に登録したキーペアでssh接続して変更

ssh -i "/path/to/key/key.pem" ubuntu@~~~~~~~~~~~~~~~.compute.amazonaws.com
timedatectl set-timezone Asia/Tokyo

Jenkinsの実行ユーザーは「tomcat」

github接続時のキー登録するのに大変時間を無駄にしたので…

まあシステム情報見たらわかることでしたが
f:id:y-iio:20170824172448p:plain

tomcatユーザーになってから、こちら参考に鍵登録しました。

blog.duck8823.com

結論

普通にインスタンス立ててyumでインストールしたらいいとおもう。

qiita.com

おわり

Macのローカルサーバーに接続して、iPhone/iPadの実機で動作確認・デバックする

開発環境のMacで立てたローカルサーバに、iPadの実機端末からアクセスしたときのメモです。

実行環境

  • macOS Sierra 10.12.5
  • iOS 10.3.2
  • iPadとMacが同じwifiネットワークに接続している
  • Lightningケーブルで接続している

実機から開発環境のローカルサーバーにアクセス

このあたり参照して「hoge.local」でいけるかな〜と思ったらできず… yuji-ueda.hatenadiary.jp

qiita.com

結局IP指定で行きました tacamy.hatenablog.com

Angular CLI でng serveしてローカルサーバーたててるんですけど
このときにオプションで自分のIP直で指定しないと外から見れなかったです。

ng serve --host <MY_IP>

実機のSafariからみてみる
http://MY_IP:PORT

f:id:y-iio:20170823191813p:plain
ウェイ

デバッグする

PC側の開発者ツール使ってデバッグします。
こちらを参照しました。
www.tam-tam.co.jp

f:id:y-iio:20170823191918p:plain

f:id:y-iio:20170823191935p:plain

ウェイウェイ

感想

これで開発速度が65918723倍になりました。やったぜ

簡易O/Rマッパー作ってみた。

ども、開発部の小野です。

今、簡易O/Rマッパー作っています。今回は簡易O/Rマッパーについて書いてみます。

O/Rマッパーって何?

sqlを直で全部書かなくても、DBにsqlを投げてくれるやつです。

検索条件をちょっと書いてあげるだけで、sqlを投げてくれます。時短ですね。

実際

作ってみた簡易O/Rマッパーです。

public class Work1 {

	public static void main (String[] arg) throws SQLException{
		JdbcManager ajm = new JdbcManager("jdbc:mysql://localhost:3306/data?characterEncoding=UTF-8","root","password");
		List<Map<String,Object>> resultMap1 = ajm.selectBySql("select emp_nm from emp_mst").getResultList();
	}
}

このWork1クラスで簡易O/Rマッパーを実際に使っています。JdbcManagerがそれですね。

ではJdbcManagerクラスを見てみましょう。

public class JdbcManager {

	String db_url;
	String db_user_id;
	String db_user_password;

	public JdbcManager(String db_url,String db_user_id,String db_user_password){
		this.db_url = db_url;
		this.db_user_id = db_user_id;
		this.db_user_password = db_user_password;
	}

	public SqlSelect selectBySql(String sql){
		SqlSelect ss = new SqlSelect(this,sql);
		return ss;
	}

	public AutoSelect from(String tableNm){
		AutoSelect as = new AutoSelect(this, tableNm);
		return as;
	}

}

selectBySqlというメソッドがありますね。このメソッドの引数にsqlを渡して、検索を実行しています。

ではselectBySqlメソッドの戻り値である、SqlSelectクラスを見てみましょう。

public class SqlSelect {

	JdbcManager ajm;
	String sql;

	public SqlSelect(JdbcManager ajm, String sql) {
		this.ajm = ajm;
		this.sql = sql;
	}

	public List<Map<String, Object>> getResultList() throws SQLException{
		Select sl =new Select(this.ajm,this.sql);
		return sl.sqlExecute();
	}
}

getResultListというメソッドがありますね。このメソッドを実行することで、検索結果がListになって帰ってきます。上で見ると分かるようにMapのListですね。

メソッドの中を見てみると、さらにSelectクラスのslというインスタンスでsqlExecuteというメソッドを使用しています。

このメソッドで、実際にクエリーを実行しています。

Selectクラスを見てみましょう。

public class Select {

	JdbcManager ajm;
	String sql;

	public Select(JdbcManager ajm, String sql) {
		this.ajm = ajm;
		this.sql = sql+";";
	}

	public List<Map<String, Object>> sqlExecute() throws SQLException {

		try (Connection connection = DriverManager.getConnection(this.ajm.db_url, this.ajm.db_user_id,
				this.ajm.db_user_password); Statement statement = connection.createStatement();) {

			ResultSet rs = statement.executeQuery(this.sql);
			ResultSetMetaData rsmd = rs.getMetaData();

			List<Map<String, Object>> resultList = new ArrayList<Map<String, Object>>();

			while (rs.next()) {
				Map<String, Object> map = new HashMap<String, Object>();
				for (int i = 1; i <= rsmd.getColumnCount(); i++) {
					map.put(rsmd.getColumnName(i), rs.getObject(i));
				}
				resultList.add(map);
			}
			rs.close();

			return resultList;
		}
	}
}

ConnectionにJdbcManagerインスタンスに持たせた、DBのURL,user名,パスワードを渡して、DBサーバーに接続しています。

あとは、Work1クラスから持ってきたsqlをexecuteQueryでDBに投げ込んでみるだけです。単簡ですね。

帰ってきた結果をリストに整理してチョチョイです。

まとめ

O/Rマッパーで単簡に時短。

次はテーブル名やwhere句の要素だけ渡して、クエリを実行してくれるやつも紹介出来たらいいですね~。