Amarronの日記

iOSやMac、Web系の記事を書きます。

MBaaS 比較

f:id:Amarron:20121004112316j:plain

概要


MBaaS(「エムバース」と読みます)はMobile Backend as a Serviceの略で,去年の中頃から米国で使われるようになった用語です。一般的にBaaSと呼ばれているサービスは,IaaS(Infrastructure as a Service)やPaaS(Platform as a Service)と比較すると,プログラミングや構築,運用の手間がかからないものを指す場合が多く,MBaaSはモバイル版BaaSですので,iOS, Android, JavaScriptなどのモバイルアプリの開発環境でそのまま使えるSDKが提供されています。いろいろなサーバ機能がすべて,SDKAPI経由で利用できるので,アプリを開発するだけで,リリースすることができます。ユーザ管理,データ管理,プッシュなどの機能がひと通り揃っていて,そのサービスを使うだけで大体のことができてしまう汎用的なMBaaSは,米国のParse社,Kinvey社,StackMob社,日本のKii株式会社などが提供しています。

参考URL:第2回 MBaaSとは

サービス比較


比較1

比較2

機能の種類 Parse ニフティクラウド アピアリーズ Kii Cloud
ユーザー 会員管理・認証 会員管理・認証 ユーザー管理・認証 ユーザー管理
権限管理 権限管理 権限管理 アクセス制限機能 Objectに対するアクセス権
データストア データストア データストア データベース機能 データ管理
ファイルストア ファイルストア ファイルストア ファイル機能 Object Bodyのアップロード
位置情報 位置情報検索 位置情報検索 ロケーション機能 位置情報
プッシュ通知 プッシュ通知 プッシュ通知 プッシュ通知 プッシュ通知
SNS連携 SNS連携 SNS連携 - 外部サービスを利用した認証
アプリ分析 アプリ分析 - - アプリ分析
バックグラウンドジョブ バックグラウンドジョブ - - スケジュール起動
サーバー - - - サーバー機能拡張
A/Bテスト - - - A/Bテスト
APIリクエスト数 30/秒 200万回/月 10,000/月 100万回/月
プッシュ通知 100万回(100万の端末)/月 200万回/月 500/月 100万回/月
ストレージ データストレージ 20GB
ファイルストレージ 20GB 基本ストレージ 5GB 基本ストレージ 100MB 1GB
データ転送 2TB - 1GB -
その他 バックグラウンドジョブ 1 ファイルサイズ制限 5MB - A/Bテスト 1/月

参考URL:無料で十分試せるMBaaS、ニフティクラウド mobile backendを使ってみる

その他(MBaasとPaasの組み合わせ)

iOS HTTP メモ

  • iOSのHTTP関係のプログラムやメモです
  • 随時更新していく予定です

Flow


HTTP リクエストを発行しレスポンスを受信するまでのプログラムの流れはだいたい次の通り。

NSURLConnectionの場合

  1. リクエストヘッダ(NSURLRequest)を作る
  2. NSURLConnectionのクラスメソッドを用いリクエストを発行する
  3. レスポンスヘッダ(NSURLResponse)とレスポンスボディを取得する

NSURLSessionの場合

  1. セッションクラス(NSURLSession)を作る
  2. タスクを生成する
  3. タスクの実行する
// playgroundの場合↓のコメントアウトを消す
// import XCPlayground

/*
* NSURLConnection(非同期)
*/
func myNSURLConnectionAsync() {
    // 通信先のURLを生成.
    let url:NSURL = NSURL(string:"http://httpbin.org/get")!
    // リクエストを生成.
    let request:NSURLRequest = NSURLRequest(URL:url)
    let queue:NSOperationQueue = NSOperationQueue()
    // NSURLConnectionを使ってアクセス
    NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
        // 帰ってきたデータを文字列に変換.
        var result = NSString(data: data, encoding: NSUTF8StringEncoding)!
        println("myNSURLConnectionAsync->\(result)")
    })

    // playgroundの場合↓のコメントアウトを消す
    // XCPSetExecutionShouldContinueIndefinitely(continueIndefinitely: true)
}

/*
* NSURLConnection(同期)
*/
func myNSURLConnectionSync() {
    // 通信先のURLを生成.
    let url:NSURL = NSURL(string:"http://httpbin.org/get")!
    // リクエストを生成.
    let request:NSURLRequest = NSURLRequest(URL:url)
    // NSURLConnectionを使ってアクセス
    var response: NSURLResponse?
    var error: NSError?
    let data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error)
    var result = NSString(data: data!, encoding: NSUTF8StringEncoding)!
    println("myNSURLConnectionSync->\(result)")
    
    // playgroundの場合↓のコメントアウトを消す
    // XCPSetExecutionShouldContinueIndefinitely(continueIndefinitely: true)
}

