位置情報を利用するエンタープライズアプリを構築する – WWDC2020

Session概要

自社ビジネス用に位置情報を利用するエンタープライズアプリを開発し、従業員の日常生活を個人向けに使いやすくします。AppleがiBeaconと位置情報サービスを使用し、自社内のカフェテリア向けにCaffe Macs アプリを構築した方法と、従業員のプライバシーを保護しながら、それらのツールとフレームワークをどのように貴社独自のアプリに応用するかを学びましょう。そこから、海外の従業員にすばらしい日常生活を届けるためのローカライズ活用方法について https://developer.apple.com/videos/play/wwdc2020/10140/

Caffe Macs について

  • Caffe Macs
    • Apple Parkにある社員専用のスペースで、ランチ・軽食・コーヒーブレイクなどで利用する
  • Caffe Macs アプリ
    • Caffe Macsで利用するアプリで、メニューの閲覧からApple Payを使った決済などの機能がある
    • ユーザーからの注文は厨房のiPadのKitchen Display Systemへと送られ、注文を受け取るとシェフが作り、料理が完成するとKitchen Display Systemでユーザーへ通知される
    • 位置情報は従業員に出来たての食事を提供するために重要な要素である

位置情報サービスを使わず、ユーザー設定を使ったアプリ体験について

  • Caffe Macs アプリでは従業員は、デフォルトのロケーションを設定する(この設定により、アプリ内でメニューが表示可能となる)
  • ユーザーは位置情報サービスを紐付けることも拒否することもできる
  • ユーザーのカフェの設定はアプリ起動時に確認する
    • 設定が特定の位置ではなく、「最寄り」の場合は、位置情報サービスを基に表示するカフェを決定し、特定のカフェを選択した場合は、そのカフェのメニューを表示する
  • このように、ユーザーがデフォルトのロケーションを選択できるようになっていることが望ましい

位置情報サービスを使ったアプリ体験の提供

  • Caffe Macs アプリの課題は、ユーザーの位置に基づいた正しいメニューを表示すること
    • 社内には大きなカフェと近くにはコーヒーステーションがあり、複数のロケーションが隣接しているため、ユーザーの期待通りのメニューを表示することが求められる
  • この課題を解決するために、Core LocationiBeaconプロトコルを使用する
    • アプリでCore Location を使用するには、ユーザーにアクセス許可をリクエストする
    • アクセス許可は必要なデータのみに限定し、必要な理由をユーザに説明することが大切
  • アプリが求めるアクセス許可は2種類ある
    • アプリ使用中は許可
      • アプリの起動中のみlocationイベントを受け取る
      • バックグラウンドのlocationの使用を有効化し、アクセスを許可し続けることもできる
    • 常に許可
      • アプリの起動等に関係なく、バックグラウンドでアクセスを許可する
    • 近似許可(新たに追加されたリクエスト方法)
      • リクエストが許可されたとしてもデータが必ず取得できるわけではなく、アクセスを拒否された場合も適切な処理が必要
      • 1度だけ許可を選択し、限定的に許可することもある
  • アクセス許可のリクエストを送るには、 NSLocationWhenInUseUsageDescription キーと利用目的をInfo.plistに追加する
    • システムが利用目的の説明文と許可リクエストのダイアログを表示するので許可を求める前に必要なキーをInfo.plistに追加する
    • キーが存在しない場合は、許可リクエストは却下される
    • ユーザーがタスクの実行において、位置情報サービスが必要な時のみ許可を求める
    • アプリが位置情報を求める理由が明確でない場合、ユーザーは拒否する可能性があるため、明確に記述する
  • 許可を求めたり、アプリの許可ステータスが変わる時は、locationManagerのデリゲートオブジェクトのdidChangeAuthorizationメソッドを使用する
    • 位置情報サービスの可用性は常に変わる可能性がある
    • アプリの実行中に可用性ステータスが変わると、システムはlocationManagerを呼び出し、didChangeAuthorizationメソッドで通知する
  • 位置情報サービスが求めるハードウェアはすべての端末にあるとは限らない
    • そのため、 CLLocationManagerオブジェクトのメソッドで端末がサービスに対応しているか確認する
  • ビーコンは信号を発信するデバイスで、その信号が検出されるとアプリへ送られる
    • この信号によりユーザがカフェにいるかを識別し、特定のビーコン信号を検出することで、そのカフェのメニューを表示可能となる
  • iBeaconプロトコルの使用に必要なのは、位置情報のアクセス許可のみ
    • ビーコン本体の配備には、proximity UUID, メジャー番号, マイナー番号の設定が必要
    • 各ビーコンを識別する一意的な番号をつけると、アプリがビーコンを区別できるようになる
  • ビーコン対応をアプリに追加するには、2つの段階でビーコンの検出がある
    • リージョン監視を使用し、ビーコンを検出する
    • ビーコン距離測定を使用して、検出したビーコンへの接近度合を判断する
