【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 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
{
の前には改行は入れないswitch
とcase
の左は揃える
// ◯ 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
今回は、上記のファイル構成を元に、
という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を構成することができます。
format
でAPIのレスポンスデータのフォーマットをjson
に指定しています。
■ /app/apis/api/v1/base.rb
module API module V1 class Base < Grape::API version 'v1' format :json end end end
先ほど作成したAPI::Base
にAPI::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クラスを作成します。
resource
でapiの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::Users
をAPI::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
にアクセスして、画面上から適当にデータを作成します。
http://localhost:3000/api/v1/users
にアクセスするとUsersテーブルのデータの全件が取得できます。
http://localhost:3000/api/v1/users/1
にアクセスするとUsersテーブルのデータの1件目が取得できます。
参考サイト
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にて任意の画面を作成します。
以下では、画面に一つボタンを置いています。
上記、赤枠の名前
は、各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プロジェクトのパッケージ名。iOSのBundle 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シュミレータが起動して以下のような画面が表示されているかと思います。
これで、開発のための準備ができました。
今回は、iOS、Androidのアプリをターゲットにしているので、
今後は、各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