[英]Get the EXPIRATION date of a Provisioning Profile at Run-time?
I have an app that I routinely pass out to testers via the ad-hoc distribution method.我有一个应用程序,我经常通过临时分发方法分发给测试人员。 Some of these testers are 'on the ball' and know enough about provisioning profiles and the quarterly expirations and can (if I forget) give me a nudge to rebuild a new version for them to test.这些测试人员中的一些人“随时待命”并且对配置文件和季度到期有足够的了解,并且可以(如果我忘记的话)给我一个建议,让我重新构建一个新版本以供他们测试。
However some of the users always seem to get to the point where it stops running and then bitch and moan about it - despite them probably dismissing the iOS level reminder.然而,一些用户似乎总是到了它停止运行的地步,然后抱怨它——尽管他们可能会忽略 iOS 级别的提醒。
My question is can I programatically get hold of the expiry date at runtime and do my own 'in-app' alerts or system notifications to remind them to pull down the newer version?我的问题是我可以在运行时以编程方式获取到期日期并执行我自己的“应用内”警报或系统通知以提醒他们拉下较新的版本吗?
You are looking for something like你正在寻找类似的东西
"<key>ExpirationDate</key><date>2014-12-06T00:26:10Z</date>" in [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"]
But getting there is not easy!但是到达那里并不容易! This code could be improved, parts of it were based on other stackoverflow posts.这段代码可以改进,部分代码基于其他 stackoverflow 帖子。 Note: Another option would be to load everything between item plist and /plist into ...a plist (dictionary).注意:另一种选择是将项目 plist 和 /plist 之间的所有内容加载到...a plist(字典)中。 But since we're already there, we just find the sibling by hand.但既然我们已经在那里了,我们只能手动找到兄弟姐妹。
- (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 @"";
}
Swift version:迅捷版:
// 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
}
In iOS 13, it doesn't work.在 iOS 13 中,它不起作用。 The description of NSData changes from NSData 的描述从
{0x30822755 06092a86 4886f70d 010702a0 ... 889bfcbe ed768bac }
to到
{length = ..., bytes = 0x30822755 06092a86 4886f70d 010702a0 ... 889bfcbe ed768bac }
Try this code:试试这个代码:
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]];
}
In iOS 13, to simplify Ttnka's suggestion, you can use profileData.在 iOS 13 中,为了简化 Ttnka 的建议,您可以使用 profileData。 debugDescription to create the profileString (from Objective-C code posted initially): debugDescription创建 profileString(来自最初发布的 Objective-C 代码):
NSString *profileString = [NSString stringWithFormat:@"%@", profileData.debugDescription];
Reference: https://developer.apple.com/forums/thread/119111参考: https : //developer.apple.com/forums/thread/119111
Combining @Pierre-Francoys Brousseau's and @Ttnka's answers:结合@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 @"";
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.