■TouchID認証を使ってみました

TouchID認証を使ってみました

サンプルアプリ

認証ボタンのみを持つアプリとして作成しました。

f:id:unokun3:20170101100939p:plain

起動時

利用可能かどうかチェックします。 LAContext.canEvaluatePolicyを呼び出します。このメソッドは、true/falseを返します。falseの場合、error.localizedDescriptionに説明が入っています。

第1引数をdeviceOwnerAuthenticationWithBiometricsとするとTouch ID認証ができない端末ではエラーになるので、deviceOwnerAuthenticationを使うのが一般的でしょうか?

LAPolicy 説明
deviceOwnerAuthenticationWithBiometrics Touch IDを使った端末のオーナー認証
deviceOwnerAuthentication Touch IDを使った、あるいはパスコードによる端末のオーナー認証
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        let context = LAContext()
        var error: NSError?
        let result = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
        if !result {
            authButton.isEnabled = false
            var message = ""
            if let detail = error {
                message = detail.localizedDescription
//                print("error => \(detail.localizedDescription)")
            }
            let alertController: UIAlertController = UIAlertController(title: "アラート表示", message: message, preferredStyle:  UIAlertControllerStyle.alert)
            let defaultAction: UIAlertAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler:{
                // ボタンが押された時の処理を書く(クロージャ実装)
                (action: UIAlertAction!) -> Void in
                print("OK")
            })
            alertController.addAction(defaultAction)
            self.present(alertController, animated: true, completion: nil)
            
        }
    }

認証ボタンタップ時

認証します。 LAContext.evaluatePolicyメソッドを呼び出します。 f:id:unokun3:20170101101005p:plain

    @IBAction func tapAuthButton(_ sender: UIButton) {
        let context = LAContext()
        context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "アプリの認証に使う") { success, error in
            if success {
                print("available")
                return
            }
            var message = ""
            if let detail = error {
                message = detail.localizedDescription
                
            }
            let alertController: UIAlertController = UIAlertController(title: "認証", message: message, preferredStyle:  UIAlertControllerStyle.alert)
            // OKボタン
            let defaultAction: UIAlertAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler:{
                // ボタンが押された時の処理を書く(クロージャ実装)
                (action: UIAlertAction!) -> Void in
                print("OK")
            })
            alertController.addAction(defaultAction)
            self.present(alertController, animated: true, completion: nil)
        }
    }

キャンセルが選択された場合は、error.localizedDescriptionにCanceled by userというメッセージが格納されます。

f:id:unokun3:20170101101020p:plain

参考

SRACOM APIを使って見ました

概要

Software Design 5月号に付録として付いていました、SORACOM SIMを使ったAPIを試してみました。

維持費用の安いSIMカードとしても使えますが、特長の一つにAPIによるSIMカード操作があります。SIMカードを付けたIoTを状況に応じて操作することができるようになれば今までにないサービスを提供できるかもしれないと感じました。

また、SORACOM EndorseというSIMカードを使った認証はうまい方法だと思いました。

構成・手順

スマートフォン(Android)

このうち、3は雑誌についているクーポン(500円分)を使うための手順です。

  1. SIMカードAndroid Nexus5Xに挿入して有効化します。
  2. SORACOM Endorseを有効にします。
  3. クーポンを登録します。
  4. SORACOM Airメタデータサービスを有効化します。
  5. テザリングします

ノートPC

MacOSX Yosemite(10.11.4)。テザリングを使って、SORACOM SIMを挿しているスマートフォンの回線上で、(Web)APIを実行します。これによって、SIM情報を見たり、通信速度などを変えることができます。

以下、ターミナル上でそうさしました。また、記事中にあるjsonデータを操作するツールjqは、ここにあります。

