Lento con forza

大学生気分のIT系エンジニアが色々書いてく何か。ブログ名決めました。

毎日コードを書いていきたい。

毎日コードを書いていきたいものだ。

最近のGithubはひどい。

f:id:kouki_dan:20150614001315p:plain

なんていうか、2月くらいからどうしたんだ自分。

緑でまっ緑にすることを目指して頑張って行きたい。

UIDocumentInteractionControllerでクラッシュ

Swiftが熱いですね!
Objective-Cなんてもう忘れ去れるくらい最近Swiftしか書いてないです。
ということで、Swiftを使ってちょっとハマった事をメモ。

外部アプリにファイル転送

外部アプリにファイルを転送するときに便利なものに、UIDocumentInteractionControllerがあります。よく見る、Open In...ってやつですね。これを普通に使うと

    @IBAction func showOpenIn(sender: AnyObject) {
        if let url = NSBundle.mainBundle().URLForResource("pdf_name", withExtension: "pdf") {
            let dic = UIDocumentInteractionController(URL: url)
            dic.delegate = self
            dic.presentOpenInMenuFromBarButtonItem(openInButton, animated: true)
        }
    }

このようなコードになると思います。
しかし、このまま実行すると、iBooksを選択することまではできるのですが、選択した後落ちてしまいました。
ちなみに、シミュレーターだとUIDocumentInteractionControllerがうまく動かないため、実機で実行する必要があります。

こうすればOK

上記のコードで落ちてしまう原因は、関数が終了した段階でGCdicインスタンスを開放してしまうことが原因のようです。そのため、今回はクラスのインスタンス変数にインスタンスを保持する事で解決しました。コードにすると以下のようになります。

    var dic:UIDocumentInteractionController?
    @IBAction func showOpenIn(sender: AnyObject) {
        if let url = NSBundle.mainBundle().URLForResource("pdf_name", withExtension: "pdf") {
            dic = UIDocumentInteractionController(URL: url)
            dic?.delegate = self
            dic?.presentOpenInMenuFromBarButtonItem(openInButton, animated: true)
        }
    }

cocos2d-xで尋常じゃない数の線を引きたい時

最近cocos2d-x v3.3でゲーム作ってます。
ゲームの実装で、タッチの動きに合わせて線を引きたかったのですが、素直に実装したらとんでもないことになりました。

DrawNodeで実装

まず最初は、タッチ位置が移動する度にDrawNodeのdrawSegumentを呼び出す方法で実装しました。コードにするとこんな感じです。

    auto drawNode = DrawNode::create();
    this->addChild(drawNode);
    
    auto listener = EventListenerTouchOneByOne::create();
    listener->onTouchBegan = [this](Touch* touch, Event* event) {
        auto pos = touch->getLocation();
        this->prevPos = pos;
    
        return true;
    };
    listener->onTouchMoved = [drawNode, this](Touch* touch, Event* event) {
        auto pos = touch->getLocation();
        drawNode->drawSegment(prevPos, pos, 10, Color4F(1,1,1,1));
        
        this->prevPos = pos;
    };
    Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this);

これで一応要件は満たせます。後はcocos2d-xが内側でいい感じに処理をしてくれればいいなーって考えてたんですけど、実行するとこんな感じに。

f:id:kouki_dan:20150211184054p:plain

注目すべきは左下です。

左下のフレームレートとGL Vertsの数がヤバイことに。
元は60フレームだったのに、線の描画が重くて10フレームまで落ちてます。
これじゃあ使えないなぁと思い別の方法を探索。

RenderTextureで実装

DrawNodeは毎回線全部を描画しているのが問題なので(多分)、別の方法で描画してあげれば処理が早くなりそうです。
調べた感じ、RenderTextureを使えばこの問題を解決できそうだったのでこれを採用することに。
グラフィックの世界の専門用語はよくわからないですが、オフスクリーンレンダリングと呼ばれる手法らしいです(間違ってたらごめんなさい)
DrawNodeで線を描画して、RenderTextureにレンダリング といった処理を繰り返す実装にしてみました。

