Passion+Action+Sincerely=Success!

週末にのみ趣味でコーディングするおやじの備忘録

Swiftのenumが素敵すぎる Part2

前回の投稿で、以下のSwiftの列挙型(enum)がもつ固有の性質のうち、#3と#4を使ってサーチプログラムをリファクタリングした。
今回は、#1と#2を使って、さらにリファクタリングするぜ。

Swiftの列挙型の固有な性質:
1. メンバーに具体的な値をいれる必要はない。メンバーを値として扱える。
2. メンバーに付随する型を設定できる。
3. メンバーに値を入れた場合は、Objective-CのNS_ENUMと同様に扱える。
4. 列挙型自体を値型としてオブジェクトとして扱え、プロパティーやメソッドも定義できる。

問題:
サーチプログラムのコアとなる以下の関数では、サーチ状態を表すのに、大きく3つのパラメタ(isLoading, HasSearched, searchResults)を使っている。なんだけど、本当に興味があるのは、以下の4つの状態のはずで、これらの状態を3つのパラメタ(isLoading, HasSearched, searchResults)の組み合わせで判断する必要がある。コードが美しくないし、わかりにくいので、バグを誘発しそうだ。こういう時に、enumの#1と#2を使ってコードをすっきりできるぜ。

1) サーチ処理実行前
2) サーチ実行中
3) サーチ実行完了かつ該当の項目が一つもない
4) サーチ実行完了かつ該当の項目が一つ以上あり

typealias SearchComplete = (Bool) -> Void

func performSearchForText(text: String, category: Category, completion SearchComplete) { 

  if !text.isEmpty {
    dataTask?.cancel()
    isLoading = true
    hasSearched = true
    searchResults = [SearchResult]()
  
    let url = urlWithSearchText(text, category: category)
  
    let session = NSURLSession.sharedSession()
  
    dataTask = session.dataTaskWithURL(url, completionHandler: {
        data, response, error in 

        var success = false

        if let error = error {
          if error.code == -999 { return } // Search was cancelled
        } else if let httpResponse = response as? NSHTTPURLResponse { 
          if httpResponse.statusCode == 200 {
            if let dictionary = self.parseJSON(data) { 
              self.searchResults = self.parseDictionary(dictionary) 
              self.searchResults.sort(<)
              println("Success! ") 
              self.isLoading = false 
              success = true
            } 
          }
        }
        if (!success) {
           self.hasSearched = false 
           self.isLoading = false
        }
       
        dispatch_async(dispatch_get_main_queue()) {
           SearchCompletion(success)
        }
    })

  dataTask?.resume() }
}

素直に、enumを使えば、以下のように定義できる。これらは、先にのべた本当に興味のある状態を表している。
#1の性質にあるように、4つの状態すべてに値を定義しておらず、それぞれのメンバを値として扱える。
また、4つめの.Resultsのケースが特殊で、#2の性質にあるように、Swiftenumは、メンバーに付随する型を設定できる。ここでは、SearchResultオブジェクトのArrayを設定している。

enum State {
 case NotSearchedYet  // サーチ処理実行前
 case Loading //サーチ実行中
 case NoResults //サーチ実行完了かつ該当の項目が一つもない
 case Results([SearchResult]) //サーチ実行完了かつ該当の項目が一つ以上あり
}

上記の状態(State)に従って、先のPerformSearchForText関数をリファクタリングをしたのが以下である。

