27680


現在のソースでは、ボタンを押すと、バッテリー残量を表示しています。  
・これを１秒毎に、結果（時刻とバッテリー残量）を取得するにはどうすればいいでしょうか。  
・また、結果をCSVか何かに保存するにはどうすればいいでしょうか。（Lightningケーブルで繋いで、Macに出力するのでも構いません）

[code]

    - (IBAction)buttonGetValue:(id)sender {    
    
        self.labelBatteryCapa.text = [NSString stringWithFormat:@"%zd", [self 
    batteryCapacity]];
    
    }
    
    
    
    - (NSInteger)batteryCapacity {
    UIApplication *app = [UIApplication sharedApplication];
    NSArray *subviews = [[[app valueForKey:@"statusBar"] valueForKey:@"foregroundView"] subviews];
    NSString *dataNetworkItemView = nil;
    
    for (id subview in subviews) {
        if([subview isKindOfClass:[NSClassFromString(@"UIStatusBarBatteryItemView") class]]) {
            dataNetworkItemView = subview;
            break;
        }
    }
    
    return [[dataNetworkItemView valueForKey:@"capacity"] intValue];
    
    }
    
[/code]


CC BY-SA 3.0
2016-07-16T14:31:33.207
27669
2016-07-17T07:03:23.543
2016-07-17T04:30:31.373
7590
7590
question
ios objective-c
1秒毎に実行結果をログに保存したい


ステータスバー周りのprivateクラスを使っているので、Appleの審査には通らないコードだと思いますが、それは置いておき。

## １秒毎に結果を取得する方法

いろいろ方法はありますが、手っ取り早いのは`NSTimer`を使う方法です。

## 結果をCSVか何かに保存する

これも方法はいろいろとありますが、例えば端末内にファイルとして保存するという方法をとる場合は以下のようなコードになります。

[code]

    @interface ViewController ()
    
    @property (nonatomic, nullable) NSTimer *timer;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    }
    
    -(void)viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0
                                                      target:self
                                                    selector:@selector(writeLog:)
                                                    userInfo:nil
                                                     repeats:YES];
    }
    
    -(void)viewWillDisappear:(BOOL)animated
    {
        [super viewWillDisappear:animated];
        [self.timer invalidate];
        self.timer = nil;
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        [self.timer invalidate];
        self.timer = nil;
    }
    
    - (NSInteger)batteryCapacity {
        __block NSInteger capacity = 0;
        [[[UIApplication sharedApplication] valueForKeyPath:@"statusBar.foregroundView.subviews"]
         enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
             if([obj isKindOfClass:[NSClassFromString(@"UIStatusBarBatteryItemView") class]]) {
                 capacity = [[obj valueForKey:@"capacity"] integerValue];
                 *stop = YES;
             }
         }];
    
        return capacity;
    }
    
    -(void) writeLog:(NSTimer *)timer
    {
        @synchronized (self) {
    
            NSString *documentDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true) lastObject];
            NSURL *filePath = [[[NSURL URLWithString:documentDirectory]
                                URLByAppendingPathComponent:@"batteryCapacity"]
                               URLByAppendingPathExtension:@"csv"];
    
            NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
            formatter.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
            formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"ja_JP"];
            formatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"JST"];
            formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss ZZZ";
    
            NSString *wirteData = [NSString stringWithFormat:@"%@, %zd\r\n", [formatter stringFromDate:[NSDate date]], [self batteryCapacity]];
    
            NSFileManager *fileManager = [NSFileManager defaultManager];
            if(![fileManager fileExistsAtPath:filePath.absoluteString]){
                [fileManager createFileAtPath:filePath.absoluteString contents:nil attributes:nil];
            }
    
            NSError *error = nil;
            NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingToURL:filePath error:&error];
            if(error){
                // error handling
                NSLog(@"%@", [error localizedDescription]);
                return;
            }
            [fileHandle seekToEndOfFile];
            [fileHandle writeData:[wirteData dataUsingEncoding:NSUTF8StringEncoding]];
            [fileHandle closeFile];
        }
    }
    
    @end
    
[/code]

* * *

`NSTimer`についての解説は特に必要がないと思うので、ファイル保存について、

[code]

    NSString *documentDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true) lastObject];
    NSURL *filePath = [[[NSURL URLWithString:documentDirectory]
                 URLByAppendingPathComponent:@"batteryCapacity"]
                 URLByAppendingPathExtension:@"csv"];
    
[/code]

サンドボックス内のファイル保存先のパスを`NSSearchPathForDirectoriesInDomains`関数で取得し、ファイルパスを組み立てています。

[code]

    NSFileManager *fileManager =[NSFileManager defaultManager];
    if(![fileManager fileExistsAtPath:filePath.absoluteString]){
        [fileManager createFileAtPath:filePath.absoluteString contents:nil attributes:nil];
    }
    
[/code]

ファイルが存在しない場合に、空ファイルを生成しています。

[code]

    NSError *error = nil;
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingToURL:filePath error:&error];
    if(error){
        // error handling
        NSLog(@"%@", [error localizedDescription]);
            return;
    }
    [fileHandle seekToEndOfFile];
    [fileHandle writeData:[wirteData dataUsingEncoding:NSUTF8StringEncoding]];
    [fileHandle closeFile];
    
[/code]

`NSFileHandle`でファイルを開き（このときファイルが存在しないとエラーになるので、先に空ファイルを生成しています）、`seekToEndOfFile`で末尾に移動し、データを書き込んでいます。

[code]

    2016-07-17 15:35:31 +0900, 100
    2016-07-17 15:35:32 +0900, 100
    2016-07-17 15:35:33 +0900, 100
    2016-07-17 15:35:34 +0900, 100
    2016-07-17 15:35:35 +0900, 100
    2016-07-17 15:35:36 +0900, 100
    
[/code]

このようなCSVファイルが端末内に書き出されます。

* * *

実際的にバッテリー状態を監視することを考えると、`NSTimer`で１秒毎にログを書き込むのは非効率的なので、`UIDeviceBatteryLevelDidChangeNotification`の通知を受け取って、`[UIDevice
currentDevice].batteryLevel`で変化が起きたときのバッテリー残量のみを残すという方法を取った方が良いと思います。


CC BY-SA 3.0
2016-07-17T06:44:47.897
27680
2016-07-17T07:03:23.543
2016-07-17T07:03:23.543
5337
5337
27669
answer