IOS app进行自动更新(不用App Store)

让编程改变世界 2015-03-13

在APP没有登录到app store的前提下,如果app需要更新,如何通知用户呢?

实现思想:

app运行时,如果发现最新的版本号和现在app内的版本号不同时,在桌面的图标的右上角显示一个红色的圆圈1,蹦出一个提示框提示升级或者取消,点击提示升级时,应启动默认的浏览器跳转到能下载到最新版本app的页面。

实现流程:

1.在每一次程序开始运行的时候,都要进行一次版本对比。

2.对比的双方是,从app内部取得的版本号和从服务器端取得的需要安装的最新的版本号

3.客户端要有能取到最新版本号的接口(这里采用的方案是,在服务器端放一个.json的文件,里面只存最新的版本号)

实现代码:

//在appdelegate.m中的applicationDidBecomeActive方法里,填入以下代码

//appdelegate.m中的applicationDidBecomeActive方法会在每次程序激活时运行

- (void)applicationDidBecomeActive:(UIApplication *)application

{

    //UpdateManager是github上的一个开源插件,专门用来进行这种版本更新的操作

       //https://github.com/slmcmahon/UpdateManager

    UpdateManager *manager = [UpdateManagersharedManager];

    //设定服务器端用来取得最新app版本号的json文件(xxxx代表根据各个具体项目自己设置的文件夹名)

    [manager setVersionUrl:@"/xxxx/appVersion.json"];

    //提示用户有新版本后,跳转到下载页面的URL

    [manager setPListUrl:@"/xxx/xxxxxxx.html"];

    //进行版本号检测并更新

    [manager checkForUpdates];

}

//UpdateManager.h

//

//  UpdateManager.h

//  ReactiveLearning

//

//  Created by Stephen L. McMahon on 8/4/13.

//

//  implmementation: https://gist.github.com/slmcmahon/6152160

#import <Foundation/Foundation.h>

@interface UpdateManager : NSObject

@property (nonatomic, copy) NSString *pListUrl;

@property (nonatomic, copy) NSString *versionUrl;

@property (nonatomic, copy) NSString *currentServerVersion;

+ (UpdateManager *)sharedManager;

- (void)checkForUpdates;

- (void)performUpdate:(NSString *)pageUrl;

@end

//UpdateManager.m

//具体连接的地方,需要自己完善了

//

//  UpdateManager.m

//  ReactiveLearning

//

//  Created by Stephen L. McMahon on 8/4/13.

//

//  interface: https://gist.github.com/slmcmahon/6152156

static NSString *const kPreferenceAskUpdate = @"pref_ask_update";

#import "UpdateManager.h"

#import "AFNetworking.h"

#import "UIAlertView+Blocks.h"

#import "RIButtonItem.h"

#import "OMPromise.h"

#import "OMDeferred.h"

@implementation UpdateManager

+ (UpdateManager *)sharedManager {

    static UpdateManager *sharedManager = nil;

    if (!sharedManager)

    {

        sharedManager = [[super allocWithZone:nil] init];

    }

    return sharedManager;

}

+ (id)allocWithZone:(NSZone *)zone {

    return [selfsharedManager];

}

// This will return the version by combining both the version and build fields in

// the iOS Application Target found in the summary section of the current build target

- (NSString *)appVersion {

    NSDictionary *info = [[NSBundlemainBundle] infoDictionary];

    NSString *version = [info objectForKey:@"CFBundleShortVersionString"];

    NSString *build = [info objectForKey:@"CFBundleVersion"];

    if ([build isEqualToString:@""]) {

        return [NSString stringWithFormat:@"%@", version];

    } else {

        return [NSString stringWithFormat:@"%@.%@", version, build];

    }

}

- (BOOL)shouldAskForUpdate {

    NSUserDefaults *prefs = [NSUserDefaultsstandardUserDefaults];

    if ([prefs valueForKey:kPreferenceAskUpdate] == nil) {

        return YES;

    }

    return [prefs boolForKey:kPreferenceAskUpdate];

}

// this is exposed as a public method in case you would like to create another view, an App Update

// view perhaps, that explains that there is an update for the application and allow the user to

// manually update in case they opted NOT to when initially prompted.

- (void)disableAskUpdate {

    NSUserDefaults *prefs = [NSUserDefaultsstandardUserDefaults];

    [prefs setBool:NOforKey:kPreferenceAskUpdate];

    [prefs synchronize];

}

- (void)performUpdate:(NSString *)pageUrl {

    // in case there's a network issue or some other type of failure, we go

    // ahead and reset the preference so that the user will be prompted again

    // to update on future sessions.

    NSUserDefaults *prefs = [NSUserDefaultsstandardUserDefaults];

    [prefs setBool:YESforKey:kPreferenceAskUpdate];

    NSLog(@"%@", pageUrl);

    NSURL *url = [NSURL URLWithString:pageUrl];

    UIApplication *thisApp = [UIApplicationsharedApplication];

    // turn off the badge

    [thisApp setApplicationIconBadgeNumber:0];

    // launch Mobile Safari, which will immediately attempt to install the application

    // from the URL that was specified.

    [thisApp openURL:url];

}