/*
* NSURLSession(非同期)
*/
func myNSURLSession() {
    // 通信先のURLを生成.
    let url:NSURL = NSURL(string: "http://httpbin.org/get")!
    // セッションの生成.
    let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
    // 通信のタスクを生成.
    let task = session.dataTaskWithURL(url, completionHandler: {
        (data, response, err) in
        
        // 帰ってきたデータを文字列に変換.
        var result = NSString(data: data, encoding: NSUTF8StringEncoding)!
        println("myNSURLSession->\(result)")
        
    })
    // タスクの実行.
    task.resume()

    // playgroundの場合↓のコメントアウトを消す
    // XCPSetExecutionShouldContinueIndefinitely(continueIndefinitely: true)
}

参考UTL:NSURLSession のまとめ

Json


jsonを取得しパース

func myNSURLConnectionSync() {
    // 通信先のURLを生成.
    let url:NSURL = NSURL(string:"http://httpbin.org/get")!
    // リクエストを生成.
    let request:NSURLRequest = NSURLRequest(URL:url)
    // NSURLConnectionを使ってアクセス
    var response: NSURLResponse?
    var error: NSError?
    let data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error)
//    var result = NSString(data: data!, encoding: NSUTF8StringEncoding)!
//    println("myNSURLConnectionSync->\(result)")
    
    // Jsonでパース
    let json:NSDictionary = NSJSONSerialization.JSONObjectWithData(data!,
        options: NSJSONReadingOptions.AllowFragments, error: nil) as! NSDictionary
    for (name, value) in json {
        println("key:\(name) value:\(value)")
    }
}

GCD


GCDを使った非同期完了後の処理

func myNSURLConnectionAsync() {
    // 通信先のURLを生成.
    let url:NSURL = NSURL(string:"http://httpbin.org/get")!
    // リクエストを生成.
    let request:NSURLRequest = NSURLRequest(URL:url)
    let queue:NSOperationQueue = NSOperationQueue()
    // NSURLConnectionを使ってアクセス
    NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
        // 帰ってきたデータを文字列に変換.
        var result = NSString(data: data, encoding: NSUTF8StringEncoding)!
        println("myNSURLConnectionAsync->\(result)")
        
        // GCD(Grand Central Dispatch)
         dispatch_async(dispatch_get_main_queue(), {
              // 完了後に実行する処理(self.tableView.reloadData()等)
          })
    })

}

参考URL:[Swift 1.1] swiftで api を叩いて、JSONをパースして、表示させる方法 (xcodeは6.1, iOSは8.1)

Auth


cookie


Cookieの永続化

// Cookieの永続化
import UIKit

class AppDelegate: UIResponder, UIApplicationDelegate {
    let SavedHTTPCookiesKey = "SavedHTTPCookies"

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
            self.window = UIWindow(frame: UIScreen.mainScreen().bounds)

        self.loadCookie()
        
        return true
    }
    func applicationDidEnterBackground(application: UIApplication) {
        
        self.saveCookie()
    }

    func saveCookie() {
        let cookiesData:NSData = NSKeyedArchiver.archivedDataWithRootObject(NSHTTPCookieStorage.sharedHTTPCookieStorage().cookies!)
        NSUserDefaults.standardUserDefaults().setObject(cookiesData, forKey: SavedHTTPCookiesKey)
    }
    
    func loadCookie() {
        if let cookiesData:NSData = NSUserDefaults.standardUserDefaults().objectForKey(SavedHTTPCookiesKey) as? NSData {
            for cookie:NSHTTPCookie in NSKeyedUnarchiver.unarchiveObjectWithData(cookiesData) as [NSHTTPCookie] {
                NSHTTPCookieStorage.sharedHTTPCookieStorage().setCookie(cookie)
            }
        }
    }
}

Custom URL Scheme


  • Custom URL Schemeの処理をシンプルに書く
    • URLの設計:scheme://controller/action?query
    • アプリ側は受け取ったcontrollerを元に、使用するcontrollerクラスを選び、そのcontrollerクラスにactionとqueryを渡す。
    • 各actionの挙動については各々のcontrollerに任せる
import UIKit

class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject?) -> Bool {
        var result:Bool = true

        let schema:String = url.scheme!
        let host:String   = url.host!
        let action:String = url.lastPathComponent!
        let query:String  = url.query!
        return result
    }
}

