2019年12月29日日曜日

ipaファイル内のembedded.mobileprovisionから有効期限(ExpirationDate)を読み込む

AdHocやEnterpriseでのリリースでは有効期限があるので、それを取り出し、ユーザーに見せるようにしていた。

embedded.mobileprovisionをNSDataで読み込み、NSStringに全変換してから、NSStringのメソッドで処理していたのだが、暗号化された文字コード以外の値が入っているため、SDK13ではクラッシュを招いてしまった。

反省して、NSPropertyListSerializationで処理する正しい方法に改めた。



-(NSString *)readExpiredDay
{
    NSString *path = [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"];
    if (!path) return @"NG";
    NSData *data = [NSData dataWithContentsOfFile:path];
    
    // plistを抽出
    NSRange start = [data rangeOfData:[@"<?xml" dataUsingEncoding:NSUTF8StringEncoding] options:NSDataSearchBackwards range:NSMakeRange(0, [data length])];
    NSRange end = [data rangeOfData:[@"</plist>" dataUsingEncoding:NSUTF8StringEncoding] options:NSDataSearchBackwards range:NSMakeRange(start.location, [data length] - start.location)];
    NSData *body = [data subdataWithRange:NSMakeRange(start.location, end.location + end.length - start.location)];
    
    // NSDictionaryに変換
    NSPropertyListFormat format;
    NSDictionary *dic = (NSDictionary *)[NSPropertyListSerialization propertyListWithData:body options:NSPropertyListMutableContainersAndLeaves format:&format error:nil];
    if (!dic) return @"NG";
    
    // ExpirationDateを検出
    NSDate *date = dic[@"ExpirationDate"];
    if (!date) return @"NG";

    // UTC->JST変換し文字列とする
    NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
    [fmt setDateFormat:@"yyyy/MM/dd"];
    [fmt setTimeZone:[NSTimeZone defaultTimeZone]];
    NSString *str = [fmt stringFromDate:date];
    
    if (!str) return @"NG";
    return str;
}

swift版のM.Ike様のコードを参考にしました。


[Xcode11 SDK13] UISplitViewControllerでメニューが引っ込まない

Xcode6(SDK8)で非推奨になったのに、放っておいたら、「戻る」ボタンは表示されないし、メニューとして使っているプライマリビューは隠れないし、たいへんなことになってしまった。

willHideViewController:withBarButtonItem:forPopoverController:
のforPopoverControllerでメニューのオブジェクトを取得し、メニューを隠すのに使っていたのだが、SDK13からforPopoverControllerは何も返さなくなってしまった。

修正点は以下の2点。

1. メニューを隠す


縦画面の時だけ隠したいので、端末の向きを、

// NotificationCenter登録


[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changedOrientation:) name:UIDeviceOrientationDidChangeNotification object:nil];

// ハンドラ


- (void)changedOrientation:(NSNotificationCenter*)center {
    switch ([UIDevice currentDevice].orientation) {
        case UIDeviceOrientationLandscapeLeft:
        case UIDeviceOrientationLandscapeRight:
        case UIDeviceOrientationPortrait:
        case UIDeviceOrientationPortraitUpsideDown:
            _orientation = [UIDevice currentDevice].orientation;
            _splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAutomatic;
            break;
        default: //FaceUp及びFaceDownでは何もしない。
            break;
    }
}

で_orientationを取得した上で、今までどおり、ゆっくり隠したいので0.3秒を指定。

// メニューを押した後に、以下を実装


switch (_orientation) {
    case UIDeviceOrientationPortrait:
    case UIDeviceOrientationPortraitUpsideDown: {
        [UIView animateWithDuration:0.3 animations:^{
        _splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModePrimaryHidden; }];
        break; }
    default:
        break;
}

2. 「戻る」ボタンを表示する


以下のようなものをviewDidLoadに実装するだけで、あとは自動的にやってくれる

navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem;

はずだったのだが、起動時の一発目に、左上の文字がでないことがあった。
白紙から作り直せばうまくいくのだが、そういう訳にもいかないので、viewDidAppearに以下を実装。

navigationItem.leftBarButtonItem.title = @"●●●";