// SIM情報を取得します。
// ※)一部のデータはXXXXに変更しました。
$ curl -s http://metadata.soracom.io/v1/subscriber
{"imsi":"XXXX","msisdn":"XXXX","ipAddress":"XXXX","apn":"soracom.io","type":"s1.standard","groupId":"XXXX","createdAt":1461534616669,"lastModifiedAt":1462145801291,"expiredAt":null,"expiryAction":null,"terminationEnabled":false,"status":"active","tags":{},"sessionStatus":{"lastUpdatedAt":1462143291785,"imei":"XXXX","location":null,"ueIpAddress":"10.228.24.158","dnsServers":["100.127.0.53","100.127.1.53"],"online":true},"speedClass":"s1.standard","moduleType":"nano","plan":0,"serialNumber":"XXXX","expiryTime":null,"operatorId":"OP0071750211","createdTime":1461534616669,"lastModifiedTime":1462145801291}

// 速度クラスをsq.fastに変更します。
$ curl -sX POST -d '{"speedClass":"s1.fast"}' -H 'Content-Type: application/json' http://metadata.soracom.io/v1/subscriber/update_speed_class
{"imsi":"XXXX","msisdn":"XXXX","ipAddress":"XXXX","apn":"soracom.io","type":"s1.fast","groupId":"XXXX","createdAt":1461534616669,"lastModifiedAt":1462146445481,"expiredAt":null,"expiryAction":null,"terminationEnabled":false,"status":"active","tags":{},"sessionStatus":{"lastUpdatedAt":1462143291785,"imei":"XXXX","location":null,"ueIpAddress":"10.228.24.158","dnsServers":["100.127.0.53","100.127.1.53"],"online":true},"speedClass":"s1.fast","moduleType":"nano","plan":0,"serialNumber":"XXXX","expiryTime":null,"operatorId":"OP0071750211","createdTime":1461534616669,"lastModifiedTime":1462146445481}%

関連情報

【swift】playgroundでユーザーインタラクション

概要

Xcode7.3から、playgroundでユーザーインタラクションがつかるようになりました。

Xcode Release Notes, Xcode7.3のPlaygroundはついにインタラクションに対応! - Qiita

UISliderをつかって新しい機能を試してみました。

UISliderをplaygroundで使ってみる

playgroundでのUISlider(7.3まで)

playgroundでUISliderのインスタンスを作成すると Quick LookでUISliderを表示できますが、スライダーのボタンを動かすことはできませんでした。

    let label: UILabel = UILabel();

f:id:unokun3:20160410164801j:plain

playgroundでのUISlider(7.3以降)

playground7.3から、スライダー移動時のイベントを取得することができるようになりました。

以下の例は、イベントを取得時に、値をラベルに出力しています。

import UIKit
import XCPlayground

class ViewController: UIViewController {
    let label: UILabel = UILabel();
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .whiteColor()
        view.frame = CGRectMake(0, 0, 600, 300)
        
        let slider = UISlider()
        slider.frame = CGRectMake(0,0,300,100)
        view.addSubview(slider);
        slider.setValue(0.5, animated: true)
        slider.continuous = false
        slider.addTarget(self, action:
            #selector(ViewController.sliderChanged(_:)), forControlEvents: UIControlEvents.ValueChanged)
        label.frame = CGRectMake(0,50,300,100)
        view.addSubview(label)
        
    }
    
    func sliderChanged(sender: UISlider){
        label.text = String(sender.value)
    }
}

f:id:unokun3:20160410165029j:plain

セレクタの指定方法

playgroundの機能とは関係ありませんが、swift2.2で変更になったセレクタの指定をつかっています。

#selectorは、swift2.2で導入された文法です。今までの文字列でselectorを設定する方法は非奨励になっています。 新しい方法では、文字列での設定でないためメソッド名を間違った場合、コンパイルエラーとして通知されるようになります。

// 今までのセレクタの指定
// 非奨励になりました。
slider.addTarget(self, action: "sliderChanged:", forControlEvents: UIControlEvents.ValueChanged)