外部ライブラリ


note

  • 特にこだわりがないようであれば、Alamofireを使ったほうがよさそう(画像のキャッシュとか自分でコードを書く量が最終的に少なくてすみそう) f:id:Amarron:20150401221639p:plain

HTTPの確認に便利


参考URL


プッシュ通知 証明書 更新 手順

更新手順


No. 内容 Development Product 備考
0 KeyChain(開発環境)->証明局に証明書(CertificateSigningRequest.certSigningRequest)を要求 Development・Product共通、複数のアプリで共通で使う
1 Deveropper Center(Safari)->Certificates の作成 アプリごと、No.0で要求した「CertificateSigningRequest.certSigningRequest」ファイルを使う
2 Deveropper Center(Safari)->Provisioning Profiles の更新 アプリごとに対応必要。アプリのバージョンアップ時に必要
2 Xcode(開発環境)->Buid Setting->Provisioning Profile のダウンロード アプリごとに対応必要。アプリのバージョンアップ時に必要。新しくProvisioning Profileは作成されるが、特に更新しなくてもプッシュ通知は受信できる
3 KeyChain(開発環境)->p12ファイルを書き出す アプリごとに対応必要。鍵ではなく証明書で書きだす
  • No.0
    • f:id:Amarron:20150401221129p:plain
  • No.3
    • f:id:Amarron:20150401221114p:plain

手順

  1. Certificates の削除(更新前のCertificatesの有効期限が切れていない場合)・作成(Apple Push Notification service SSL)→作成後ダウンロード
  2. Provisioning Profiles の更新
  3. p12ファイルを書き出す(キーチェーンより「1.Certificates」で作成した証明書を選択し書き出し)
  4. p12ファイルの鍵を書き出す(キーチェーンより「1.Certificates」で作成した鍵を選択し書き出し)(サーバー側で必要であれば)
  5. p12をpemファイルへ変換する(サーバー側で必要であれば)
  6. サーバへp12またはpemファイルを配布

p12からpemへの変換コマンド

openssl pkcs12 -clcerts -nokeys -out apns-dev-cert.pem -in apns-dev-cert.p12  
openssl pkcs12 -nocerts -out apns-dev-key.pem -in apns-dev-key.p12  
openssl rsa -in apns-dev-key.pem -out apns-dev-key-noenc.pem  
cat apns-dev-cert.pem apns-dev-key-noenc.pem > apns-dev.pem

参考URL

プッシュ通知文字数(おまけ)


表示箇所 iOS6 iOS7
ステータスバー(画面上部) 全角22文字、半角40文字 全角35文字、半角64文字
ダイアログ(ロック画面に表示) 全角57文字、半角108文字 全角58文字、半角114文字
通知センター 全角43文字、半角154文字 全角58文字、半角114文字

参考URL:http://www.hangout.co.jp/blog/archives/295

iOS 開発 カメラ

  • iOS(iPhone,iPad)のカメラ、写真の開発時のメモです
  • カメラを起動したり、フォトアルバムから写真を選択してアプリに渡したりできます
  • アプリで高解像度(1MB以上)の写真や画像を大量に表示するとメモリ不足でクラッシュします(100KB位まで解像度を下げるのがオススメ)

f:id:Amarron:20150320001518p:plain

写真やムービーの撮影


  • 2つのテクノロジー
    1. UIImagePickerController:カスタマイズ可能な基本的なユーザイン ターフェイス
    2. AVFoundation FW:UIKitと一緒に使用し完全にカスタマイズした静止画像やムービーのキャプチャ機能をアプリケーション向けに作成することができる
  • どちらを採用する場合も、Assets Library FWを使用して、メディアのメタデータGPS位置情報など)を管理できる

保存済みの写真やムービーをユーザのフォトアルバムから選択


  • 2つのテクノロジー
    1. UIImagePickerController:基本的なユーザインターフェイスを使用してユーザがフォトライブラリからメディアを選択できる
    2. Assets Library FW:UIKitと一緒に使用し完全にカスタマイズした写真やムービーのブラウザを作成できる

Image Picker Controllerを利用した写真やムービーの撮影


  1. カメラインターフェイス(UIImagePickerControllerクラスのインスタンス)をインスタンス化してモーダルモードで表示
  2. システムは、このカメラインターフェイスとユーザとのやり取りを管理(ユーザが写真やムービーを撮影、またはキャンセル)
  3. システムはImagePickerControllerのデリゲートオブジェクトのメソッドを呼び出し今度は、このメソッドがユーザの操作(たとえば、新規の写真を「カメラロール(Camera Roll)」アルバムに保存する)の結果を処理。(カメラインターフェイスを消去するのもデリゲート)

