//
// CWLSynthesizeSingleton.h
// CocoaWithLove
//
// Created by Matt Gallagher on 2011/08/23.
// Copyright (c) 2011 Matt Gallagher. All rights reserved.
//
// Permission is given to use this source code file, free of charge, in any
// project, commercial or otherwise, entirely at your risk, with the condition
// that any redistribution (in part or whole) of source code must retain
// this copyright and permission notice. Attribution in compiled projects is
// appreciated but not required.
//
#import <objc/runtime.h>
#define CWL_DECLARE_SINGLETON_FOR_CLASS_WITH_ACCESSOR(classname, accessorMethodName) \
+ (classname *)accessorMethodName;
#if __has_feature(objc_arc)
#define CWL_SYNTHESIZE_SINGLETON_RETAIN_METHODS
#else
#define CWL_SYNTHESIZE_SINGLETON_RETAIN_METHODS \
- (id)retain \
{ \
return self; \
} \
\
- (NSUInteger)retainCount \
{ \
return NSUIntegerMax; \
} \
\
- (oneway void)release \
{ \
} \
\
- (id)autorelease \
{ \
return self; \
}
#endif
#define CWL_SYNTHESIZE_SINGLETON_FOR_CLASS_WITH_ACCESSOR(classname, accessorMethodName) \
\
static classname *accessorMethodName##Instance = nil; \
\
+ (classname *)accessorMethodName \
{ \
@synchronized(self) \
{ \
if (accessorMethodName##Instance == nil) \
{ \
accessorMethodName##Instance = [super allocWithZone:NULL]; \
accessorMethodName##Instance = [accessorMethodName##Instance init]; \
method_exchangeImplementations(\
class_getClassMethod([accessorMethodName##Instance class], @selector(accessorMethodName)),\
class_getClassMethod([accessorMethodName##Instance class], @selector(cwl_lockless_##accessorMethodName)));\
method_exchangeImplementations(\
class_getInstanceMethod([accessorMethodName##Instance class], @selector(init)),\
class_getInstanceMethod([accessorMethodName##Instance class], @selector(cwl_onlyInitOnce)));\
} \
} \
\
return accessorMethodName##Instance; \
} \
\
+ (classname *)cwl_lockless_##accessorMethodName \
{ \
return accessorMethodName##Instance; \
} \
\
+ (id)allocWithZone:(NSZone *)zone \
{ \
return [self accessorMethodName]; \
} \
\
- (id)copyWithZone:(NSZone *)zone \
{ \
return self; \
} \
- (id)cwl_onlyInitOnce \
{ \
return self;\
} \
\
CWL_SYNTHESIZE_SINGLETON_RETAIN_METHODS
#define CWL_DECLARE_SINGLETON_FOR_CLASS(classname) CWL_DECLARE_SINGLETON_FOR_CLASS_WITH_ACCESSOR(classname, shared##classname)
#define CWL_SYNTHESIZE_SINGLETON_FOR_CLASS(classname) CWL_SYNTHESIZE_SINGLETON_FOR_CLASS_WITH_ACCESSOR(classname, shared##classname)
//
// PSUpdateApp.h
// PSUpdateApp
//
// Created by iBo on 18/02/13.
// Copyright (c) 2013 D-Still. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "CWLSynthesizeSingleton.h"
typedef void(^PSUpdateAppCompletionBLock)(NSError *error, BOOL success, id JSON);
typedef enum {
DefaultStrategy = 0,
ForceStrategy,
RemindStrategy
} UpdateStrategy;
@interface PSUpdateApp : NSObject
CWL_DECLARE_SINGLETON_FOR_CLASS(PSUpdateApp)
@property (nonatomic) NSString *appID, *appStoreLocation, *appName, *route, *updatePageUrl;
@property (nonatomic) UpdateStrategy strategy;
@property (nonatomic) int daysUntilPrompt;
@property (nonatomic) NSDate *remindDate;
+ (id) startWithRoute:(NSString *)route;
+ (id) startWithAppID:(NSString *)appId;
+ (id) startWithAppID:(NSString *)appId store:(NSString *)store;
- (void) detectAppVersion:(PSUpdateAppCompletionBLock)completionBlock;
- (void) setURLAdHoc:(NSString *)url;
@end
//
// PSUpdateApp.m
// PSUpdateApp
//
// Created by iBo on 18/02/13.
// Copyright (c) 2013 D-Still. All rights reserved.
//
#ifndef PSUdateAppLocalizedStrings
#define PSUdateAppLocalizedStrings(key) \
NSLocalizedStringFromTable(key, @"PSUdateApp", nil)
#endif
#import "PSUpdateApp.h"
#import <AFNetworking/AFNetworking.h>
#define APPLE_URL @"http://itunes.apple.com/lookup?"
#define kCurrentAppVersion [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]
@interface PSUpdateApp () <UIAlertViewDelegate> {
NSString *_newVersion;
}
@end
@implementation PSUpdateApp
CWL_SYNTHESIZE_SINGLETON_FOR_CLASS(PSUpdateApp)
+ (id) startWithRoute:(NSString *)route
{
return [[self alloc] initWithAppID:nil store:nil route:route];
}
+ (id) startWithAppID:(NSString *)appId store:(NSString *)store
{
return [[self alloc] initWithAppID:appId store:store route:nil];
}
+ (id) startWithAppID:(NSString *)appId
{
return [[self alloc] initWithAppID:appId store:nil route:nil];
}
- (id) initWithAppID:(NSString *)appId store:(NSString *)store route:(NSString *)route
{
self = [super init];
if ( self ) {
[self setAppName:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"]];
[self setStrategy:DefaultStrategy];
[self setAppID:appId];
[self setAppStoreLocation: store ? store : [[NSLocale currentLocale] objectForKey: NSLocaleCountryCode]];
[self setDaysUntilPrompt:2];
[self setRoute:route];
}
return self;
}
- (void) detectAppVersion:(PSUpdateAppCompletionBLock)completionBlock
{
if ( _strategy == RemindStrategy && [self remindDate] != nil && ![self checkConsecutiveDays] )
return;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:[self setJsonURL]]];
[request setHTTPMethod:@"GET"];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
if ( [self isNewVersion:JSON] ) {
if ( completionBlock && ![self isSkipVersion] ) {
completionBlock(nil, YES, JSON);
} else if ( ![self isSkipVersion] ) {
[self showAlert];
} else {
if ( completionBlock )
completionBlock(nil, NO, JSON);
}
} else {
if ( completionBlock )
completionBlock(nil, NO, JSON);
}
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
if ( completionBlock && ![self isSkipVersion] )
completionBlock(error, NO, nil);
}];
[operation start];
}
- (NSString *) setJsonURL
{
return self.route ? self.route : [NSString stringWithFormat:@"%@id=%@&country=%@", APPLE_URL, self.appID, self.appStoreLocation];
}
- (void) setURLAdHoc:(NSString *)url
{
[self setRoute:[NSString stringWithFormat:url, self.appStoreLocation]];
}
#pragma mark - Check version
- (BOOL) isNewVersion:(NSDictionary *)dictionary
{
if ( [[dictionary objectForKey:@"results"] count] > 0 ) {
_newVersion = [[[dictionary objectForKey:@"results"] objectAtIndex:0] objectForKey:@"version"];
[self setUpdatePageUrl:[[[dictionary objectForKey:@"results"] objectAtIndex:0] objectForKey:@"trackViewUrl"]];
if ([[[dictionary objectForKey:@"results"] objectAtIndex:0] objectForKey:@"type"]) {
[self setStrategy: [[[[dictionary objectForKey:@"results"] objectAtIndex:0] objectForKey:@"type"] isEqualToString:@"mandatory"] ? ForceStrategy : DefaultStrategy];
}
return [kCurrentAppVersion compare:_newVersion options:NSNumericSearch] == NSOrderedAscending;
}
return NO;
}
- (BOOL) isSkipVersion
{
return [[[NSUserDefaults standardUserDefaults] objectForKey:@"skipVersion"] isEqualToString:_newVersion];
}
#pragma mark - remindDate getter / setter
- (NSDate *) remindDate
{
return [[NSUserDefaults standardUserDefaults] objectForKey:@"remindDate"];
}
- (void) setRemindDate:(NSDate *)remindDate
{
[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:@"remindDate"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
#pragma mark - Show alert
- (void) showAlert
{
switch ( self.strategy ) {
case DefaultStrategy:
default:
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:PSUdateAppLocalizedStrings(@"alert.success.title")
message:[NSString stringWithFormat:PSUdateAppLocalizedStrings(@"alert.success.default.text"), self.appName, _newVersion]
delegate:self
cancelButtonTitle:PSUdateAppLocalizedStrings(@"alert.button.skip")
otherButtonTitles:PSUdateAppLocalizedStrings(@"alert.button.update"), nil];
[alertView show];
}
break;
case ForceStrategy:
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:PSUdateAppLocalizedStrings(@"alert.success.title")
message:[NSString stringWithFormat:PSUdateAppLocalizedStrings(@"alert.success.force.text"), self.appName, _newVersion]
delegate:self
cancelButtonTitle:PSUdateAppLocalizedStrings(@"alert.button.update")
otherButtonTitles:nil, nil];
[alertView show];
}
break;
case RemindStrategy:
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:PSUdateAppLocalizedStrings(@"alert.success.title")
message:[NSString stringWithFormat:PSUdateAppLocalizedStrings(@"alert.success.remindme.text"), _appName, _newVersion]
delegate:self
cancelButtonTitle:PSUdateAppLocalizedStrings(@"alert.button.skip")
otherButtonTitles:PSUdateAppLocalizedStrings(@"alert.button.update"), PSUdateAppLocalizedStrings(@"alert.button.remindme"), nil];
[alertView show];
}
break;
}
}
#pragma mark - UIAlertViewDelegate Methods
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
switch ( self.strategy ) {
case DefaultStrategy:
default:
{
if ( buttonIndex == 0 ) {
[[NSUserDefaults standardUserDefaults] setObject:_newVersion forKey:@"skipVersion"];
[[NSUserDefaults standardUserDefaults] synchronize];
} else {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:self.updatePageUrl]];
}
}
break;
case ForceStrategy:
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:self.updatePageUrl]];
break;
case RemindStrategy:
{
if ( buttonIndex == 0 ) {
[[NSUserDefaults standardUserDefaults] setObject:_newVersion forKey:@"skipVersion"];
[[NSUserDefaults standardUserDefaults] synchronize];
} else if ( buttonIndex == 1 ) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:self.updatePageUrl]];
} else {
[self setRemindDate:[NSDate date]];
}
}
break;
}
}
#pragma mark - Check if have passed
- (BOOL) checkConsecutiveDays
{
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *today = [NSDate date];
NSDate *dateToRound = [[self remindDate] earlierDate:today];
NSDateComponents * dateComponents = [gregorian components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit
fromDate:dateToRound];
NSDate *roundedDate = [gregorian dateFromComponents:dateComponents];
NSDate *otherDate = (dateToRound == [self remindDate]) ? today : [self remindDate] ;
NSInteger diff = abs([roundedDate timeIntervalSinceDate:otherDate]);
NSInteger daysDifference = floor(diff/(24 * 60 * 60));
return daysDifference >= _daysUntilPrompt;
}
@end
分享到:
相关推荐
苹果iOS app开发之更新升级app的办法.zip
SwiftWeather:采用Swift 2开发的iOS天气App。应用程序一直积极升级到采用最新的iOS和Swift语言特性。
开发 iOS 应用程序,您需要: Mac 电脑,运行 OS X 10.8 (Mountain Lion) 或更高版本 Xcode iOS SDK Xcode 是 Apple 的集成开发环境 (IDE)。Xcode 包括源代码编辑器、图形用户界面编辑器和许多其他功 能。iOS SDK 扩展...
iOS 16 真机开发包 正式版. 使用方法: 将下载好的调试包解压,快捷键command+shift+g前往文件夹: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport 把解压后的文件放入到该...
注意:升级到iOS 16后需打开开发者模式否则Xcode会报错“Failed to prepare device for development”,一直转圈不能调试。入口为:设置-隐私与安全性 安全性-开发者模式打开,打开后会重启手机,之后再调试就可以了...
iOS 16 真机开发包 beta版. 使用方法: 将下载好的调试包解压,快捷键command+shift+g前往文件夹: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport 把解压后的文件放入到该...
注意:升级到iOS 16后需打开开发者模式否则Xcode会报错“Failed to prepare device for development”,一直转圈不能调试。入口为:设置-隐私与安全性 安全性-开发者模式打开,打开后会重启手机,之后再调试就可以了...
Xcode升级到iOS15以后发现无法创建category了,现已找到解决方案供大家参考。 这是由于Xcode15缺少了"CategoryNSObject"和"ExtensionNSObject"两个文件夹导致的,解决办法就是将这两个加进去就好了。 如果你有旧的...
首先为什么要使用vBox虚拟机?很简单, 1、我不想买Mac本本 2、黑苹果安装苦难,危险。正常按照网络上的教程,不折腾个7、8天是不行的,还不一定能... -xcode_4.2_and_ios_5_sdk使用的iOS开发软件 来吧,还等什么。
xcode配置iOS11.0开发包 解决iOS 11.0升级后无法真机测试 Could not find Developer Disk Image 使用方法: 在“/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport”里添加...
混合开发的App(HybridApp)就是在一个App中内嵌一个轻量级的浏览器,一部分原生的功能改为Html5来开发,这部分功能不仅能够在不升级App的情况下动态更新,而且可以在Android或iOS的App上同时运行,让用户的体验更好...
WWDC(苹果开发者大会)刚刚过去不久,iOS 10将不久来袭,是时候升级你的iOS开发装备了!小编整理了10款必备开发工具,让你的开发过程事半功倍。 SourceTree SourceTree是一个免费的Mac软件,主要用于Git和...
ain 16-bit or P3 assets if the app is targeting iOS releases earlier than iOS 9.
当我们的app开发完成之后,无可避免的以后会进行产品升级,那么我们希望在客户的手机上让app进行自动升级,可以分为自动升级和手动升级。 自动升级:一般在客户app第一次打开首页的时候。 手动升级:在app界面提供一...
下载解压放到/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport即可,重启Xcode即可,无需升级系统和Xcode
IOS开发常见问题及常用技巧 1.升级到iOS 8.0 SDK以后,创建的新项目会带有LaunchScreen.xib文件作为App的启动界面,此时若不作任何设置就把基于8.0 SDK的App运行在7.0等版本的设备上,可能会出现应用不能全屏,为了解决...
官网地址: : Vue.js开发者使用Vue语法编写代码, uni-app框架将其编译到小程序(微信/支付宝/百度/字节跳动/ QQ /钉钉),App(iOS / Android),H5等多个平台,保证其正确运行并达到优秀体验。uni-app的特点开发者...
iOS升级到10.2,Xocde不能真机调试。这个是真机调试资源包,下载以后,加入到下面的目录下就可以支持了。 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport