It’s now or never

IT系の技術ブログです。気になったこと、勉強したことを備忘録的にまとめて行きます。

【iOS, swift】swiftコーディング規約

※ Swift 2.3をベースに記載

自分がswiftを書くときのコーディング規約を記載します。

クラス

  • Pascal記法 (大文字で始まる) で記述する
// ◯
class ViewController: UIViewController {

}

// ☓ snake case
class view_controller: UIViewController {
・・・
}
  • 継承する時にクラス名の後ろにつける:は:の後ろにスペースを入れる
// ◯
class ViewController: UIViewController {
}

// ☓
class view_controller:UIViewController {
}

メソッド

  • Camel記法(小文字始まり)で記述する(hogeFuga)
  • メソッド名は動詞を使う
  • ;は付けない
  • {の行は改行しない。前にスペースを入れる。
  • ->の前後はスペースを入れる
// ◯
func getName() -> Strig {
    return "name"
}

// ☓ name snake
func getName() -> Strig {
    return "name"
}

// ☓ break
func getName() -> Strig 
{
    return "name"
}

// ☓ space
func getName()->Strig {
    return "name"
}

変数・プロパティ(保持型・計算型)

  • Camel記法(小文字始まり)で記述する(hogeFuga)
  • 行末に;は付けない
  • privateな保持型プロパティは_から始める(_hogeFuga
  • interlな保持型プロパティはinternalを省略する
    • 理由: 基本的にスコープは厳格にすべきである。
    • しかしprivateスコープはテストファイルから参照できない。
    • テストを書くことの方が重要のため基本的にinternalであることを許可する。
    • internalがデフォルトであるため、冗長を削除するため省略して良しとする。
  • 基本的にpublicは使用しない
    • 理由: 同じモジュール内であれば参照可能なためPublicは必要ない
    • APIを定義する場合は必要
  • 計算型プロパティのgetがある場合はprivateでOK
class Sample {
    // ◯
    var userName: Int;
    private var _userMessage: String;

    func hoge() -> Int {
        let age = 10
        return age
    }

    private var _inSpeed: Double = 0
    var speed: Double {
        get {
            return self._inSpeed + 10;
        }

        set(speed) {
            self._inSpeed = speed
        }
    }

    // ☓ Pascal
    var UserName: String;
    // ☓ not `_`
    private var userLevel: Int;
}
  • storybord(or xib)上で強参照する変数(ViewController.viewが保持するなど)は@IBOutlet weak varで宣言する
// ◯
@IBOutlet weak var clickHogeButton: UIBarButtonItem!

// ☓
@IBOutlet var clickHogeButton: UIBarButtonItem!
  • 読み取り専用のプロパティと添字付けのgetterは暗黙的にする
  • 可能な限り、読み取り専用のプロパティと添字付けではgetキーワードを省略する
  • 理由: 意図と意味が明確だから。コードも少なくて済む。
// ◯
var myGreatProperty: Int {
    return 4
}

subscript(index: Int) -> T {
    return objects[index]
}

// ☓
var myGreatProperty: Int {
    get {
        return 4
    }
}

subscript(index: Int) -> T {
    get {
        return objects[index]
    }
}

enum

  • enum名はPascal記法 (大文字で始まる) で記述する
  • 値もPascal記法で記述する
  • caseは省略せず、caseごとに改行を入れる
    • 理由: 統一性
// ◯
enum CustomResult {
    case Success, 
    case Error
}

// ☓ upper case
enum CustomResult {
    case SUCCESS, ERROR
}

// ☓ Case is not omitted
enum CustomResult {
    case Success, Error
}

if

  • 判定式に()は付けない
  • {の前には改行は入れない
// ◯
let x = 1
if x == 1 {
    print("")
}

// ☓
let x = 1
if (x == 1) {
    print("")
}

for

  • for-inを使用する
  • {の前には改行は入れない
  • cの構文for(let i; i < 10; i++)は使わない(非推奨になっている)
// ◯
var sum: Int = 0
for i in 1...10 {
    sum += i
}

// ☓
var sum: Int = 0
for (var i = 1; i <= 10; i += 1) {
    sum += i
}

switch

  • {の前には改行は入れない
  • switchcaseの左は揃える
// ◯
let c: Int = 1
switch c {
case 1:
    print("")
}

// ☓
let c: Int = 1
switch c 
{
    case 1:
        print("")
}

可能な限りvar宣言よりもlet宣言を使う

  • 理由: letは値が変わらないことを保証するためより安全で明確なコードになる

1行の文字数

  • 努力目標:80文字
  • 最大:100文字

識別子に型を指定する時は、常に識別子のすぐ後ろにコロンを置き、空白を一つあけて型名を書く

// ◯
class MyClass: SuperClass { ... }
let myConst: Int = 2
func myFunc(type: Int) -> String { ... }

// ☓
class MyClass:SuperClass { ... }
let myConst:Int = 2
func myFunc(type:Int) -> String { ... }

明示的なself参照は必要な時だけ

  • selfの持つプロパティやメソッドへアクセスする時、デフォルトではselfへの参照は省く
  • 理由: クロージャにおけるselfのキャプチャリングが目立つようになる。その他の場所における冗長さが無くなるため。
class MyClass {
    var hoge: Int

    // ◯
    func myFunc() {
        hoge = 1234
    }

    var myClosure: () -> () {
        return {
            self.hoge
        }
    }

    // ☓
    func myFunc() {
        self.hoge = 1234
    }
}

【Swift 2.0】クラスのプロパティ名を取得する

swiftでクラスからプロパティ名のリストを取得する方法です。

static func propertyNames() -> Array<String> {
    var names: Array<String> = []
    var count: UInt32 = 0
    self.classForCoder()
    let properties:UnsafeMutablePointer<objc_property_t> = class_copyPropertyList(self.classForCoder(), &count)
    for var i: UInt32 = 0; i < count; i++ {
        let property = properties[Int(i)];
        let cname = property_getName(property)
        let name = String.fromCString(cname)
        names.append(name!)
    }
    free(properties)
    return names
}

【Rails4】GrapeをつかってAPIを作成する

Grapeとは

REST-likeなAPIを作成する為のフレームワークです。 詳しくは、Grape githubを参照してください。

今回は、サンプルアプリケーションを作成し、jsonを返すAPIを動かしてみます。

サンプルプロジェクトにGrapeをインストール

任意のRailsアプリケーションを作成します。

rails new grape_api --skip-bundle -T

Gemのインストール

作成したアプリケーションのGemfileを編集し、grapeのGemをインストールします。

■ Gemfile

gem 'grape'
bundle install
// ※ローカルにインストールする場合は、--path vendor/bundleをつける

ディレクトリを作成する

API経由でデータを参照するために任意のモデルを作成します。

# userテーブルを作成しDBへ反映
rails g scaffold user name:string
rake db:migrate

ディレクトリは、下記のような構成にします。

- app
  - apis
    - api
      -v1
       - users
      -vXX
# とりあえず、v1のディレクトリのみ作成
mkdir -p app/apis/api/v1

今回は、上記のファイル構成を元に、

  • http://[ホスト]/api/v1/users
  • http://[ホスト]/api/v1/users/[user_id]

という2つのAPIを作成します。

ファイルの自動インポートの設定を行なう

apisディレクトリ配下に置かれたファイルが自動的にRailsのファイルパスに追加されるようにapplication.rbを以下のように編集します。

■ config/application.rb

module GrapeApi
  class Application < Rails::Application
    # app/api配下の.rbファイルを読み込み対象にする
    config.paths.add File.join('app', 'apis'), glob: File.join('**', '*.rb')
    config.autoload_paths += Dir[Rails.root.join('app', 'apis', '*')]
  end
end

APIのパスをルーティングする

APIのルートパスを定義します。

■ config/routes.rb

Rails.application.routes.draw do
・・・
  # APIモジュールのBaseクラスを'/'Pathとして定義する
  mount API::Base => '/'
・・・
end

Baseクラスを作成

ルーティングしたAPI::Baseクラスを定義します。
prefixにapiを定義することでhttp://[host]/apiのpathを構成することができます。

■ /app/apis/api/base.rb

module API
  class Base < Grape::API
    # Baseクラスのpathを定義する
    prefix 'api'

  end
end

上記のBaseクラスと同じようにv1ディレクトリ配下にもbase.rbを作成します。
versionを定義することでhttp://[host]/api/v1のpathを構成することができます。
formatAPIのレスポンスデータのフォーマットをjsonに指定しています。

■ /app/apis/api/v1/base.rb

module API
  module V1
    class Base < Grape::API
      version 'v1'
      format :json
    end
  end
end

先ほど作成したAPI::BaseAPI::V1::Baseをマウントします。

■ /app/apis/api/base.rb

module API
  class Base < Grape::API
    # Baseクラスのpathを定義する
    prefix 'api'

    # v1のBaseクラスをマウントする
    mount API::V1::Base
  end
end

apiクラスを作成

v1ディレクトリ配下にusersクラスを作成します。

resourceapiのpathを定義しています。

■ /app/apis/api/v1/users.rb

module API
  module V1
    class Users < Grape::API
      # `users`resource配下にすることで
      # /api/v1/usersのapiとしてアクセスできる
      resource :users do
        # GET http://[host]/api/v1/users
        get do
          User.all
        end

        # パラメタバリデーション
        params do
          requires :id, {type: Integer, desc: 'user id.'}
         end
         # GET http://[host]/api/v1/users/[user_id]
         get ':id' do
          User.find(params[:id])
         end
      end
    end
  end
end

API::V1::UsersAPI::V1::Baseにマウントします。

■ /app/apis/api/v1/base.rb

module API
  module V1
    class Base < Grape::API
      version 'v1'
      format :json
    end
    mount API::V1::Users
  end
end

サンプルデータを作成してAPIにアクセス

ローカル環境でRailsサーバを起動します。

rails server

http://localhost:3000/usersにアクセスして、画面上から適当にデータを作成します。

f:id:inon29:20150907082655p:plain

  • http://localhost:3000/api/v1/usersにアクセスするとUsersテーブルのデータの全件が取得できます。

f:id:inon29:20150907082705p:plain

  • http://localhost:3000/api/v1/users/1にアクセスするとUsersテーブルのデータの1件目が取得できます。

f:id:inon29:20150907082714p:plain

参考サイト

http://kzy52.com/entry/2014/11/07/084023 http://qiita.com/hkusu/items/2ca0323cc276ab31e926

【cocos2d-x】【CocoStudio】CocoStudioを使って画面のレイアウトを読み込む

環境

cocos2d-x 3.2
CocoStudio for Mac 1.0.0 Bata

CocoStudioでレイアウトを作成する

まずは、CocoStudioにて任意の画面を作成します。
以下では、画面に一つボタンを置いています。

cocos.png

上記、赤枠の名前は、各UIパーツのをソースコード上から取得する時に必要になるので、
わかりやすい名前にしておきます。

レイアウトファイルの読み込み

CocoStudioにて、cmd + Eでエクスポート。
[CocoStudioのプロジェクト]/Export配下のに作成したキャンバスのディレクトリが出力されているので、それをそのままXCodeへimportします。

インポートしたレイアウトを以下のようにソースコード上で読み込みます。

#include "cocostudio/CocoStudio.h"
// UIパーツを使用するためにimportしておく
#include "CocosGUI.h"

auto node = cocostudio::timeline::NodeReader::getInstance()->createNode("[jconファイル名]");

// 画面に配置
addChild(node);

子ノードを取得する

レイアウト上に配置した、子ノードを取得するにはgetChildByName()を使います。
ここで指定するオブジェクト名は、CocoStudio上で名前に指定した文字列です。

auto button = (Button *)scene->getChildByName("[オブジェクト名]");


階層が深い位置のオブジェクトを取得するには、以下のヘルパーメソッドを使います。

utils::findChildren([探索対象ノード], [オブジェクト名])

Buttonイベントを取得するとき

参考までに、ui::Buttonクラスのタッチイベントを取得するには以下のようにします。

button->addTouchEventListener([&](Ref *ref, Widget::TouchEventType type) {
    if (type == Widget::TouchEventType::ENDED) { // タッチアップを取得する
    }
});

参考

http://qiita.com/kyokomi/items/9e3dea9325047940f3e0
http://iphone-labo.blogspot.jp/2014/08/cocos2dx-v32node.html
http://giginet.hateblo.jp/entry/2014/08/29/185519
http://raharu0425.hatenablog.com/entry/2014/06/20/154718

【cocos2d-x】cocos2d-x 3.X系を使ってみる① (セットアップ)

最近、ゲーム開発に携わる機会があり、
ゲーム開発の大手プラットホームであるcocos2d-xについて触れてみました。

cocos2d-xは、2Dゲームをつくることに優れたプラットホームで、
開発の方法はわかり易いのです。 ただ、2.X系と3.X系で大きな違いがあったり、ビルド関連でハマったり色々つまずいたのでメモしておきます。

cocos2d-xのセットアップ

1.ダウンロード

cocos2d-xを以下のページからダウンロードします。

公式ページ

ダウンロードしたcocos2d-xのディレクトリを任意の場所に配置します。

2.setup.pyを実行

手順1でダウンロードしたディレクトリへ移動し、直下にあるsetup.pyファイルを実行する。

python setup.py

色々と足りないパスを聞かれるので、設定されていないパスがあれば設定を行います。
これで、開発する為の準備が完了しました。

3.新規プロジェクトを作成する

作成するゲームのプロジェクトを作成します。
cocos2d-x 3.X系で新規プロジェクトを作成するには、cocosコマンドを使用します。

cocos new [プロジェクト名] -p [パッケージ名] -l [開発言語(cpp)] -d [プロジェクトを置くディレクトリ名]

■ 各引数の簡単な解説

-p Androidプロジェクトのパッケージ名。iOSBundle identifierも共通かなと思ってましたが、ここは変わりませんでした。

-l 開発言語を指定します。対応する言語は次のとおりです。 c++ => "cpp", lua => "lua", javascript => "js"

-d プロジェクトを配置するRootディレクトを指定します

作成されたディレクトリを確認すると、以下のようなディレクトリが作成されているかと思います。
これが各開発環境のプロジェクトディレクトリになります。

proj.android
proj.ios_mac
proj.linux
proj.win32
proj.wp8-xaml

プロジェクトをビルド

プロジェクトが正しく作成できたら、試しにプロジェクトをビルドします。
ここでは、iOS用のプロジェクトをビルドしてみます。

cocos run -s [プロジェクトディレクトリ] -p ios

ビルドが成功すると、iPhoneシュミレータが起動して以下のような画面が表示されているかと思います。

f:id:inon29:20140916001114p:plain


これで、開発のための準備ができました。
今回は、iOSAndroidのアプリをターゲットにしているので、 今後は、各OSでのハマりどころなどを押さえていきたいと思います。

Apache用のVirtualHostの設定メモ(for Mac)

■ 環境

Mac OSX 10.9.3 (Mavericks)

Apacheの関連ファイルパス

httpd.conf

/private/etc/apache2/httpd.conf

アクセスログ

/private/var/log/apache2/access_log

エラーログ

/private/var/log/apache2/error_log

各ユーザー設定ファイル

/private/etc/apache2/extra/httpd-userdir.conf
/private/etc/apache2/users/[ユーザー名].conf

1.httpd.confの設定

ユーザーの設定ファイルは、以下のファイルに記述します。

/private/etc/apache2/users/[ユーザー名].conf

設定内容は、以下の通りです。

<VirtualHost *:80>
    ServerName [ホスト名]
    DocumentRoot [ドキュメントルートPath]
    <Directory [ディレクトリパス ※ドキュメントルートと同じ]>
        Options Indexes FollowSymLinks Includes ExecCGI
        AllowOverride all
        Options -MultiViews
        Order allow,deny
        Allow from all 
    </Directory>
</VirtualHost>

2.hostsの編集

以下のファイルにhostの設定を記載します。

/etc/hosts


127.0.0.1  [ホスト名]

【Android】他のアプリケーションの上にViewを表示する

Androidのアプリの中には、 何かのアプリを起動中もずっと画面上に残り続けるアプリがあると思います。

今回は、上記のように他のアプリケーション上で、
自分のアプリケーションのViewを表示する方法について試して見ました。


Androidのviewは、複数のレイヤーによって構成されており、
通常のアプリケーションのレイヤーは、他のアプリケーションの画面上に乗せることができません。

常に画面上に自分のアプリケーションのViewを表示し、
他のアプリケーションが起動中も自分のアプリのViewを画面上に表示するためには、WindowManagerを使ってレイヤーを指定する必要があります。


通常のアプリケーションの上に載せられるレイヤーには、
幾つか種類がありますが、今回は、画面上のViewに対してタッチイベントを取得したかったので、WindowManager.LayoutParams.TYPE_SYSTEM_ALERTを使用しました。


Androidのレイヤーについては、下記の参考リンクにて詳しく説明されているので、ご参照ください。

使い方は、
以下のようにまずWindowManagerインスタンスを取得し、
画面上に出したいViewをWindowManagerに対してaddView()します。

その際に、LayoutParamsにWindowManager.LayoutParams.TYPE_SYSTEM_ALERTを指定することで、
通常のアプリケーションよりも上にViewを表示することが可能となります。

WindowManager.LayoutParams  Params = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,       // アプリケーションのTOPに配置
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |  // フォーカスを当てない(下の画面の操作がd系なくなるため)
            WindowManager.LayoutParams.FLAG_FULLSCREEN |        // OverlapするViewを全画面表示
            WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,  // モーダル以外のタッチを背後のウィンドウへ送信
            PixelFormat.TRANSLUCENT);  // viewを透明にする

WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
windowManager.addView([OverlapView], mOverlapViewParams);


通常のアプリケーションよりも上にViewを表示するため、
使用後は、removeView()でViewを削除しないと、
画面上にずっと出続けてしまう危険がありますので注意が必要です。

[サンプル]

以下のサンプルでは、
ActivityからServiceをBindし、ServiceからWindowmanagerに対して、
ViewをaddView()しています。

アプリケーションが終了したタイミングで、
画面上に表示しているViewも一緒に削除されるようにServiceのunbindのタイミングで、
removeView()を呼び画面からViewを削除しています。

■ OverlapService.java

public class OverlapService extends Service {
    
    private WindowManager mWindowManager;
    private FrameLayout mOverlapView;
    private WindowManager.LayoutParams mOverlapViewParams;
    
    // Serviceに接続するためのBinderクラスを実装する
    public class LocalBinder extends Binder {
        //Serviceの取得
        OverlapService getService() {
            return OverlapService.this;
        }
    }
    // Binderの生成
    private final IBinder mBinder = new LocalBinder();
    
    @Override
    public IBinder onBind(Intent intent) {
        // Service接続時に呼び出される
        // 戻り値として、Serviceクラスとのbinderを返す。
        Log.i("", "onBind" + ": " + intent);
        return mBinder;
    }
 
    @Override
    public void onRebind(Intent intent){
        // Unbind後に再接続する場合に呼ばれる
        Log.i("", "onRebind" + ": " + intent);
    }
 
    @Override
    public boolean onUnbind(Intent intent){
        // Service切断時に呼び出される
        // onUnbindをreturn trueでoverrideすると次回バインド時にonRebildが呼ばれる
        return true;
    }
    
    @Override
    public void onCreate() {
        super.onCreate();
        
        mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        LayoutInflater layoutInflater = LayoutInflater.from(this);
        mOverlapView = new FrameLayout(getApplicationContext());
        ((FrameLayout)mOverlapView).addView(layoutInflater.inflate(R.layout.Overlap_content_view, null));
        mOverlapViewParams = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,       // アプリケーションのTOPに配置
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |  // フォーカスを当てない(下の画面の操作がd系なくなるため)
            WindowManager.LayoutParams.FLAG_FULLSCREEN |        // OverlapするViewを全画面表示
            WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, // モーダル以外のタッチを背後のウィンドウへ送信
            PixelFormat.TRANSLUCENT);  // viewを透明にする

        mWindowManager.addView(mOverlapView, mOverlapViewParams);
    }
    
    @Override
    public void onDestroy() {
        // ServiceがunbindされるタイミングでViewも削除して上げる
        mWindowManager.removeView(mOverlapView);
        super.onDestroy();
    }
}

■ MainActivity(呼び出し元)

public class MainActivity extends Activity {
    
    // Serviceとのインターフェースクラス
    private ServiceConnection mConnection = new ServiceConnection() {
        OverlapService mBindService;
        public void onServiceConnected(ComponentName className, IBinder service) {
            // Serviceとの接続確立時に呼び出される。
            // service引数には、Onbind()で返却したBinderが渡される
            mBindService = ((OverlapService.LocalBinder)service).getService();
            //必要であればmBoundServiceを使ってバインドしたServiceへの制御を行う
        }
         
        public void onServiceDisconnected(ComponentName className) {
            // Serviceとの切断時に呼び出される。
            mBindService = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        Button bindBtn = (Button) findViewById(R.id.bind_service_button);
        bindBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent(MainActivity.this,OverlapService.class);
                bindService(i, mConnection, Context.BIND_AUTO_CREATE);
            }
        });
        
        Button unbindBtn = (Button) findViewById(R.id.unbind_service_button);
        unbindBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Serviceをunbindする
                unbindService(mConnection);
            }
        });
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Serviceをunbindする
        unbindService(mConnection);
    }
}

(参考リンク)
http://techbooster.org/android/ui/13182/
http://matsuhilog.blogspot.jp/2011/06/typesystemalert.html