- (void)checkForUpdates {

    NSURL *defaultSettingsFile = [[NSBundle mainBundle] URLForResource:@"DefaultSettings" withExtension:@"plist"];

    NSDictionary *defaultSettings = [NSDictionary dictionaryWithContentsOfURL:defaultSettingsFile];

    NSString *baseUrlString = [defaultSettings objectForKey:@"login_host_pref"];

    

    NSString *currentVersion = [self appVersion];

    

    OMPromise *chain = [self GET:_versionUrl parameters:nil baseUrl:baseUrlString];

    [chain fulfilled:^(id result) {

        NSLog(@"%@", result);

        UIApplication *thisApp = [UIApplicationsharedApplication];

        // assumes that the server will be responding with a JSON object containing at least:

        // { CurrentVersion: "1.2.3.4" }

        NSString *serverVersion = [result valueForKeyPath:@"CurrentVersion"];

        if ([self compareVersion:serverVersion toVersion:currentVersion] <= 0) {

            // make sure that we don't have a badge showing since there are no updates.

            [thisApp setApplicationIconBadgeNumber:0];

            _currentServerVersion = currentVersion;

            NSLog(@"The application is up to date.");

            return;

        }

        // we have determined that there is an update.  We are going to ask the user if they would like to

        // update immediately, but they may choose not to, so we will set a badge here to remind them later

        // that there are pending updates.

        [thisApp setApplicationIconBadgeNumber:1];

        _currentServerVersion = serverVersion;

        

        // if we have previously asked the user if they wanted to update and they refused, then we don't

        // want to continue to bother them about it.

        //if (![self shouldAskForUpdate]) {

        //NSLog(@"There is a new version, but the user has opted to update manually later.");

        //return;

        //}

        

        // this action will be performed if the user selects "OK" in the upcoming alert view.  If the

        // user selects "OK" then we will attempt to perform the update.

        RIButtonItem *okButton = [RIButtonItem itemWithLabel:@"アップデート" action:^{

            NSString *updatePageUrl = [baseUrlString stringByAppendingString:_pListUrl];

            [self performUpdate:updatePageUrl];

        }];

        

        // if the user cancels the update, then we will set a persistent preference value so that it

        // will not ask them on subsequent runs of the application.

        RIButtonItem *cancelButton = [RIButtonItem itemWithLabel:@"キャンセル" action:^{

            //[self disableAskUpdate];

        }];

        

        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"XXXX からの通知(アップデート)"

                                                        message:@"新しいバージョンがあります。アップデートしますか?"

                                               cancelButtonItem:cancelButton

                                               otherButtonItems:okButton, nil];

        [alert show];

    }];

    [chain failed:^(NSError *error) {

        if (error) {

            NSLog(@"Update error: %@", [error localizedDescription]);

        }

        [self setCurrentServerVersion:currentVersion];

    }];

}

- (OMPromise*)GET:(NSString*)path parameters:(id)parameters baseUrl:(NSString*)baseUrl{

    OMDeferred *deferred = [OMDeferred deferred];

    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManagermanager];

    manager.requestSerializer = [AFHTTPRequestSerializerserializer];

    manager.responseSerializer = [AFJSONResponseSerializerserializer];

    

    NSString *urlString = [baseUrl stringByAppendingString:path];

    DDLogVerbose(@"Connecting to %@", urlString);

    

    [manager GET:urlString  parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {

        [deferred fulfil:responseObject];

    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

        [deferred fail:error];

    }];

    return deferred.promise;

}

// compares all of the bits in the version identifier starting from the left and

// returns as soon as it finds a difference.  same = 0, l > r = 1, r > l = -1

- (int)compareVersion:(NSString *)firstVersion toVersion:(NSString *)secondVersion {

    NSMutableArray *fvArray = [self splitVersionString:firstVersion];

    NSMutableArray *svArray = [self splitVersionString:secondVersion];

    

    while ([fvArray count] < [svArray count]) {

        [fvArray addObject:[NSNumber numberWithInt:0]];

    }

    while ([svArray count] < [fvArray count]) {

        [svArray addObject:[NSNumber numberWithInt:0]];

    }

    

    for (int i = 0; i < [fvArray count]; i++) {

        int a = [[fvArray objectAtIndex:i] intValue];

        int b = [[svArray objectAtIndex:i] intValue];

        

        if (a > b) {

            return 1;

        }

        

        if (b > a) {

            return -1;

        }

    }

    return 0;

}

- (NSMutableArray *)splitVersionString:(NSString *)version {

    return [NSMutableArrayarrayWithArray:[version componentsSeparatedByString:@"."]];

}

@end

//xxxx.json文件的内容

{
	"CurrentVersion": "3.0.1.0"
}

相关推荐