func performSearchForText(text: String, category: Category, completion SearchComplete) { 

  if !text.isEmpty {
    dataTask?.cancel()
    
    state = .Loading //サーチ実行中
  
    let url = urlWithSearchText(text, category: category)
  
    let session = NSURLSession.sharedSession()
  
    dataTask = session.dataTaskWithURL(url, completionHandler: {
        data, response, error in 

        self.state = .NotSearchedYet // サーチ処理実行前
        var success = false

        if let error = error {
          if error.code == -999 { return } // Search was cancelled
        } else if let httpResponse = response as? NSHTTPURLResponse { 
          if httpResponse.statusCode == 200 {
            if let dictionary = self.parseJSON(data) { 
              var searchResults = self.parseDictionary(dictionary) 
              if searchResults.isEmpty {
                 self.state = .NoResults //サーチ実行完了かつ該当の項目が一つもない
              } else {
                 searchResults.sort(<)
                 self.state = .Results(searchResults) //サーチ実行完了かつ該当の項目が一つ以上あり
                 success = true
            } 
          }
        }
       
        dispatch_async(dispatch_get_main_queue()) {
           SearchCompletion(success)
        }
    })

  dataTask?.resume() }
}

このようにenumを使えば、処理状態の管理が容易になり、かつ、ある状態にのみ付随する情報をまとめて設定できるので便利だ。
参考に、上記で、.Resultsに付随する情報として設定したsearchResultsを読み出す処理例として、状態(state)に応じて、tableViewの構成する各Cellを返す関数の例を以下に記載しておく。

  func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        
        switch search.state {
        case .NotSearchedYet:
            fatalError("Should never get here")
        case .Loading:
            let cell = tableView.dequeueReusableCellWithIdentifier(TableViewCellIdentifiers.loadingCell, forIndexPath: indexPath) as UITableViewCell
            let spinner = cell.viewWithTag(100) as UIActivityIndicatorView
            spinner.startAnimating()
            
            return cell
            
        case .NoResults:
            return tableView.dequeueReusableCellWithIdentifier(
            TableViewCellIdentifiers.nothingFoundCell, forIndexPath: indexPath) as UITableViewCell
            
        case .Results(let list):
            let cell = tableView.dequeueReusableCellWithIdentifier(
                TableViewCellIdentifiers.searchResultCell, forIndexPath: indexPath) as SearchResultCell
            
            let searchResult = list[indexPath.row]
            cell.configureForSearchResult(searchResult)
            return cell
        }
    }

U

Swiftのenumが素敵すぎる

Swiftの列挙型(enum)が便利すぎて泣けてきた。
今回は、Swiftの列挙型がもつ、固有な性質を使って、コードのリファクタリングを実施してみた。


Swiftの列挙型には、以下の固有な性質があるんです!素敵だ。
1. メンバーに具体的な値をいれる必要はない。メンバーを値として扱える。
2. メンバーに付随する型を設定できる。
3. メンバーに値を入れた場合は、Objective-CのNS_ENUMと同様に扱える。
4. 列挙型自体を値型としてオブジェクトとして扱え、プロパティーやメソッドも定義できる。


特に、今回は#3と#4の性質を使って、コードをリファクタリングしてみる。

問題: 以下の関数を実際に使用する際、category値として、segmentedContrrol.selectedSegmentIndexを入力する。そのため、Int型で宣言してあるが、現在のコード上、実際には0-3の値しかとらない。例えば、4以上の値が入ってくることはない。でも、この関数のシグネチャだけでは、categoryとしてどんな値を取りうるのか分からず、将来、バグを発生させてしまう可能性がある。このような時にもちろん列挙型が使えるわけだ。

func urlWithSearchText(searchText: String, category: Int) -> NSURL {
        var entityName: String
        switch category {
            case 1: entityName = "musicTrack"
            case 2: entityName = "software"
            case 3: entityName = "ebook"
            default: entityName = ""
        }

        let escapedSearchText =
            searchText.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
        let urlString = String(format:
            "http://itunes.apple.com/jp/search?term=%@&limit=200&entity=%@", escapedSearchText, entityName)
        let url = NSURL(string: urlString)
        return url!
}

上記をリファクタリングしたのが以下だ。ここで、#3の性質を使って、メンバーに値を設定している。これにより、urlWithSearchTextの引数は、Int型ではなく、Category型となり、値としては、0-3までしか扱えなくなった。

enum Category: Int { 
  case All = 0
  case Music = 1 
  case Software = 2 
  case EBook = 3
}