以下コードはこんな感じ。

    Size size = Director::getInstance()->getVisibleSize();
    auto renderTexture = RenderTexture::create(size.width, size.height);
    renderTexture->setPosition(Vec2(size.width/2, size.height/2));
    this->addChild(renderTexture);
    
    auto listener = EventListenerTouchOneByOne::create();
    listener->onTouchBegan = [this](Touch* touch, Event* event) {
        auto pos = touch->getLocation();
        this->prevPos = pos;
        
        return true;
    };
    listener->onTouchMoved = [renderTexture, this](Touch* touch, Event* event) {
        auto pos = touch->getLocation();
        
        renderTexture->begin();
        auto drawNode = DrawNode::create();
        drawNode->drawSegment(prevPos, pos, 10, Color4F(1,1,1,1));
        drawNode->visit(); // RenderTextureにレンダリング
        renderTexture->end();
        
        this->prevPos = pos;
    };
    Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this);

こちらの実行結果はこんな感じです

f:id:kouki_dan:20150211185230p:plain

見た目は一緒ですが中身と速度は全く違います。

フレームレートが30になってしまったのですが、最初から30だったので問題なし(?)です。
RenderTextureを使うと30よりあがらない制限があるのかな・・?詳しくは調べてないからわからないです。

線を描画することによって処理が重くなっている様子もなく、ゲームにも使えそうです!
DrawNodeが重いわ!!って方はRenderTextureを使って描画する方法もお試しください!

WebStormをVim化できない?

なんかWebStorm学生無料になってるじゃないですか!! 全然知りませんでした。

とりあえず、インストールして、Vimキーバインドを適用しようとしたんですが。

Browse repositoriesからインストールできない。

Browse repositoriesから、IdeaVimを検索して、インストールをしようとしたんですが、うんともすんとも言いません。
なんでなんだろうなー。って思いましたけど、悩むのが嫌だったので別の方法で入れちゃいました。

jarファイルを直接インストール

JetBrainsのプラグインページから直接jarファイルを落として、Install plugin from disk...から、直接jarファイルをインストールしちゃいました。

この方法では問題なくインストール可能。

なんでプラグインマネージャーからインストールできなかったんだろうなぁ・・・。

Swiftでのユニットテストでクラスが見えない問題

XcodeでProjectを作った時にデフォルトで作られるテストケースでのユニットテストで、少し(だいぶ)はまったのでメモして起きます。

Testsファイルから自作クラスが見えない?

Testsファイルから、自作クラスをインスタンス化しようとすると

f:id:kouki_dan:20141115094605p:plain

このように、クラスが見えなくインスタンス化できない状態になってしまいました。

Targetがプロダクト用とテスト用で別だからできないのかなぁ。と思いつつ、不便だなぁ。とずっと思ってたんですが・・。

解決策

テストしたい対象のswiftファイルのTargetsに、~~Testsも追加してあげれば解決します。

ファイルを作成する時に、ここのTargetsを

f:id:kouki_dan:20141115095107p:plain

このように、チェックを入れるのを忘れないようにすれば~~Testsからでもクラスが見えるようになります!

f:id:kouki_dan:20141115095212p:plain

既存ファイルの場合・・・

既存ファイルの場合でも、簡単にTargetを変更可能です。

f:id:kouki_dan:20141115095452p:plain

File Inspector内の、Target Membershipのチェックを変更することで、ファイル作成時に設定したTargetを自由に変更する事が可能です!

まとめ

よく考えてみたら当たり前だけど、なかなか気付きにくいんじゃないかなぁって思います。
classとmethodをpublicにしないとユニットテストできねーじゃん!!とか最初思ってたんですが、そんなことはなかったです。

Swiftで簡易APIクライアント

AFNetworkingを使った簡易クライアントをSwiftで書いてみました。

Objective-CのライブラリをSwiftで利用

Cocoa Podsかなんかで適当にAFNetworkingを入れておきます。

SwiftでCocoaPodsを使う - Qiita

APIClientを作成