// 位置情報サービスの可用性変更時の通知を受け取る
// Add NSLocationWhenInUseUsageDescription to your Info.plist 
// e.g. “Location is required for placing orders while using the app."

locationManager.requestWhenInUseAuthorization()

func locationManager(
    _ manager: CLLocationManager,
    didChangeAuthorization status: CLAuthorizationStatus) {
        
    switch status {
    case .restricted, .denied: 
        disableLocationFeatures()

    case .authorizedWhenInUse, .authorizedAlways: 
        enableLocationFeatures()

    case .notDetermined: // The user hasn’t chosen an authorization status
    }
}
// ハードウェアがサービスを利用可能かチェックする

if CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self) {
    // Supports region monitoring to detect beacon regions
}

if CLLocationManager.isRangingAvailable() {
    // Supports obtaining the relative distance to a nearby iBeacon device
}
// リージョン監視を行い、ビーコンを検出する実装
// Stage 1: Region Monitoring

func monitorBeacons() {
    if CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self) {

        // ビーコンの監視には、proximityUUIDを使用し、CLBeaconIdentityConstraintオブジェクトを作成
        let constraint = CLBeaconIdentityConstraint(uuid: proximityUUID)

        let beaconRegion = CLBeaconRegion(
            beaconIdentityConstraint: constraint,
            identifier: beaconID
        )

        // startMonitoringforメソッドで登録する        
        // https://developer.apple.com/documentation/corelocation/cllocationmanager/1423656-startmonitoring
        self.locationManager.startMonitoring(for: beaconRegion)
    }
}
// Stage 2: Beacon Ranging

// ビーコンが領域内に入った時
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
    guard let region = region as? CLBeaconRegion,
        CLLocationManager.isRangingAvailable() // このメソッドで距離測定可能かを確認する
        else { return }
    
    let constraint = CLBeaconIdentityConstraint(uuid: region.uuid)
    manager.startRangingBeacons(satisfying: constraint)
    beaconsToRange.append(region)
}

// ビーコンが領域外になった時
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
    
}

func locationManager(
    _ manager: CLLocationManager,
    didRangeBeacons beacons: [CLBeacon],
    in region: CLBeaconRegion) {
    
    guard let nearestBeacon = beacons.first else { return }
    let major = CLBeaconMajorValue(truncating: nearestBeacon.major)
    let minor = CLBeaconMinorValue(truncating: nearestBeacon.major)
    
    switch nearestBeacon.proximity {
    case .near, .immediate:
        displayInformation(for: major, and: minor)
        
    default:
        handleUnknownOrFarBeacon(for: major, and: minor)
    }
}

アプリの国際化について

  • ユーザーに見える日付や時間の表記には、DateFormatterが提供する多言語に対応した事前設定や設定オプションを使用する
    • NumberFormatterを使用して、数値の書式設定を行う
      • ただし、通貨の換算はしません
    • 金額書式の設定は事前定義された、通貨書式スタイルの使用が可能で、NumberFormatterのnumberStyleプロパティが使います
  • 端末のロケールとISO 4217通貨コードの両方を守ることで、NumberFomatterを使用して金額の書式設定が可能
  • ローカライズ
    • Xcodeでは、ユーザインタフェースのファイルに.stringsの拡張子付きファイルを生成し翻訳文を記述することで対応可能
// Formatting Dates
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .short
dateFormatter.string(from: Date())
// "Jun 25, 2020 at 9:41 AM"


// Configuring the Format of Currency
let formatter = NumberFormatter()
formatter.currencyCode = "CAD"
formatter.numberStyle = .currency
formatter.string(from: amount)
// "CA$1.00"
最新情報をチェックしよう!