参考URL


iOS 保存領域 比較 〜NSUserDefaults, CoreData,Keychain,等〜

アプリ容量の制限について


アプリのストレージの最大容量は、おそらく制限なし(20M以上だとDL時にwifiが必要となる)

f:id:Amarron:20150314205528j:plain

保存領域について


種類 説明
アプリ領域 アプリが自由に使え、他のアプリから参照できない領域
OS領域 OSが使用するデータが保存されており、基本的にアプリから参照できない領域
共有領域 メディアライブラリ(写真、音楽等)やイベント(カレンダー等)、連絡先は全てのアプリから参照することができる領域(アプリからできる内容は、書き込みと閲覧のみ)

永続データの復元(比較)について


  • 復元時にデータも復元するには、外部サーバーにデータを保存するかiCloudを利用しないと殆ど初期化される
  • 保存領域が共有領域であれば、アプリ自体にiCloudの処理をいれなくても、共有領域のデータは復元できそう
種類 復元可否 備考
アーカイブ オブジェクトをバイナリ形式に変換してからファイルに永続化(一番基礎的な技術)
プロパティリスト - ユーザーが変更しないもの(定数)、plist
NSUserDefaults ユーザーが変更するもの
CoreData データ量多い
Keychain パスワードや個人情報(暗号化されていない場合復元されない)

アプリ内にDBとして保存したい場合realmがオススメです。(日本語のドキュメントもしっかりしていて、組み込みも簡単で、ブラウザも用意されています)

参考URL


Xcode デバッグ 値変更

概要

  • Xcodeでデバック中に、変数の値や関数の実行する方法の説明です
  • デバックエリアでLLDBコマンドを実行して確認します
  • 値の変更はデバックエリアで「po 変数名 = 値」で変更できます

LLDBとは、Mac OS X上のXcodeでデフォルトのデバッガで、デスクトップとiOSデバイスとシミュレータ上のC、のObjective-CC++Swiftデバッグをサポートしています。

f:id:Amarron:20150314202752p:plain

実行方法

  1. デバックエリアを表示(メニューバー->View->Debug Area->Show Debug Area)
  2. 止めたいところにブレークポイント設定
  3. プログラムの実行
  4. デバックエリアでLLDBコマンドの実行

実行例

  • 変数の値や関数の実行する場合は、「(lldb) po 変数名 または 関数名」を実行します

f:id:Amarron:20150314202812p:plain 「po self.dynamicType」でプロジェクト名とクラス名を表示している例

LLDBコマンド一覧(一部抜粋)

省略形 内容 正式名
po 式の評価(poはオブジェクト、pは基本型も可) expression -o --
h ヘルプの表示 help
s ステップイン thread step-in
c 続きを実行(次のbreakPointまで) process continue
br l breakPointを一覧表示 breakpoint list
bt 現スレッドのバックトレース情報を表示 thread backtrace

iOS AdMob 実装 方法

AdMob SDKの導入方法や手順が書いてあります。(iPhoneiPadが対象)

やりたいこと

iOSアプリにAdMobのバナー広告を表示させたい。
(AdMobとは、Googleが運営するアプリ専門のアドネットワーク。日本国内の収益性は微妙だが、海外の広告案件が豊富。)

f:id:Amarron:20150215023503p:plain

流れ

  1. AdMobに登録
  2. アプリにSDK組込
  3. 広告ユニットIDの作成

細かい部分はAdMobのスターターズガイドがしっかりしているので、その通り進めば多分大丈夫だと思います。

1. AdMobに登録

AdMobのサイトでユーザー登録をします。
「Sign Up」ボタン押下後適当に個人情報を入力して進みます。
(全文英語ですが、一般的なユーザー登録なので英語読めなくても何とかなるはず。。。)

f:id:Amarron:20150215024507p:plain

2. アプリにSDK組込

今回は、CocoaPodsを利用してSDKを組込みます。
CocoaPodsの利用方法がわからない方はこちら

f:id:Amarron:20150215023529p:plain

2-1. Podfileの作成・SDKインストール

ターミナルを起動しPodfileを作成しSDKをインストールします。

$ vi Podfile   # Podfileの作成
platform :ios, '8.0'
pod 'Google-Mobile-Ads-SDK', '~> 7.0'
$ pod install  # SDKインストール
Analyzing dependencies

