Я отправлю обновление для своего приложения с новой структурой данных, поэтому, если пользователь обновляет мое приложение, мне нужно обновить их текущие данные. Поэтому мне было интересно, как я могу программно определить, обновил ли пользователь мое приложение или установил новую копию (если установлена новая копия, мне не нужно ничего обновлять)?
Как узнать, обновил ли пользователь приложение или установил новую копию?
Ответ 1
Это зависит от типа используемой структуры данных.
В общем, я бы посоветовал вам не полагаться на проверку вашей версии приложения: пользователь, использующий 2.0, может просто обновиться или может быть новым пользователем.
Я бы предпочел проверить, есть ли уже структура данных и действовать соответственно. Предполагая, что вы используете хранилище Core Data с поддержкой Sqlite, вы можете проверить, существует ли файл .sqlite или проверить, есть ли в вашем хранилище объекты.
Ответ 2
Проверка структуры данных - это твердое решение. Я начал беспокоиться в своих приложениях о людях, которые не обновляют несколько версий. Я чувствовал, что это приведет к множеству структурных проверок. Код, показанный ниже, определяет и сохраняет версию и предыдущую версию в 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];
}
Ответ 3
Просто сохраните версию пакета и проверьте, отличается ли она от
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]
при каждом запуске приложения.
Ответ 4
Я создал для этого категорию. Просто реализуйте два новых вызова делегата, найденных в заголовке. Он очень сильно зависит от библиотек времени выполнения 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, "[email protected]:@@");
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