private func urlWithSearchText(searchText: String, category: Category) -> NSURL {
  var entityName: String
  switch category { 
    case .All: entityName = ""
    case .Music: entityName = "musicTrack"
    case .Software: entityName = "software"
    case .EBook: entityName = "ebook"
  }
  
  let escapedSearchText = . . .
  
・・・・・・・・・・・

上記では、urlWithSearchText関数内で、Category値に従って、さらにentityName値を決定しており、なんかすっきりしない。そこでで、さらに、#4の性質を使って、すっきりさせるぜ!

Swiftの列挙型は本当に素敵だ。なんと、Computed propertyを定義できるんだ。つまり、Categoryに、entityNameというcomputed propertyを追加できちゃうんだな。
追加したCateogyが以下だ。

enum Category: Int { 
  case All = 0
  case Music = 1 
  case Software = 2 
  case EBook = 3

  var entityName: String {
    switch self {
      case .All: return "" 
      case .Music: return "musicTrack"
      case .Software: return "software" 
      case .EBook: return "ebook"
    } 
  }
}

これにより、urlWithSearchText関数は、以下のようにすっきりできる。

private func urlWithSearchText(searchText: String,category: Category) -> NSURL {
   let entityName = category.entityName  // cateogory値に基づいて決定されるentityName値を取り出せる。

   let escapedSearchText = . . .

Swiftの列挙型がもつ、
 「列挙型自体を値型としてオブジェクトとして扱え、プロパティーやメソッドも定義できる」
という性質は本当に便利だ。

これを使って、コードをすっきりさせよう。

Swift版 AFNetworkingのAlamofireを使ってみたぜ!

毎週末のジョギングを終えて、ビール片手に、前回以下で投稿した生NSURLSessionを使って実装したサーチプログラムを、Alamofireフレームワークを使って、書き換えてみた。

iTuneStoreのWeb serviceであるSearch APIを使ってみた - Passion+Action+Sincerely=Success!

まず、Alamofireフレームワークを以下からダウンロードする。

Alamofire/Alamofire · GitHub


プロジェクトへの組み込み方は、以下のエントリーを参考にしたよ。

Beginning Alamofire Tutorial - Ray Wenderlich


このサーチプログラムでは、大きく2箇所で、NSURLSessionを使っているので、これらをAlamofireを使って書き換えるよ。


(1) 検索バーに入力されたテキストをもとに実際のサーチ処理を実行するperformSearch()関数を以下のように書き換えた。

ここで何気に重要なのが、dataRequestこれは、Alamofire.Requestクラスのオブジェクトだ。サーチ中に、別のサーチが実行された場合は、dataRequest?.cancel()で、実行中の処理をキャンセルすることができる。

    func performSearch() {
       
        if !searchBar.text.isEmpty {
            
            searchBar.resignFirstResponder()
            dataRequest?.cancel()
            
            //ダウンロード中は、ActivityIndicatorを表示する
            isLoading = true
            tableView.reloadData()
            
            hasSearched = true
            searchResults = [SearchResult]()
            
            //検索バーに入力されたテキストから、SearchAPIコール用のurlを作成する
            let url = self.urlWithSearchText(searchBar.text, category: segmentedControl.selectedSegmentIndex)
            
            dataRequest = Alamofire.request(.GET, url, parameters: nil).responseJSON() { _, response, data, error in
                
      //SearchAPIのレスポンスが返ってきたらここが呼ばれる。
                if error == nil {
                    if let response = response {
                        if response.statusCode == 200 {
                            if let dictionary = data as? [String: AnyObject] {
                                
                                self.searchResults = self.parseDictionary(dictionary)
                                self.searchResults.sort(<)
                                //メインスレッドで、サーチ結果を表示する。
         // ActivityIndicatorを非表示にすることも忘れずにね!
                                dispatch_async(dispatch_get_main_queue()) {
                                    self.isLoading = false
                                    self.tableView.reloadData()
                                }
                                return
                            }
                            
                        }
                    }
                }
                
                dispatch_async(dispatch_get_main_queue()) {
                    self.hasSearched = false
                    self.isLoading = false
                    self.tableView.reloadData()
                    self.showNetworkError()
                }
            
            }
        }
    }

(2) もう一箇所は、Search結果で入手したImageデータが置いてあるURLからImageデータをダウンロードする部分だ。これはUIImageViewのextensionとして実装してあり、以下のように書き換えた。

import Foundation
import UIKit
import Alamofire

extension UIImageView {
    
    func loadImageWithURL(url: NSURL) -> Request {
        var fileName: String?
        var finalPath: NSURL?
        
        let request = Alamofire.download(.GET, url, { (temporaryURL, response) in
            
     /*この部分で、ダウンロードされたファイルが格納されたローカルファイルのフルパスをreturnするコードを書く。*/

            if let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as? NSURL {
                fileName = response.suggestedFilename!
                finalPath = directoryURL.URLByAppendingPathComponent(fileName!)
               
                return finalPath!
            }
            return temporaryURL
            
        }).response { (request,response, data, error) in
                        
    /* この部分で、ローカルファイルからImageデータを取り込むコードを書く*/
            if let response = response {
                if response.statusCode == 200 && finalPath != nil {
                    if let data = NSData(contentsOfURL: finalPath!) {
                            
                        if let image = UIImage(data: data) {
                            dispatch_async(dispatch_get_main_queue()) {
                                self.image = image
                            }
                        }
                    }
                }
            }
        }

        return request
        
    }
}

上記では、loadImageWithURL関数の中で、Alamofire.Requestオブジェクトを返却している。これも重要だ。例えば、スクロールして、現在のダウンロード処理が不要になった場合は、キャンセルが必要になる。そのために、requestオブジェクトをreturnしている。

Alamofireは、なんか直感的に書けて、とってもモダンで、なんかとってもいい感じだ。俺もビール片手に酔っ払って、なんかとってもいい感じになってきたぜ!

Swiftで検索すると、やっぱり、テイラースイフトだらけになった(前と同じ結果)よ。

f:id:diinosimple:20141214183602p:plain
f:id:diinosimple:20141214183616p:plain

今後も積極的にAlamofireを使って通信周りのコードを書いてみようと思う。

iTuneStoreのWeb serviceであるSearch APIを使ってみた

iTuneStoreのWeb serviceであるSearch APIを使ってみた。

 

しっかりとした仕様書を以下に発見したので、これに従って、swiftベースでNSURLSessionで Search APIをコールし、JSON形式のレスポンスを受信し、tableViewに検索結果として表示する実装をしてみた。

iTunes, App Store, iBooks, and Mac App Store Affiliate Resources - Search API

 

Step1. 以下のような感じで、検索キーワード(SearchText)と、検索対象(entity)を指定してURLを構築する。なお、SearchTest中にスペースを入れると飛んじゃうので、ちゃんとエスケープしないといけません。

 

-----コード抜粋------

func urlWithSearchText(searchText: String, category: Int) -> NSURL {

        var entityName: String

        switch category {

            case 1: entityName = "musicTrack"

            case 2: entityName = "software"

            case 3: entityName = "ebook"

            default: entityName = ""

        }

        

        let escapedSearchText =

            searchText.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!

        

        let urlString = String(format:

            "http://itunes.apple.com/jp/search?term=%@&limit=200&entity=%@", escapedSearchText, entityName)

        let url = NSURL(string: urlString)

        return url!

    }

-------------

 

Step2. 以下のような感じで、NSURLSessionを使って、URLをコールする。

Responseは、JSON形式なので、JSONに含まれるwrapperTypeに従い、それぞれのケースのparserを作成した。SearchResult Object Arrayに順番にappendしていき(parseDictionary内で実装)、最後にtableViewに反映する(reloadData内で実装)。

 

------コード抜粋-----

let session = NSURLSession.sharedSession()

dataTask = session.dataTaskWithURL(url, completionHandler: {

                data, response, error in

                if let error = error {

                    println("Failure! (error)")

                    if error.code == -999 { return }

                } else if let httpResponse = response as? NSHTTPURLResponse {

                    if httpResponse.statusCode == 200 {

                        if let dictionary = self.parseJSON(data) {

                            self.searchResults = self.parseDictionary(dictionary)

                            dispatch_async(dispatch_get_main_queue()) {

                                self.isLoading = false

                                self.tableView.reloadData()

                            }

                            return

                        }

                    } else {

                        println("Failure! (response)")

                    }

                }   

                // UIのアップデートはメインスレッドでね!

                dispatch_async(dispatch_get_main_queue()) {

                    self.tableView.reloadData()

                    self.showNetworkError()

                }

})

dataTask?.resume()

------------

 

こんな感じにSwiftで検索すると、テイラースイフトだらけになった。

f:id:diinosimple:20141209005425p:plain

 

え!Objective-Cでは、NSURLSessionをそのまま使わず、AFNetworkingを使うのが主流なの?(ってエンジニアが言っていた気が。。。)

 

Swiftでは、Alamofireというlibraryがあることが分かった。ちなみにAFNetworkingを作った人と同じ人が作ったらしい。"AF"は、AlamofireのObjective-C命名規則に従った呼び方なんだな。この週末は、Alamofireを使って、通信周りをリファクタリングしてみようと思う。

 

以下に、参考になる資料発見。あー、週末が待ち遠しい。


Beginning Alamofire Tutorial - Ray Wenderlich

SmartBeat、クラッシュ解析ツールを使ってみた。

SmartBeat、クラッシュ解析ツールを使ってみた。

 

SDKのインテグレーションが5分で完了し、クラッシュ解析レポーティングツールとして秀逸です。本体の組み込み自体は1行追加で、完了する。

 

Android/iOSの両方を使ってみたが、スタックトレースだけでなく、NSLog/LogCatログや、クラッシュ時のスマフォ画面をキャプチャしてレポートしてくれる機能もあり、それぞれ1行で機能をON/OFFできる。シンプルだが、パワフルで、一度使い始めたら、やめられないツールになりそう。

 

CrittercismとかBugSenseとか海外のツールもあるがやっぱり日本純正が安心でしょ。

 

以下から、無料トライアルができます。


SmartBeat -スマホアプリのクラッシュ解析ツール

 

Dashboard上、同一のクラッシュはグルーピングしてくれるので、件数の多いクラッシュから優先的に対応できます。Dashboard上で対応状況のステータストラックをすることもできます。

 

また、簡単な分析機能(Analytics)もあり、ユニークユーザー数/セッション数/離脱率を視覚的に確認できます。

 

また、端末情報詳細も自動的にアップされるので、どのバージョンのOSで、どの端末のクラッシュが多いかも簡単にわかっちゃいます。

 

なお、Dashboard上に設定した回数以上の異常を検出したら、担当者にメールを送信する機能もあります。以下のようなメールを受け取りました。

 

はい。そのとおり! そこで、存在しないmethodをコールしちゃいました。ごめんなさい。

 

----------

A new error has occurred for application [xxxxxxxxxxxxxx].

For complete information regarding this error please visit:
  https://dash.smrtbeat.com/smartbeat/xxxxxxxxxxxxxxxxxxxxxxx

Summary
=======

Error Message:
  NSInvalidArgumentException: -[MainViewController buttonTapped]: unrecognized selector sent to instance 0x14e4f410

Application Verion:
  1.0

Error Location:
  unknown: 0

Stacktrace:
  0   CoreFoundation                      0x000000002929749f 0x29191000 + 1074335
1   libobjc.A.dylib                     0x0000000036a76c8b 0x36a70000 + 27787
2   CoreFoundation                      0x000000002929c8b9 0x29191000 + 1095865
3   CoreFoundation                      0x000000002929a7d7 0x29191000 + 1087447
4   CoreFoundation                      0x00000000291cc058 0x29191000 + 241752
5   UIKit                               0x000000002c78a9fb 0x2c74b000 + 260603
6   UIKit                               0x000000002c78a9a1 0x2c74b000 + 260513
7   UIKit                               0x000000002c775613 0x2c74b000 + 173587
8   UIKit                               0x000000002c78a40d 0x2c74b000 + 259085
9   UIKit                               0x000000002c78a0e7 0x2c74b000 + 258279
10  UIKit                               0x000000002c7839b1 0x2c74b000 + 231857
11  UIKit                               0x000000002c75a15d 0x2c74b000 + 61789
12  UIKit                               0x000000002c9cdab9 0x2c74b000 + 2632377
13  UIKit                               0x000000002c758bb9 0x2c74b000 + 56249
14  CoreFoundation                      0x000000002925dd57 0x29191000 + 838999
15  CoreFoundation                      0x000000002925d167 0x29191000 + 835943
16  CoreFoundation                      0x000000002925b7cd 0x29191000 + 829389
17  CoreFoundation                      0x00000000291a93c1 0x29191000 + 99265
18  CoreFoundation                      0x00000000291a91d3 0x29191000 + 98771
19  GraphicsServices                    0x00000000305670a9 0x3055e000 + 37033
20  UIKit                               0x000000002c7b8fa1 0x2c74b000 + 450465
21  Problems                            0x0000000000068e4b 0x62000 + 28235
22  Problems                            0x0000000000068dd8 0x62000 + 28120


OS:
  8.1.1

Device:
  iPhone6,1

 

 

 

SwiftのExtensionが素敵すぎる

View Controllerを拡張して、delegate methodを作るのは日常茶飯事だが、自分自身が書いていないiOS frameworkのクラスも簡単に拡張できる。

 

話を超簡単にするため、以下にiOSのStringクラスを拡張して、string objectにランダムな言葉を追加する例を備忘録として残しておく。

 

Step1: プロジェクトにソースファイルを追加する。例えば、String+RandomWord.swift

Step2: このファイルに、以下のようにString extensionを追加する

extension String {

   func addRandomWord() -> String {

       let value = arc4random_uniform(3)

       var word:  String

       switch value {

          case 0: word = "rabit"

          case 1: word = "banana"

          case 2: word = "boat"

          default: word = ""

       }

       return self + word

   }

}

Step3:  これで、コード上のどこでもString objectからaddRandomWordが呼べるようになった。

let someString = "Hello, "
let result = someString.addRandomWord()

println("The queen says: \(result)")

 

 

SwiftのExtensionって素敵!  例えば、標準ではUIImageは、ネットから画像をダウンロードする機能や、リサイズする機能を持っていないが、UIImageを拡張して自分なりの新しい機能を追加することが簡単にできる。

 

また、普通は継承できないような structsやenumsにも、Extensionを適用することが可能なようだ(俺は実際にまだつかっことが無い)。

 

SwiftベースのiOSゲームアプリに、プレイ動画共有サービスのKamcordを組み込んでみた

Swiftで書いたiOS Gameアプリに、Kamcordを組み込んでみた。

 

Kamcordは、プレイ動画の共有サービスで、Android/iOSの両方をサポートしている。

 

ゲームセンターで上手い人がプレイしている時は、その周りに多くの人が集まって見てたよね。「なるほど、この難しいレベルは、あーやって攻略するんだー」とか、上手い人からいろいろテクニックを盗んだ記憶があるよね。当然、ゲームを上手にプレイ出来たら、友達に見せびらかしたい。「ねー、見て見て見て、俺ってすごくなーい?」。そんなバイラル効果を狙ったサービスだ。

 

まず、Swiftで書いたコードからObjective-CAPIをコールする方法を勉強する必要があったので、以下のページを参考に、WrapperクラスのKamcordBridgeクラスを作って実装したので、備忘録として残しておこうと思う。

How to call Objective C code from Swift - Stack Overflow

 

Step1. Kamcord.frameworkをプロジェクトに組み込む。以下に従えば簡単。

https://docs.kamcord.com/documents/platform/ios/getting-started/

 

Step2. Kamcord.frameworkのObjective-C APIをコールするために、KamcordBridgeクラスをプロジェクトに作成する。

 

ゲーム自体はSwiftベースだが、KamcordBridgeクラスのファイルはObjective-Cで作る。

f:id:diinosimple:20141123162735p:plain

すると、Bridging-Headerを作成するか,Xcodeに聞かれるので、Yesとする。すると、以下のように勝手に(ProjectName)-Bridging-Header.hが作成される。

f:id:diinosimple:20141123163017p:plain

ここに、作成したクラスヘッダを定義する。上記では、#import "KamcordBridge.h"

 

Step3. KamcordBridgeクラス内は、Objective-Cで書けるので、ここからKamcord.frameworkのAPIを呼ぶぞ。

 

以下のような感じ。

//KamcordBridge.h
#Import <Foundation/Foundation.h>
#import <Kamcord/Kamcord.h>

@interface KamcordBridge : NSObject

- (void)setDeveloperKey:(NSString *)key
developerSecret:(NSString *)secret
 appName:(NSString *)appName
parentViewController:(UIViewController *)parentViewController;

- (void)startRecording;
- (void)stopRecording;
- (void)showView;

@end

//KamcordBridge.mm
#import "KamcordBridge.h"
@implementation KamcordBridge

- (void)setDeveloperKey:(NSString *)key developerSecret:(NSString *)secret appName:(NSString *)appName
parentViewController:(UIViewController *)parentViewController {
[Kamcord setDeveloperKey:key developerSecret:secret appName:appName parentViewController:parentViewController];
}

- (void)startRecording {
  [Kamcord startRecording];
}

-(void)stopRecording {
[Kamcord stopRecording];
}

-(void)showView {
[Kamcord showView];
}

@end

 

Step4.  あとはSwiftのコードから、KamcordBrigeクラスのAPIを呼べるぞ。

 

ViewDidLoad()から、初期化のため、以下をコールする。

KamcordBridge().setDeveloperKey("test", developerSecret: "test", appName: "Test", parentViewController: self)

 

もちろん"Test"ではなく、KamcordのDashbordから入手した、自分のアプリ用のDeveloper Key, Secret, AppNameをセットしてくれ!

 

録画を開始するためには、以下のようにコールする。

KamcordBridge().startRecording()

録画を終了するためには、以下のようにコールする。

KamcordBridge().stopRecording()

んで、最後に録画したプレイ動画の共有用UIを以下のようにコールする。

KamcordBridge().showView()

プレイ後、以下のように表示されれば成功だぜ!

f:id:diinosimple:20141123171439p:plain