// 新しいセレクタの指定
slider.addTarget(self, action:
     #selector(ViewController.sliderChanged(_:)), forControlEvents: UIControlEvents.ValueChanged)

関連情報

【swift】Xcodeデバッガ入門

概要

Xcodeデバッガの使い方をまとめました。

デバッガはアプリの不具合の改修に役立ちます。多くの場合、1)ブレイクポイント設定、2)変数の値確認で不具合を改修することができますが、Xcodeが持っている便利な機能を使うとデバッグが捗ります。

Xcodeデバッガの概要

公式ドキュメント(Xcodeの概要: デバッガを使う)に詳しいドキュメントがあります。objective-cベースの記事になっています。

ブレイクポイントの設定

ブレイクポイントを設定すると、プログラムの動作を一時中止し、その時の変数の値やメモリの状態を調べることができます。

Xcodeで行番号付近をクリックすると、ブレイクポイントを設定することができます。

青背景のアイコン表示されるのブレイクポイントが設定されているかどうかがわかります。

ブレイクポイントの使い方

例えば、アプリ開発時にprint出力でデバッグすることがよくありますが、1)loop内のprint出力は量が多くなる、2)消し忘れてリリースしてしまうことがあります。

以下の例は、loopのindexが偶数の場合に値を出力するケースです。

import Foundation

var sum = 0
for i in 0..<10 {
    sum += i
    if (i % 2) == 0 {
        print(i)
    }
}
print("sum = \(sum)")

Xcodeデバッグ機能を使って、loopのindexが偶数の場合に値を出力きるようになります。

ブレイクポイントを編集します

ブレイクポイントを右クリックし、「Edit Breakpoint ...」を選択します。

f:id:unokun3:20160405075539p:plain

ConditionとActionを設定します。

f:id:unokun3:20160405075602p:plain

Conditionに、if文の条件部を設定します。

(i % 2) == 0

Actionをクリックし、Log Messageを選択します。変数iの値をメッセージ出力します。

Optionsの「Automatically continue after evaluating actions」にチェックを入れるとブレイクポイントでプログラムが停止しなくなります。これは他のIDEにない、とても便利な機能です。

f:id:unokun3:20160405075615p:plain

プログラムを実行すると、以下のような内容が出力されます。

0
2
4
6
8
sum = 45

f:id:unokun3:20160405075629p:plain

その他のAction

ブレイクポイントに止まったとき、ログ出力以外の処理も可能です。

f:id:unokun3:20160405075622p:plain

デバッグコマンドを実行する

Debugger Commandを選択すると、設定したコマンドを実行してくれます。

変数の値を出力する

以下のコマンドを設定すると変数の値を出力してくれます。eはswiftオブジェクトの評価、poはobjective-cオブジェクトの評価をします。

e sum
(Int) $R1 = 0
(Int) $R4 = 1
(Int) $R7 = 6
(Int) $R10 = 15
(Int) $R13 = 28

poの場合、以下のように出力されます。

po sum
0
1
6
15
28

バックトレースを出力する

Action部に以下を設定するとバックトレースを出力してくれます。

bt -c 10
* thread #1: tid = 0x9323, 0x000000010026cadd HelloConsole`main + 173 at main.swift:13, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x000000010026cadd HelloConsole`main + 173 at main.swift:13
    frame #1: 0x00007fff9c9205ad libdyld.dylib`start + 1
sum = 45

Shell Commandを実行する

DBからデータを取得するシェルスクリプトを書いておけば、ブレイクポイントで止まった時点のデータをチェックすることができます。

音をならす

ActionにSoundを設定すると音を鳴らすことができます。

関連情報

【swift】Xcodeで値変更

概要

アプリ開発において、デバッガで変数の値を変更することはよく行われます。Xcodeで変数の値を変更する方法を調べました。

MacOS X 10.11.4 YosemiteとXcode7.3の環境で実行しました。

コマンドラインアプリを作成します

OSX向けのHelloCommandlineという名前のCommandlineアプリプロジェクトを作成します。名前は何でも構いません。

一番簡単なアプリとしてコマンドラインアプリを作成しましたが、iOSアプリでも同じように使うことができます。

実行します

作成されたmain.swiftを以下のように修正します。

import Foundation

