简体   繁体   English

如何知道用户是否更新了应用程序或安装了新副本?

[英]How to know if user has updated app or installed a fresh copy?

I will be sending out an update to my app with a new data structure, therefore if a user is updating my app I need to update their current data. 我将使用新的数据结构向我的应用发送更新,因此如果用户正在更新我的应用,我需要更新他们的当前数据。 So I was wondering how can I programatically tell if the user updated my app or installed a new copy (if a new copy is installed I don't need to update anything) ? 所以我想知道如何以编程方式告诉用户是否更新了我的应用程序或安装了新副本(如果安装了新副本,我不需要更新任何内容)?

That depends on the kind of data structure you're using. 这取决于您使用的数据结构类型。

In general, I would advise you against relying on checking your application version: a user using 2.0 might have just upgraded or it might be a new user. 一般情况下,我建议您不要依赖于检查应用程序版本:使用2.0的用户可能刚刚升级, 或者可能是新用户。

I'd rather check if there's a data structure already, and act accordingly. 我宁愿检查是否已有数据结构,并采取相应措施。 Assuming that you're using a Sqlite-backed Core Data storage, you can either check whether the .sqlite file exists, or check if there are objects in your storage. 假设您使用的是Sqlite支持的Core Data存储,您可以检查.sqlite文件是否存在,或者检查存储中是否有对象。

Checking the data structure is a solid solution. 检查数据结构是一个可靠的解决方案。 I began to worry in my own apps about folks who don't upgrade for several versions. 在我自己的应用程序中,我开始担心没有为多个版本升级的人。 I felt this would lead to a myriad of structure checks. 我觉得这会导致无数的结构检查。 The code I show below determines and stores the version and previous version in the NSUserDefaults . 我在下面显示的代码确定并存储NSUserDefaults的版本和以前的版本。 You could code for those varying version difference scenarios if needed. 如果需要,您可以编写那些不同的版本差异方案。

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];
}

Just save the bundle version somewhere and check if it differs from 只需将捆绑版本保存在某处并检查它是否与之不同

[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]

on each app startup. 在每个应用程序启动。

I have created a category for this. 我为此创建了一个类别。 Just implement the two new delegate calls found in the header. 只需实现标题中找到的两个新委托调用。 It relies quite heavily on the obj-c runtime libraries, so make sure you are confident with them before using this. 它非常依赖于obj-c运行时库,因此在使用它之前请确保您对它们充满信心。

.h 。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 .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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM