2.2.1でビルドしたアプリから、3.0で追加されたAPIを使ってみました

先ほど、SysStats Liteに、バッテリ残量の表示を追加して、アップデートを申請しました。
やることは非常に簡単なのですが、その中で、少しだけ悩んだことがありました。バッテリ残量取得のAPIは、OS3.0以降で追加されたので、ふつうにコーディングすると、OS2.2.1環境では、コンパイルが通りません。かといって、3.0でビルドしてしまっては、2.2.1では動作させることができなくなってしまいます。
ということで、2.2.1でビルドしたアプリから、3.0で追加されたAPIを使う方法を、メモを兼ねて書いておきたいと思います。

これと同じようなことをしたい場合、3.0以降で追加されたAPIについては、NSInvocationクラスを使って、動的に呼び出してやることで、回避することができます。*1

バッテリー残量を取得するためには、UIDeviceクラスに定義されている、以下の2つのプロパティにアクセスする必要があるので、これらを動的に呼び出します。

  • batteryMonitoringEnabled
  • batteryLevel

あと、これらのプロパティは、3.0以降で実行されている場合にのみ存在しているので、存在チェックが必要となります。
以下に、実際のコーディング例を示します。

+ (float)batteryLevel {
    
    float batteryLevel = -1;
    UIDevice *device = [UIDevice currentDevice];
    
    if([device respondsToSelector:@selector(batteryLevel)]) { // batteryLevelプロパティの存在チェック

        // batteryMonitoringEnabledプロパティのメッセージ定義
        NSMethodSignature *setBatteryMonitoringEnabledSig = [UIDevice instanceMethodSignatureForSelector:@selector(setBatteryMonitoringEnabled:)];
        NSInvocation *setBatteryMonitoringEnabledInv = [NSInvocation invocationWithMethodSignature:setBatteryMonitoringEnabledSig];
        [setBatteryMonitoringEnabledInv setTarget:device];
        [setBatteryMonitoringEnabledInv setSelector:@selector(setBatteryMonitoringEnabled:)];
        BOOL batteryMonitoringEnabled = YES;
        [setBatteryMonitoringEnabledInv setArgument:&batteryMonitoringEnabled atIndex:2];

        // batteryLevelプロパティのメッセージ定義
        NSMethodSignature *batteryLevelSig = [UIDevice instanceMethodSignatureForSelector:@selector(batteryLevel)];
        NSInvocation *batteryLevelInv = [NSInvocation invocationWithMethodSignature:batteryLevelSig];
        [batteryLevelInv setSelector:@selector(batteryLevel)];
        [batteryLevelInv setTarget:device];

        // batteryMonitoringEnabledプロパティにYESを設定
        [setBatteryMonitoringEnabledInv invoke];

        // batteryLevelプロパティから値を取得
        [batteryLevelInv invoke];
        [batteryLevelInv getReturnValue:&batteryLevel];
        
    }
    
    return batteryLevel;
    
}

他にも、OSのバージョンをチェックして、処理を振り分けるなど、いくつか方法はあると思いますが、今回の場合は、使用するAPIが限定的なので、上記のような形で実装しました。
とりあえず、非公開APIや、偶然の実行結果を使用しているわけではなく、正式にサポートされている方法なので、審査でリジェクトされることはないと思っていますが、とりあえず、これで結果待ち状態です。

以下に、バッテリー残量表示のイメージを載せておきます。

*1:performSelector:メソッドは、引数や戻り値に、アトミック型が使われている場合には使用できないので、NSInvocationを使用しています。