AFHTTPSessionManagerのサブクラスを作る形でAPIクライアントを作成します。
AFHTTPSessionManagerはiOS6以下では利用することができないので、iOS6以下をターゲットにしている方は以下の方法は使えません。

MyClient.swift

import UIKit

class MyClientRequestSerializer: AFHTTPRequestSerializer{
    override func requestWithMethod(method: String!, URLString: String!, parameters: AnyObject!, error:NSErrorPointer) -> NSMutableURLRequest! {        
        var path:NSString = URLString + "?"
       
        var dictParameters:Dictionary<String, String>
        if (parameters != nil){
            dictParameters = parameters as Dictionary<String, String>
        }
        else{
            dictParameters = Dictionary<String, String>()
        }
        
        for key in dictParameters.keys{
            let value = dictParameters[key]
            
            path = path.stringByAppendingFormat("&%@=%@", key, value!)
        }
        
        let req = super.requestWithMethod(method, URLString: path, parameters: nil, error: error)
        
        return req
    }
   
}

class MyClient: AFHTTPSessionManager {
   
    class var sharedInstance : MyClient {
        struct Static {
            static var instance : MyClient = MyClient(baseURL:NSURL(string:"https://api.github.com/"))
        }
        return Static.instance
    }
    
    override init(baseURL url: NSURL!, sessionConfiguration configuration: NSURLSessionConfiguration!) {
        super.init(baseURL: url, sessionConfiguration: nil)
    }
    
    override init(baseURL url: NSURL!) {
        super.init(baseURL: url)
        let requestSerializer = MyClientRequestSerializer()
        self.requestSerializer = requestSerializer
        // If you need a responseSerializer, you can write it
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func yourAPI(){
        self.GET("users/kouki-dan/repos",
            parameters: ["none":"none"],
            success: { (task, response) in
                println(response)
            },
            failure: { (error) in
                println("error")
        })

    }
    func yourAPI2(id:NSString){
        //...
    }
    //.......
}

APIクライアントはこのような感じになります。
yourAPIの所でエンドポイントを記述して、実際にHTTPリクエストを呼び出しています。

APIを利用する時は

func yourFunc(){
    let sharedClient = MyClient.sharedInstance
    sharedClient.yourAPI()
}

のように使います。

簡単な例をgithubにあげときました。

kouki-dan/SimpleAPIClient · GitHub

僕が各種サービスにサボテンを植えたわけ。

サボテン植え師です、こんばんは。

f:id:kouki_dan:20141017235805p:plain

色んなサービスにサボテンを植えてます。

まぁサボテンを植えたのにも理由があります。

Unicode

Unicode6.0で絵文字が追加されて久しいですね。
utf-8の4バイトエリアに絵文字が追加されて、Twitterで絵文字が使える!みたいな事、ちょっと話題になりましたね。
僕はまぁどうでもいいやー。って感じで流してたんですが・・・。

MySQLとutf8mb4

utf8mb4、ご存知でしょうか。
MySQLでは4バイトエリアにあるutf8を、普通のutf8で扱うことができません。絵文字はutf8では4バイトで表現されるので、文字コードにutf8を使ってる限り絵文字を含む文字列をMySQLに格納する事はできないんです。絵文字を入れたい時は、utf8mb4と明示的に示してあげる必要があるみたいです。

フォントと文字コードと・・・。

utf8mb4を指定することでMySQL上に絵文字を格納する事ができても、対応するフォントが無ければ、いわゆる豆腐が表示されてしまいます。
これにどのように対処するか、エンジニアとしての腕の見せどころですね!!
そう、もうお分かりですね!!
僕は🌵と入力した時に絵文字のサボテンをどのように表示しているかを調べるためにサボテンを色んなサービスに植えて回ってたのです!!

結果

TwitterFacebookも、絵文字の文字コードは画像に置換して表示しているみたいです。これならどんなプラットフォームでも絵文字を見ることができてみんなハッピーですね。

そもそもこんな事を始めたのは、僕が作っているサービスで絵文字対応しなくちゃいけなくなったからなんです。でも画像でやるのは面倒だなぁ・・・。って思うので、Webフォントかなんかで対応しようと思います。