var message = "Hello, World!"
print(message)

実行します。コンソール領域に結果が出力されます。

Hello, World!
Program ended with exit code: 0

プログラム実行時に値を変更します

ブレイクポイントを設定します

行番号が表示されている背景色が異なる領域をクリックします。青色の領域が表示されれば成功です。

f:id:unokun3:20160330075544p:plain

実行します。

プログラムを実行するとブレイクポイントで停止します。

f:id:unokun3:20160330075528j:plain

変数の値を表示します

Xcodeの右下画面に(lldb)と表示されている領域があります。ここに、po messageと入力すると、変数messageの値が出力されます。

(lldb) po message
"Hello, World!"

変数の値を変更します

以下のように入力し、変数messageの値を変更します。

(lldb) po message = "Hello World2!"
(lldb) po message
"Hello World2!"

プログラムを再開します

lldb領域にc(continue)を入力し、プログラムを再開します。

変更した値が出力されます。

(lldb) c
Process 838 resuming
Hello World2!
Program ended with exit code: 0

関連情報

【swift】2.2の変更点(2)

概要

リリースノートをベースに【swift】2.2の変更点を作成しましたが、わかりやすい記事があったので、こちらも紹介します。

++ and -- are deprecated

++、--記法が非奨励になりました。swift 3.0ではエラーになります。

var i = 0
// 非奨励
i++
// 今後はこのように記述します
i += 1
i = i + 1

cスタイルのループ記法が使えなくなります。

for var i = 1; i <= 10; i += 1 {
    print("\(i) green bottles")
}

for in構文を使います。

for i in 1...10 {
    print("\(i) green bottles")
}

for in 構文は、開始値<終了値という制約があります。

for var i = 10; i >= 0; i -= 1 {
}

この場合、以下のように記述します。

for i in (1...10).reverse() {
}

Arrays and other slice types now have removeFirst()

最初の要素を取り出す関数removeFirstが追加されました。removeAtIndex(0)でも同じことができます。

var array = Array(1...10)
array.removeFirst() // 1
array.removeLast() // 10
print(array.count) // 8

ただし、removeFirst/removeLastは要素が存在しない場合には実行時エラーになります。

var array = [Int]()
array.removeFirst() // 実行時エラー

該当の要素がない場合、nilを返す最後の要素を取得する関数popLastがありますが、popFirstは追加されません。

var array = [Int]()
array.popLast() // nil

You can now compare tuples (within reason)

タプルの比較演算子が追加されました。

let singer = ("Taylor", "Swift")
let alien = ("Justin", "Bieber")

if singer == alien {
    print("Matching tuples!")
} else {
    print("Non-matching tuples!")
}

Tuple splat syntax is deprecated

引数をタプルで渡すことが非奨励になりました。

func describePerson(name: String, age: Int) {
    print("\(name) is \(age) years old")
}

let person = ("Taylor Swift", age: 26)
describePerson(person)

More keywords can be used as argument labels

一部のキーワードを引数ラベルとして使えるようになりました。

func printGreeting(name: String, repeat repeatCount: Int) {
    for _ in 0 ..< repeatCount {
        print(name)
    }
}

printGreeting("Taylor", repeat: 5)

var parameters have been deprecated

引数を関数内で変更できるようにするためのキーワードvarが非奨励になりました。値を変更する場合には、別変数を使います。

func printGreeting(var name: String, repeat repeatCount: Int) {
    // 値を変更できる
    name = name.uppercaseString

    for _ in 0 ..< repeatCount {
        print(name)
    }
}

printGreeting("Taylor", repeat: 5)

変数を別途作成します。

func printGreeting(name: String, repeat repeatCount: Int) {
   // 新しい変数に値をセットします。
    let upperName = name.uppercaseString

    for _ in 0 ..< repeatCount {
        print(upperName)
    }
}

printGreeting("Taylor", repeat: 5)

Renamed debug identifiers: #line, #function, #file

swift 2.1以前では、__LINE__や__FUNCTION__のシンボルが行数、関数名に置換されました。2.2移行では、#line, #functionになります。

