ios9Tips
2015-12-09 15:26
651 查看
ios9变化挺多的,一哥们写的挺好,贴上地址。
https://github.com/ChenYilong/iOS9AdaptationTips
一、ios9网络适配_ATS(App Transport Security):改用更安全的HTTPS
一个符合 ATS 要求的 HTTPS,应该满足如下条件:
1、Transport Layer Security协议版本要求TLS1.2以上
2、服务的Ciphers配置要求支持Forward Secrecy等
3、证书签名算法符合ATS要求等
解决方案
方案一:立即让公司的服务端升级使用TLS 1.2,以解析相关数据
方案二:虽Apple不建议,但可通过在 Info.plist 中声明,倒退回不安全的网络请求依然能让App访问指定http,甚至任意的http
让ios9支持http的最暴力的方法:
在info.plist中设置:NSAppTransportSecurity( 类型Dictionary)->Allow Arbitrary Loads(类型Boolean):YES
在OS X EI Capitan系统的终端中通过nscurl命令来诊断检查你的HTTPS服务配置是否满足Apple的ATS要求:
https://github.com/ChenYilong/iOS9AdaptationTips
一、ios9网络适配_ATS(App Transport Security):改用更安全的HTTPS
一个符合 ATS 要求的 HTTPS,应该满足如下条件:
1、Transport Layer Security协议版本要求TLS1.2以上
2、服务的Ciphers配置要求支持Forward Secrecy等
3、证书签名算法符合ATS要求等
解决方案
方案一:立即让公司的服务端升级使用TLS 1.2,以解析相关数据
方案二:虽Apple不建议,但可通过在 Info.plist 中声明,倒退回不安全的网络请求依然能让App访问指定http,甚至任意的http
让ios9支持http的最暴力的方法:
在info.plist中设置:NSAppTransportSecurity( 类型Dictionary)->Allow Arbitrary Loads(类型Boolean):YES
在OS X EI Capitan系统的终端中通过nscurl命令来诊断检查你的HTTPS服务配置是否满足Apple的ATS要求:
$ nscurl --verbose --ats-diagnostics https://<your_server_domain>[/code]
二、iOS9新特性:更灵活的后台定位#import "ViewController.h" #import "WGS84TOGCJ02.h" #import "AFNetworking.h" #import <CoreLocation/CoreLocation.h> static const CLLocationDegrees EmptyLocation=-1000.0; @interface ViewController () <CLLocationManagerDelegate,UIAlertViewDelegate> @property(nonatomic,retain) CLLocationManager *kLocationmanager; @property(nonatomic,strong) CLLocation *kLocation; /*地理编码器*/ @property(nonatomic,retain) CLGeocoder *kGeocdoer; @end @implementation ViewController #pragma mark - ♻️ Life Cycle - (void)viewDidLoad { [super viewDidLoad]; [self uiConfig]; //判断当前定位服务是否可用 if (![CLLocationManager locationServicesEnabled]) { [self openGPSLocationTips]; } //KVO KVC监测self.kLocaton值的变化 [self addObserverOfLocation]; [self startLocationUser]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } //-(void)dealloc{ // //KVO反注册 // [self removeObserver:self forKeyPath:@"kLocation" context:nil]; //} #pragma mark - �� CLLocationManagerDelegate /*更新用户位置,会频繁调用*/ -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations{ CLLocation *location=[locations objectAtIndex:0]; //判断是不是属于国内范围 if (![WGS84TOGCJ02 isLocationOutOfChina:[location coordinate]]) { //转换后的coord // CLLocationCoordinate2D coordinate=[WGS84TOGCJ02 transformFromWGSToGCJ:[location coordinate]]; // self.kLocation=[[CLLocation alloc] initWithLatitude:coordinate.latitude longitude:coordinate.longitude]; self.kLocation=[[CLLocation alloc] initWithLatitude:location.coordinate.latitude longitude:location.coordinate.longitude]; } } /* 检测应用是否开启定位服务 */ -(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{ [manager stopUpdatingLocation]; NSLog(@"error=%@",error.localizedDescription); switch ([error code]) { case kCLErrorDenied: [self openGPSLocationTips]; break; case kCLErrorLocationUnknown: break; default: break; } } #pragma mark - �� UIAlertViewDelegate -(void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex{ if (buttonIndex==1) { NSURL *url=[NSURL URLWithString:@"prefs:root=LOCATION_SERVICES"]; if ([[UIApplication sharedApplication] canOpenURL:url]) { [[UIApplication sharedApplication] openURL:url]; } } } #pragma mark - �� Private Method -(void)uiConfig{ self.title=@"ios9 Location Background"; UINavigationBar *bar=self.navigationController.navigationBar; [bar setTintColor:[UIColor whiteColor]]; [bar setTitleTextAttributes:@{NSFontAttributeName:[UIFont boldSystemFontOfSize:18],NSForegroundColorAttributeName:[UIColor whiteColor]}]; [bar setBarTintColor:[UIColor colorWithRed:(51)/255.f green:(171)/255.f blue:(160)/255.f alpha:1.f]]; } -(void)addObserverOfLocation{ [self addObserver:self forKeyPath:@"kLocation" options:NSKeyValueObservingOptionNew context:nil]; } -(void)openGPSLocationTips{ UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"提示" message:@"当前定位服务不可用,您可以点击设置" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:@"设置", nil]; [alert show]; int delayInSeconds=2; dispatch_time_t when=dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds*NSEC_PER_SEC)); dispatch_after(when, dispatch_get_main_queue(), ^{ [alert dismissWithClickedButtonIndex:0 animated:YES]; }); } -(void)startLocationUser{ //1、实例化定位管理器 self.kLocationmanager=[[CLLocationManager alloc] init]; //2、设置代理 self.kLocationmanager.delegate=self; //3、定位精度 [self.kLocationmanager setDesiredAccuracy:kCLLocationAccuracyBest]; //4、请求用户权限:分为两种:⓵只在前台开启定位 ⓶在后台也可定位 if ([[[UIDevice currentDevice] systemVersion] floatValue]>=8.0) { //⓵只在前台开启定位 //[self.kLocationmanager requestWhenInUseAuthorization]; //⓶在后台也开启定位 [self.kLocationmanager requestAlwaysAuthorization]; //如果是上面的1、2这样的顺序,将导致bug:第一次启动程序后,系统将只请求⓵的权限,⓶的 103b2 权限系统不会请求,只会在下一次启动应用时请求⓶ } //5、iOS9新特性:将允许出现这种场景:同一app中多个location manager:一些只能在前台定位,另一些可在后台定位(并可随时禁止其后台定位)。 if ([[[UIDevice currentDevice] systemVersion] floatValue]>=9) { self.kLocationmanager.allowsBackgroundLocationUpdates=YES; //如果没有配置info.pist,程序会崩溃掉。有两种方法解决这个问题 //⓵在info.plist中配置 1、Required background modes(Arry)->Item0(string):App registers for location updates 2、NSLocationAlwaysUsageDescription(string)设置应用的名字 //⓶在对应 target 的 Capabilities -> Background Modes -> 开启 Location Updates } //更新用户位置 [self.kLocationmanager startUpdatingLocation]; } #pragma mark - Baidu Address -(void)getAddressWithLocation:(CLLocation *)location { NSString *str=@"http://api.map.baidu.com/geocoder?output=json&location=%f,%f&key=dc40f705157725fc98f1fee6a15b6e60"; NSString *urlStr=[NSString stringWithFormat:str,location.coordinate.latitude,location.coordinate.longitude]; AFHTTPRequestOperationManager *manager=[AFHTTPRequestOperationManager manager]; [manager GET:urlStr parameters:nil success:^(AFHTTPRequestOperation * _Nonnull operation, id _Nonnull responseObject) { NSDictionary *result=responseObject[@"result"]; NSLog(@"result=%@",result); NSString *detailInfo=[NSString stringWithFormat:@"%@",result[@"formatted_address"]]; UIAlertView *alertView=[[UIAlertView alloc] initWithTitle:result[@"city"] message:detailInfo delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alertView show]; } failure:^(AFHTTPRequestOperation * _Nonnull operation, NSError * _Nonnull error) { NSLog(@"error=%@",str); }]; } #pragma mark - AppleAddress -(void)getAddressInAppleAddressWithLocation:(CLLocation *)location{ //地址反编码调用的函数 CLGeocoder *coder=[[CLGeocoder alloc] init]; //这个方法会自动向Apple服务器发送异步请求 返回数据 [coder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) { //CLPlacemark 系统自带的地址信息的类 包含:国家,城市,省份等等信息 CLPlacemark *placeInfo=[placemarks objectAtIndex:0]; NSDictionary *addressDict=placeInfo.addressDictionary; NSString *title = [addressDict objectForKey:@"Name"]; NSString *subTitle = [addressDict objectForKey:@"FormattedAddressLines"][0]; NSLog(@"City=%@ Country=%@ FormattedAddressLines=%@ Name=%@ State=%@ Street=%@ SubLocality=%@ Thoroughfare=%@",addressDict[@"City"],addressDict[@"Country"],[addressDict[@"FormattedAddressLines"] objectAtIndex:0],addressDict[@"Name"],addressDict[@"State"],addressDict[@"Street"],addressDict[@"SubLocality"],addressDict[@"Thoroughfare"]); CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude); if(![self isCoordinateEmpty:coordinate]) { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:subTitle delegate:nil cancelButtonTitle:nil otherButtonTitles:nil]; [alert show]; int delayInSeconds = 1; dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(when, dispatch_get_main_queue(), ^{ [alert dismissWithClickedButtonIndex:0 animated:YES]; }); } }]; } - (BOOL)isCoordinateEmpty:(CLLocationCoordinate2D)regionCenter { BOOL isCoordinateEmpty = NO; if((regionCenter.latitude == EmptyLocation)&&(regionCenter.longitude == EmptyLocation)) { isCoordinateEmpty = YES; } return isCoordinateEmpty; } #pragma mark - �� Action Method /*KVC 当检测的kLocaton的值发生变化时会调用该方法*/ -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{ if ([keyPath isEqualToString:@"kLocation"]) { CLLocation *location=change[NSKeyValueChangeNewKey]; if ([self.kGeocdoer isGeocoding]) { [self.kGeocdoer cancelGeocode]; } // [self getAddressWithLocation:location]; [self getAddressInAppleAddressWithLocation:location]; } } #pragma mark - �� LazyLoad -(CLLocation *)kLocation{ if (_kLocation==nil) { _kLocation=[[CLLocation alloc] initWithLatitude:EmptyLocation longitude:EmptyLocation]; } return _kLocation; } -(CLGeocoder *)kGeocdoer{ if (_kGeocdoer==nil) { _kGeocdoer=[[CLGeocoder alloc] init]; } return _kGeocdoer; } @end
三、企业级分发
*有两处变化:
1、iOS9以后,企业级分发ipa包将遭到与Mac上dmg安装包一样的待遇:默认不能安装,也不再出现“信任按钮”
2、iOS9以后,企业分发时可能存在:下载的ipa包与网页两者的 bundle ID 无法匹配而导致下载失败的情况*
iOS9以后,企业级分发ipa包将遭到与Mac上dmg安装包一样的待遇:默认不能安装,也不再出现“信任按钮”
iOS9以后,企业分发时可能存在:下载的ipa包与网页两者的 bundle ID 无法匹配而导致下载失败的情况
企业APP安装之后,在网络情况为Wi-Fi环境的时候,可能会出现无法验证应用的情况。而此时,Wi-Fi网络是接入互联网的。如果多次验证不通过的话,我们需要切换到非Wi-Fi网络环境下才能解决这个问题
Q-A
Q:企业分发,企业版证书在iOS9上安装应用报 Ignore manifest download, already have bundleID: com.mycom.MyApp 只有我的手机无法安装,别人 iOS9 都可以安装
A:这并非 iOS9的问题,iOS8及以前的系统也会出现,和缓存有关系,请尝试关机重启手机,然后就可以安装了
四、ios9 URL Scheme适配_引入白名单概念
在iOS9中,如果使用 canOpenURL: 方法,该方法所涉及到的 URL scheme 必须在”Info.plist”中将它们列为白名单,否则不能使用。key叫做LSApplicationQueriesSchemes ,键值内容是
LSApplicationQueriesSchemes
urlscheme
urlscheme2
urlscheme3
urlscheme4
白名单上限是50个:
五、搜索API
分为两类首先都要导入CoreSpotlight.framework、MobileCoreServies.framework.
第一种:#import <CoreSpotlight/CoreSpotlight.h> #import <MobileCoreServices/MobileCoreServices.h> @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [self uiConfig]; [self spotLightIndexing]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } -(void)uiConfig{ self.view.backgroundColor=[UIColor whiteColor]; self.title=@"ios9ApiDemo"; } -(void)spotLightIndexing{ NSString *path=[[NSBundle mainBundle] pathForResource:@"data" ofType:@"plist"]; NSArray *plistArr=[[NSArray alloc] initWithContentsOfFile:path]; [plistArr enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull dict, NSUInteger idx, BOOL * _Nonnull stop) { CSSearchableItemAttributeSet *attributeSet = [[CSSearchableItemAttributeSet alloc] initWithItemContentType:(NSString *)kUTTypeImage]; // Set properties that describe attributes of the item such as title, description, and image. NSString *title = [dict objectForKey:@"title"]; attributeSet.title = title; attributeSet.contentDescription = [NSString stringWithFormat:@"%@,天空是什么颜色,听阴天说什么",title]; attributeSet.keywords = @[title]; // Create an attribute set for an item UIImage *image = [UIImage imageNamed:[dict objectForKey:@"image_name"]]; NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(image)]; attributeSet.thumbnailData = imageData; // Create a searchable item, specifying its ID, associated domain, and the attribute set you created earlier. CSSearchableItem *item; NSString *identifier = [NSString stringWithFormat:@"%@",attributeSet.title]; item = [[CSSearchableItem alloc] initWithUniqueIdentifier:identifier domainIdentifier:@"what's up" attributeSet:attributeSet]; // Index the item. [[CSSearchableIndex defaultSearchableIndex] indexSearchableItems:@[item] completionHandler: ^(NSError * __nullable error) { if (error) { NSLog(@"error=%@",error.localizedDescription); } }]; }]; } @end
第二种:数据模型: #import <Foundation/Foundation.h> @interface PersonModel : NSObject @property(nonatomic,copy) NSString *pName,*pId,*pImageName; @end ViewController: #import <UIKit/UIKit.h> @interface ViewController : UIViewController -(void)showDetailViewControllerWithPId:(NSString *)pId; @end #import "ViewController.h" #import "PersonModel.h" #import "DetailViewController.h" #import <CoreSpotlight/CoreSpotlight.h> #import <MobileCoreServices/MobileCoreServices.h> @interface ViewController () <UITableViewDataSource,UITableViewDelegate> @property(nonatomic,retain) NSMutableArray *kDataSource; @property(nonatomic,retain) UITableView *kTableView; @end @implementation ViewController #pragma mark - Life Cycle - (void)viewDidLoad { [super viewDidLoad]; [self loadDataResource]; [self uiConfig]; [self savePeopleToIndex]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } #pragma mark - Public Method -(void)showDetailViewControllerWithPId:(NSString *)pId{ for (PersonModel *person in self.kDataSource) { if ([person.pId isEqualToString:pId]) { DetailViewController *VC=[[DetailViewController alloc] initWithName:person.pName andImageName:person.pImageName]; [self.navigationController pushViewController:VC animated:YES]; } } } #pragma mark - UITableViewDataSource -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return [self.kDataSource count]; } -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ PersonModel *model=[self.kDataSource objectAtIndex:indexPath.row]; static NSString *cellId=@"cellId"; UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellId]; if (cell==nil) { cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId]; } cell.textLabel.text=model.pName; // cell.imageView.image=[UIImage imageNamed:model.pImageName]; return cell; } #pragma mark - UITableViewDelegate -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ [tableView deselectRowAtIndexPath:indexPath animated:YES]; PersonModel *person=[self.kDataSource objectAtIndex:indexPath.row]; DetailViewController *VC=[[DetailViewController alloc] initWithName:person.pName andImageName:person.pImageName]; [self.navigationController pushViewController:VC animated:YES]; } #pragma mark - Private Method -(void)uiConfig{ self.edgesForExtendedLayout=UIRectEdgeNone; self.view.backgroundColor=[UIColor whiteColor]; [self.view addSubview:self.kTableView]; } -(void)loadDataResource{ NSArray *personArr=@[@"ben",@"jane",@"pete",@"ray",@"tom"]; for (int i=0; i<[personArr count]; i++) { PersonModel *person=[[PersonModel alloc] init]; person.pId=[NSString stringWithFormat:@"%d",i+1]; person.pName=personArr[i]; person.pImageName=personArr[i]; [self.kDataSource addObject:person]; } } -(void)savePeopleToIndex{ NSMutableArray<CSSearchableItem *> *searchableItems=[NSMutableArray array]; for (PersonModel *person in self.kDataSource) { CSSearchableItemAttributeSet *attributedSet=[[CSSearchableItemAttributeSet alloc] initWithItemContentType:@"image"]; attributedSet.title=person.pName; attributedSet.contentDescription=[NSString stringWithFormat:@"你好,我是%@",person.pName]; UIImage *avatarImg=[UIImage imageNamed:person.pImageName]; attributedSet.thumbnailData=UIImagePNGRepresentation(avatarImg); CSSearchableItem *item=[[CSSearchableItem alloc] initWithUniqueIdentifier:person.pId domainIdentifier:@"com.company.demo" attributeSet:attributedSet]; [searchableItems addObject:item]; } [[CSSearchableIndex defaultSearchableIndex] indexSearchableItems:searchableItems completionHandler:^(NSError * _Nullable error) { if (error) { NSLog(@"error=%@",error.localizedDescription); } }]; } #pragma mark - Lazy Method -(UITableView *)kTableView{ if (_kTableView==nil) { _kTableView=[[UITableView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height-64) style:UITableViewStylePlain]; _kTableView.dataSource=self; _kTableView.delegate=self; _kTableView.tableFooterView=[UIView new]; _kTableView.rowHeight=44; } return _kTableView; } -(NSMutableArray *)kDataSource{ if (_kDataSource==nil) { _kDataSource=[[NSMutableArray alloc] init]; } return _kDataSource; } @end DetailViewController: #import <UIKit/UIKit.h> @interface DetailViewController : UIViewController -(instancetype)initWithName:(NSString *)nameStr andImageName:(NSString *)imgName; @end #import "DetailViewController.h" @interface DetailViewController () @property(nonatomic,copy) NSString *kName,*kImgName; @property(nonatomic,retain) UIImageView *kImgView; @end @implementation DetailViewController #pragma mark - Public Method -(instancetype)initWithName:(NSString *)nameStr andImageName:(NSString *)imgName{ self=[super init]; if (self) { self.kName=nameStr; self.kImgName=imgName; } return self; } #pragma mark - Life Cycle - (void)viewDidLoad { [super viewDidLoad]; [self uiConfig]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } #pragma mark - Private Method -(void)uiConfig{ self.view.backgroundColor=[UIColor whiteColor]; self.title=self.kName; [self.view addSubview:self.kImgView]; } #pragma mark - Lazy Method -(UIImageView *)kImgView{ if (_kImgView==nil) { _kImgView=[[UIImageView alloc] initWithFrame:CGRectMake((self.view.frame.size.width-100)/2, (self.view.frame.size.height-100)/2, 100, 100)]; _kImgView.image=[UIImage imageNamed:self.kImgName]; } return _kImgView; } @end AppDelegate: #import "AppDelegate.h" #import "ViewController.h" @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ViewController *VC=[[ViewController alloc] init]; UINavigationController *navi=[[UINavigationController alloc] initWithRootViewController:VC]; self.window.rootViewController=navi; return YES; } /*当spotlight搜索到的时候会调用这个方法*/ -(BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler{ NSString *frinedId=userActivity.userInfo[@"kCSSearchableItemActivityIdentifier"]; UINavigationController *navi=(UINavigationController*)self.window.rootViewController; [navi popToRootViewControllerAnimated:NO]; ViewController *VC=[navi.viewControllers firstObject]; [VC showDetailViewControllerWithPId:frinedId]; return YES; } @end
六、iOS国际化问题:当前设备语言字符串返回有变化NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSArray *allLanguage = [defaults objectForKey:@"AppleLanguages"]; NSString *currentLanguage = [allLanguage objectAtIndex:0]; NSLog(@"The current language is : %@", currentLanguage); iOS 9 之前:以上返回结果:语言字符串代码。例如:"zh-Hans" iOS 9:以上返回结果:语言字符串代码 + 地区代码。例如:"zh-Hans-US"
相关文章推荐
- iOS 伪亮度调节/控制
- 【iOS】tableView:cellForRowAtIndexPath: 方法未调用
- CABasicAnimation精讲
- ios基本框架
- iOS 图片相关
- 关于IOS9的本地通知方法
- nagios监控cfg模块
- 监控cacti/nagios/zabbix (二)nagios
- [Cordova] Plugin里使用iOS Framework
- iOS 关联对象的使用
- IOS观察者设计模式
- IOS使用Jenkins持续集成
- iOS联网和授权问题
- iOS之旅--封装NavigationController
- 用基础动画实现iOS控件循环旋转
- iOS Tips
- 2015-12-IOS 获取最新设备型号方法
- IOS_多线程编程4 - GCD
- iOS开发-工具:Xcode7升级之后插件无法使用与不小心点击Skipbundle的解决办法
- iOS 中二维码扫描