【发布时间】:2011-05-17 17:27:24
【问题描述】:
我将使用新的数据结构向我的应用发送更新,因此如果用户正在更新我的应用,我需要更新他们当前的数据。所以我想知道如何以编程方式判断用户是否更新了我的应用程序或安装了新副本(如果安装了新副本,我不需要更新任何内容)?
【问题讨论】:
-
您使用的是什么“数据结构”?核心数据?
标签: iphone ios4 iphone-sdk-3.0
我将使用新的数据结构向我的应用发送更新,因此如果用户正在更新我的应用,我需要更新他们当前的数据。所以我想知道如何以编程方式判断用户是否更新了我的应用程序或安装了新副本(如果安装了新副本,我不需要更新任何内容)?
【问题讨论】:
标签: iphone ios4 iphone-sdk-3.0
检查数据结构是一个可靠的解决方案。我开始在我自己的应用程序中担心那些不升级多个版本的人。我觉得这会导致无数的结构检查。我在下面显示的代码确定版本和以前的版本并将其存储在NSUserDefaults 中。如果需要,您可以为这些不同的版本差异场景编写代码。
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
BOOL versionUpgraded;
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
NSString *preVersion = [prefs stringForKey:@"appVersion"];
if ([prefs stringForKey:@"appVersion"] != nil) {
//see if version is the same as prior
//if not it is an Upgraded
versionUpgraded = !([preVersion isEqualToString: version]);
} else {
//nil means new install
//This needs to be YES for the case that
//"appVersion" is not set anywhere else.
versionUpgraded = YES;
}
if (versionUpgraded) {
[prefs setObject:version forKey:@"appVersion"];
[prefs setObject:preVersion forKey:@"prevAppVersion"];
[prefs synchronize];
}
【讨论】:
这取决于您使用的数据结构类型。
一般来说,我建议您不要依赖检查您的应用程序版本:使用 2.0 的用户可能刚刚升级或它可能是新用户。
我宁愿检查是否已经存在数据结构,并采取相应措施。假设您使用的是 Sqlite 支持的 Core Data 存储,您可以检查 .sqlite 文件是否存在,或者检查存储中是否存在对象。
【讨论】:
只需将捆绑版本保存在某处并检查它是否与
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]
在每次应用启动时。
【讨论】:
我为此创建了一个类别。只需实现在标头中找到的两个新委托调用。它非常依赖 obj-c 运行时库,因此在使用它之前请确保您对它们有信心。
.h
#import <UIKit/UIKit.h>
@protocol UIApplicationDelegate <UIApplicationDelegate>
@optional
- (void) application:(UIApplication *)application willUpdateToVersion: (NSString*) newVersion fromVersion: (NSString*) previousVersion;
- (void) application:(UIApplication *)application didUpdateToVersion: (NSString*) newVersion fromVersion: (NSString*) previousVersion;
@end
@interface UIApplication (Versioning)
@end
.m
#import "UIApplication+Versioning.h"
#import <objc/message.h>
#import <objc/runtime.h>
static NSString* UIApplicationVersionFileName = @"app.ver";
@implementation UIApplication (Versioning)
+ (void) load {
Method original, swizzled;
original = class_getInstanceMethod(self, @selector(setDelegate:));
swizzled = class_getInstanceMethod(self, @selector(swizzled_setDelegate:));
method_exchangeImplementations(original, swizzled);
}
- (void) swizzled_setDelegate: (id<UIApplicationDelegate>) delegate {
IMP implementation = class_getMethodImplementation([self class], @selector(swizzled_application:didFinishLaunchingWithOptions:));
class_addMethod([delegate class], @selector(swizzled_application:didFinishLaunchingWithOptions:), implementation, "B@:@@");
Method original, swizzled;
original = class_getInstanceMethod([delegate class], @selector(application:didFinishLaunchingWithOptions:));
swizzled = class_getInstanceMethod([delegate class], @selector(swizzled_application:didFinishLaunchingWithOptions:));
method_exchangeImplementations(original, swizzled);
[self swizzled_setDelegate: delegate];
}
- (BOOL)swizzled_application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//Check for a version change
NSError* error;
NSArray* directories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* versionFilePath = [[directories objectAtIndex: 0] stringByAppendingPathComponent: UIApplicationVersionFileName];
NSString* oldVersion = [NSString stringWithContentsOfFile: versionFilePath
encoding: NSUTF8StringEncoding
error: &error];
NSString* currentVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey: @"CFBundleVersion"];
switch (error.code) {
case NSFileReadNoSuchFileError:
{
//Delegate methods will not be called first time
oldVersion = [currentVersion copy];
[currentVersion writeToFile: versionFilePath
atomically: YES
encoding: NSUTF8StringEncoding
error: &error];
break;
}
default:
{
NSLog(@"Warning: An error occured will loading the application version file -> Recreating file");
[[NSFileManager defaultManager] removeItemAtPath: versionFilePath
error: nil];
oldVersion = [currentVersion copy];
[currentVersion writeToFile: versionFilePath
atomically: YES
encoding: NSUTF8StringEncoding
error: &error];
break;
}
}
if( ![oldVersion isEqualToString: currentVersion] ) {
if ([[application delegate] respondsToSelector: @selector(application:willUpdateToVersion:fromVersion:)]) {
objc_msgSend([application delegate], @selector(application:willUpdateToVersion:fromVersion:), currentVersion, oldVersion);
}
[currentVersion writeToFile: versionFilePath
atomically: YES
encoding: NSUTF8StringEncoding
error: &error];
if ([[application delegate] respondsToSelector: @selector(application:didUpdateToVersion:fromVersion:)]) {
objc_msgSend([application delegate], @selector(application:willUpdateToVersion:fromVersion:), currentVersion, oldVersion);
}
}
SEL realSelector = @selector(swizzled_application:didFinishLaunchingWithOptions:);
return (BOOL) objc_msgSend([application delegate], realSelector, application, launchOptions);
}
@end
【讨论】: