在运行时获取配置文件的到期日期?

问题描述 投票:0回答:6

我有一个应用程序,我经常通过临时分发方法分发给测试人员。其中一些测试人员“很机灵”,并且对配置文件和季度到期有足够的了解,并且可以(如果我忘记了)推动我重建新版本以供他们测试。

然而,一些用户似乎总是到了它停止运行的地步,然后对此抱怨和抱怨——尽管他们可能忽略了 iOS 级别的提醒。

我的问题是我可以在运行时以编程方式获取到期日期并执行我自己的“应用内”警报或系统通知以提醒他们下载更新版本吗?

ios notifications runtime provisioning profiles
6个回答
8
投票

您正在寻找类似的东西

"<key>ExpirationDate</key><date>2014-12-06T00:26:10Z</date>" in [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"]

但是到达那里并不容易!该代码可以改进,其中部分内容基于其他 stackoverflow 帖子。注意:另一种选择是加载 item plist 之间的所有内容 和 /plist 到 ...plist (字典)。但既然我们已经在那里了,我们只需用手找到兄弟姐妹即可。

- (NSString*) getExpiry{

    NSString *profilePath = [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"];
    // Check provisioning profile existence
    if (profilePath)
    {
        // Get hex representation
        NSData *profileData = [NSData dataWithContentsOfFile:profilePath];
        NSString *profileString = [NSString stringWithFormat:@"%@", profileData];

        // Remove brackets at beginning and end
        profileString = [profileString stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:@""];
        profileString = [profileString stringByReplacingCharactersInRange:NSMakeRange(profileString.length - 1, 1) withString:@""];

        // Remove spaces
        profileString = [profileString stringByReplacingOccurrencesOfString:@" " withString:@""];


        // Convert hex values to readable characters
        NSMutableString *profileText = [NSMutableString new];
        for (int i = 0; i < profileString.length; i += 2)
        {
            NSString *hexChar = [profileString substringWithRange:NSMakeRange(i, 2)];
            int value = 0;
            sscanf([hexChar cStringUsingEncoding:NSASCIIStringEncoding], "%x", &value);
            [profileText appendFormat:@"%c", (char)value];
        }

        // Remove whitespaces and new lines characters
        NSArray *profileWords = [profileText componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

        //There must be a better word to search through this as a structure! Need 'date' sibling to <key>ExpirationDate</key>, or use regex
        BOOL sibling = false;
        for (NSString* word in profileWords){
            if ([word isEqualToString:@"<key>ExpirationDate</key>"]){
                NSLog(@"Got to the key, now need the date!");
                sibling = true;
            }
            if (sibling && ([word rangeOfString:@"<date>"].location != NSNotFound)) {
                NSLog(@"Found it, you win!");
                NSLog(@"Expires: %@",word);
                return word;
            }
        }

    }

    return @"";
}

4
投票

快速版本:

// Returns `nil` if it fails
private func getProvisioningProfileExpirationDateAsString() -> String? {
    guard
        let profilePath = Bundle.main.path(forResource: "embedded", ofType: "mobileprovision"),
        let profileData = try? Data(contentsOf: URL(fileURLWithPath: profilePath)),
        // Note: We use `NSString` instead of `String`, because it makes it easier working with regex, ranges, substring etc.
        let profileNSString = NSString(data: profileData, encoding: String.Encoding.ascii.rawValue)
        else {
        print("WARNING: Could not find or read `embedded.mobileprovision`. If running on Simulator, there are no provisioning profiles.")
        return nil
    }


    // NOTE: We have the `[\\W]*?` check to make sure that variations in number of tabs or new lines in the future does not influence the result.
    guard let regex = try? NSRegularExpression(pattern: "<key>ExpirationDate</key>[\\W]*?<date>(.*?)</date>", options: []) else {
        print("Warning: Could not create regex.")
        return nil
    }

    let regExMatches = regex.matches(in: profileNSString as String, options: [], range: NSRange(location: 0, length: profileNSString.length))

    // NOTE: range `0` corresponds to the full regex match, so to get the first capture group, we use range `1`
    guard let rangeOfCapturedGroupForDate = regExMatches.first?.range(at: 1) else {
        print("Warning: Could not find regex match or capture group.")
        return nil
    }

    let dateAsString = profileNSString.substring(with: rangeOfCapturedGroupForDate)
    return dateAsString
}

2
投票

在iOS 13中,它不起作用。 NSData 的描述从

改变

{0x30822755 06092a86 4886f70d 010702a0 ... 889bfcbe ed768bac }

{length = ..., bytes = 0x30822755 06092a86 4886f70d 010702a0 ... 889bfcbe ed768bac }

试试这个代码:

NSData *data = ....;
NSUInteger dataLength = [data length];
NSMutableString *string =[NSMutableString stringWithCapacity:dataLength*2];

const unsigned char *dataBytes = [data bytes];
for (NSInteger idx = 0; idx < dataLength; ++idx)
{
    [string appendFormat:@"%02x", dataBytes[idx]];
}

1
投票

在 iOS 13 中,为了简化 Ttnka 的建议,您可以使用 profileData.debugDescription 创建 profileString(来自最初发布的 Objective-C 代码):

NSString *profileString = [NSString stringWithFormat:@"%@", profileData.debugDescription];

参考:https://developer.apple.com/forums/thread/119111


0
投票

结合@Pierre-Francoys Brousseau 和@Ttnka 的答案:

- (NSString *) getAppExpiry
{

    NSString *profilePath = [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"];
    // Check provisioning profile existence
    if (profilePath)
    {
        // Get hex representation
        NSData *profileData = [NSData dataWithContentsOfFile:profilePath];
        NSString *profileString;
        
        if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 13.0)
        {
            NSUInteger dataLength = [profileData length];
            NSMutableString *string =[NSMutableString stringWithCapacity:dataLength*2];

            const unsigned char *dataBytes = [profileData bytes];
            for (NSInteger idx = 0; idx < dataLength; ++idx)
            {
                [string appendFormat:@"%02x", dataBytes[idx]];
            }
            
            profileString = string;

        }
        else
        {
            profileString = [NSString stringWithFormat:@"%@", profileData];
            
            // Remove brackets at beginning and end
            profileString = [profileString stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:@""];
            profileString = [profileString stringByReplacingCharactersInRange:NSMakeRange(profileString.length - 1, 1) withString:@""];

            // Remove spaces
            profileString = [profileString stringByReplacingOccurrencesOfString:@" " withString:@""];
        }

        // Convert hex values to readable characters
        NSMutableString *profileText = [NSMutableString new];
        for (int i = 0; i < profileString.length; i += 2)
        {
            NSString *hexChar = [profileString substringWithRange:NSMakeRange(i, 2)];
            int value = 0;
            sscanf([hexChar cStringUsingEncoding:NSASCIIStringEncoding], "%x", &value);
            [profileText appendFormat:@"%c", (char)value];
        }
        
        // Remove whitespaces and newline characters
        NSArray *profileWords = [profileText componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
        
        // There must be a better word to search through this as a structure! Need 'date' sibling to <key>ExpirationDate</key>
        BOOL sibling = false;
        for (NSString* word in profileWords){
            if ([word isEqualToString:@"<key>ExpirationDate</key>"]){
                NSLog(@"Got to the key, now need the date!");
                sibling = true;
            }
            if (sibling && ([word rangeOfString:@"<date>"].location != NSNotFound)) {
                NSLog(@"Found it, you win!");
                NSLog(@"Expires: %@", word);
                return word;
            }
        }
    }
    else
        NSLog(@"profile path is nil");
    
    NSLog(@"Could not find app expiry date");
    
    return @"";
}

0
投票

这是我制作的一个简单的解决方案。它使用范围来提取

<date>
字段。由于有两个,
CreationDate
ExpirationDate
,您只需调整要查找的索引即可。只需使用您的文件路径创建一个
URL
对象即可。

func getExpirationDate(for filePath: URL) -> String? {
    guard let profileData = try? Data(contentsOf: filePath),
          let profileString = String(data: profileData, encoding: .ascii) else {
        return nil
    }
    
    // Index 0 - Creation Date
    // Index 1 - Expiration Date
    let startRange = profileString.ranges(of: "<date>")[1]
    let endRange = profileString.ranges(of: "</date>")[1]
    
    let startIndex = profileString.index(startRange.upperBound, offsetBy: 0)
    let endIndex = profileString.index(endRange.lowerBound, offsetBy: 0)

    let expirationDate = profileString[startIndex..<endIndex]
    
    return String(expirationDate)
}
© www.soinside.com 2019 - 2024. All rights reserved.