func printGreeting(name: String, repeat repeatCount: Int) {
    // old - deprecated!
    print("This is on line \(__LINE__) of \(__FUNCTION__)")

    // new - shiny!
    print("This is on line \(#line) of \(#function)")

    let upperName = name.uppercaseString

    for _ in 0 ..< repeatCount {
        print(upperName)
    }
}

printGreeting("Taylor", repeat: 5)

Stringified selectors are deprecated

文字列セレクタが非奨励になりました。

以下の例だと、セレクタを#selector(buttonTapped)と記述します。もし、関数buttonTappedがなければコンパイルエラーになります。

navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Tap!", style: .Plain, target: self, action: "buttonTaped")

Compile-time Swift version checking

コンパイル時にバージョンチェックできるビルドオプションが追加されました。

#if swift(>=2.2)
print("Running Swift 2.2 or later")
#else
print("Running Swift 2.1 or earlier")
#endif

New documentation keywords: recommended, recommendedover, and keyword

swiftはmarkdown形式のドキュメントを書くことができます。

/**
Say hello to a specific person
- parameters:
- name: The name of the person to greet
- returns: Absolutely nothing
- authors:
Paul Hudson
Bilbo Baggins
- bug: This is a deeply dull function
*/
func sayHello(name: String) {
    print("Hello, \(name)!")
}

sayHello("Bob")

Swift 2.2で、3つのキーワードが追加されました。recommended, recommendedover, keyword。 Xcode 7.3では、このキーワードを追加しても何も変わりません。

関連情報

【swift】日付時間処理

概要

swiftで日付時刻の処理を行う場合には、NSDate、NSCalendarクラスを使います。

NSDate, NSDateFormatterを使う方法

書式を使って時間を取得します

日付時刻の書式については、 ここを参照してください。

  // 現在時間を取得
  let now = NSDate()
  let formatter = NSDateFormatter()
  // yyyyMMdd HH:mm:ss
  formatter.dateFormat = "HH"

  // 時間を整数値に変換
  let hour = Int(formatter.stringFromDate(now))
  // 時間に応じて挨拶を変える
  if hour < 12 {
      print("Good Morning!")
  } else  if hour < 18 {
      print("Good Afternoon!")
  } else {
      print("Good Evening!")
  }

日付時刻の文字列フォーマットします

NSDateFormatterにlocaleを設定する、国別のの日付表示を取得できます。

// 2016/3/27の場合
let date = NSDate()
// US English Locale (en_US)
dateFormatter.locale = NSLocale(localeIdentifier: "en_US")
print(dateFormatter.stringFromDate(date)) // My 27, 2016

// French Locale (fr_FR)
dateFormatter.locale = NSLocale(localeIdentifier: "fr_FR")
print(dateFormatter.stringFromDate(date)) // 27 mars 2016

// Japanese Locale (ja_JP)
dateFormatter.locale = NSLocale(localeIdentifier: "ja_JP")
print(dateFormatter.stringFromDate(date)) // 2016/03/27

文字列表現をパーズしてNSDateに変換します

let RFC3339DateFormatter = NSDateFormatter()
RFC3339DateFormatter.locale = NSLocale(localeIdentifier: "ja_JP")
RFC3339DateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
let string = "2016-03-27T16:39:57-08:00"
let date2 = RFC3339DateFormatter.dateFromString(string)
print(date2) // Optional(2016-03-28 00:39:57 +0000)

NSCalendarを使う方法

NSCalendarを使うと、年、月、日、時間、分、秒を個別に取得することができます。

  let now = NSDate()
  let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierJapanese)!
  let hour: NSDateComponents = calendar.components(NSCalendarUnit.Hour, fromDate: now)
  print(hour.hour)

日付計算もできます。が、ちょっと使うのは面倒。

let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian)!
let now = NSDate()
let date = calendar.dateByAddingUnit(.Day, value: 1, toDate: now, options: NSCalendarOptions())

参考情報