//
// 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版本和升级.url`:这个链接可能指向一个教程,详细解释如何在代码中判断和处理版本更新。 - `苹果app不能出现 当前版本 字样.txt`:文件可能列出苹果关于此规定的详细信息。 - `iOS开发 ...
在iOS应用开发中,定期检测并提示用户更新App版本是保持应用活跃度和用户...总的来说,通过一句代码实现的版本检测和更新跳转是iOS开发中的一个实用技巧,它能帮助开发者有效地提醒用户升级应用,保持应用的最新状态。
开发 iOS 应用程序,您需要: Mac 电脑,运行 OS X 10.8 (Mountain Lion) 或更高版本 Xcode iOS SDK Xcode 是 Apple 的集成开发环境 (IDE)。Xcode 包括源代码编辑器、图形用户界面编辑器和许多其他功 能。iOS SDK 扩展...
SwiftWeather是一款基于Swift...通过分析SwiftWeather项目,我们可以深入学习Swift编程语言、iOS开发最佳实践以及如何构建一个完整的iOS应用。无论是对于初学者还是经验丰富的开发者,这个项目都提供了丰富的学习资源。
Java和iOS自动升级服务是移动应用开发中一个重要的功能,它允许用户无痛地更新到最新版本的应用,提高用户体验并确保他们始终运行安全且优化的软件。以下是对这个主题的详细解析: 首先,我们要理解自动升级服务的...
在iOS开发中,开发者可能会创建这样一个组件来跟踪当前应用的版本号,比较它与App Store中的最新版本,从而决定是否显示更新提示。此外,这个模块还可能负责处理更新过程,比如下载更新包、暂停或恢复下载、处理错误...
uni-app 项目基于 IOS 平台的开发及配置 一、 开发环境 在uni-app项目基于IOS平台的开发及配置中,首先需要安装Xcode 12.1 及以上版本,并下载HBuilderX的IOS版本,下载地址为...
iOS 16 真机开发包 正式版. 使用方法: 将下载好的调试包解压,快捷键command+shift+g前往文件夹: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport 把解压后的文件放入到该...
在iOS开发领域,掌握基础知识是至关重要的,尤其是对于初学者来说。这个“iOS开发基础教程源代码 完整版”提供了宝贵的实践资源,帮助开发者深入理解iOS应用开发的核心概念。这个压缩包包含的"Begin_iOS_5_projects_...
综上所述,文档中提到的每一个知识点都是iOS开发过程中不可或缺的部分。掌握这些知识对iOS开发新手来说是非常重要的。开发者在阅读完这些内容之后,应当能够更好地理解iOS 7的应用开发流程,以及如何使用Xcode和其他...
在iOS应用开发过程中,开发者时常会遇到“Could not find Developer Disk Image”这样的错误,这通常是由于Xcode缺少对应iOS版本的SDK...因此,这个“ios13.3真机开发包”是iOS开发过程中不可或缺的工具之一。
**iOS 10.2 开发包详解** iOS 10.2 是苹果公司为iPhone、iPad和iPod touch推出的操作系统版本,它在iOS 10的基础上进行了诸多更新和优化,旨在提供更好的用户体验和增强设备的功能性。这个开发包包含了针对开发者...
Windows 下 iOS 开发环境搭建及基本开发操作 Windows 下 iOS 开发环境搭建是 iOS 开发的必备步骤,而基本开发操作是新手必须掌握的技能。本文将指导读者如何在 Windows 下搭建 iOS 开发环境,并学习基本的开发操作...
iOS7应用开发基础是针对苹果公司的移动操作系统iOS 7版本和集成开发环境Xcode 5的软件...通过这本书,开发者可以构建在iOS 7上运行的高质量、适应性强的应用程序,并为在后续iOS版本上进行升级和维护打下坚实的基础。
在iOS应用开发过程中,开发者时常会遇到“Could not find Developer Disk Image”这样的错误,这个问题通常是由于Xcode中的SDK(Software Development Kit)版本不匹配引起的。当用户的iOS设备系统升级到新版本,...
iOS 支付控件 SDK 升级新控件 3.1.1 改造指南是为了帮助已经接入过 iOS 版银联支付控件 SDK 的商户升级到新版本。下面是该升级指南的重要知识点总结: 一、SDK 版本升级 * 根据商户选择的 SDK 版本,将 sdk/inc ...
注意:升级到iOS 16后需打开开发者模式否则Xcode会报错“Failed to prepare device for development”,一直转圈不能调试。入口为:设置-隐私与安全性 安全性-开发者模式打开,打开后会重启手机,之后再调试就可以了...
例如,可以通过App Store的帮助文章、指南、API参考、QuickHelp以及示例代码来深入理解iOS开发的各个方面。开发者也可以通过教程来学习如何使用Xcode进行用户界面设计、游戏开发、数据管理以及多媒体处理。 总的来...
在VMware上搭建iOS开发环境的总体方案是使用64位Windows 7作为宿主机,通过VMware 9安装Mac OS X Mavericks虚拟机。这个环境将用于安装和运行Xcode,从而进行iOS应用的开发和打包发布。Swift编程语言的使用也包含在...