CocoaPods 0.36.0.beta.2 is available.
To update use: `gem install cocoapods --pre`
[!] This is a test version we'd love you to try.

For more information see http://blog.cocoapods.org
and the CHANGELOG for this version http://git.io/BaH8pQ.

Downloading dependencies
Installing Google-Mobile-Ads-SDK (7.0.0)
Generating Pods project
Integrating client project

[!] From now on use `BannerExample.xcworkspace`.
$  # 「プロジェクト名.xcworkspace」が作成されていればインストール完了!

2-2. AdMobを実装

とりあえずSDKが読み込めているか試してみましょう

# swift
// Bridging-Header.h
@import GoogleMobileAds

// BannerExampleViewController.swift
class BannerExampleViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        println("Google Mobile Ads SDK version:\(GADRequest.sdkVersion())")
    }
 }
# Objective-C
// BannerExampleViewController.m

@import GoogleMobileAds;
#import "ViewController.h"

@implementation BannerExampleViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  NSLog(@"Google Mobile Ads SDK version: %@", [GADRequest sdkVersion]);
}

@end

次に、バナーを作成し画面表示されたらOKです。
広告ユニットIDは、「3. 広告ユニットIDの作成」で作成し自分のIDに変更しましょう

# swift
// BannerExampleViewController.swift

// GADBannerViewDelegateを追加
class BannerExampleViewController, GADBannerViewDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        println("Google Mobile Ads SDK version:\(GADRequest.sdkVersion())")
        
        var bannerView: GADBannerView = GADBannerView()
        bannerView = GADBannerView(adSize:kGADAdSizeBanner)
        // 広告ユニットIDを指定する(3. 広告ユニットIDの作成参照)
        bannerView.adUnitID = "ca-app-pub-3940256099942544/2934735716" 
        bannerView.delegate = self
        bannerView.rootViewController = self
        self.view.addSubview(bannerView)
        bannerView.loadRequest(GADRequest())
    }
}
# Objective-C
// BannerExampleViewController.h

// SDK から GADBannerView の定義をインポートする
#import "GADBannerView.h"

@interface BannerExampleViewController : UIViewController {
  // インスタンス変数として 1 つ宣言する
  GADBannerView *bannerView_;
}

@end

// BannerExampleViewController.m

#import "BannerExampleViewController.h"

@implementation BannerExampleViewController

- (void)viewDidLoad {
  [super viewDidLoad];

  // 画面上部に標準サイズのビューを作成する
  // 利用可能な広告サイズの定数値は GADAdSize.h で説明されている
  bannerView_ = [[GADBannerView alloc] initWithAdSize:kGADAdSizeBanner];

        // 広告ユニットIDを指定する(3. 広告ユニットIDの作成参照)  bannerView_.adUnitID = MY_BANNER_UNIT_ID;

  // ユーザーに広告を表示した場所に後で復元する UIViewController をランタイムに知らせて
  // ビュー階層に追加する
  bannerView_.rootViewController = self;
  [self.view addSubview:bannerView_];

  // 一般的なリクエストを行って広告を読み込む
  [bannerView_ loadRequest:[GADRequest request]];
}

- (void)dealloc {

  // プロジェクトで ARC を使用している場合は bannerView_ を解放しない
  [bannerView_ release];
  [super dealloc];
}

@end

広告のサイズやターゲットの設定はこちら

3. 広告ユニットIDの作成

  1. AdMob管理画面に「1. AdMobに登録」で作成したアカウントでログイン
  2. 広告ユニットIDの作成
    1. 「収益化->新しいアプリの収益化」ページまで移動
    2. アプリを追加
    3. 広告フォーマットの選択と広告ユニット名の設定
    4. 表示の設定方法
    5. 完了ボタン押下
  3. 広告ユニットIDをアプリに設定
    1. 「収益化->すべてのアプリ」ページまで移動
    2. 自分が追加したアプリの「広告ID」が発行されています

      f:id:Amarron:20150215023554p:plain

    3. 「2-2. AdMobを実装」で設定していた「広告ID」を自分の「広告ID」に変更しましょう

  4. 表示と計測の確認
    1. アプリを起動し広告が表示されているか確認しましょう(広告IDを変更した場合、よく見かける一般的な広告に差し替わっていると思います。)
    2. 広告を押下し外部サービスが起動することを確認しましょう
    3. 管理画面上で計測が正しくとれていることを確認しましょう(リアルタイムに反映されないので時間を置いて確認しましょう(私の場合1時間位で反映されていました))

以上で全て終了です。お疲れ様でした!