您的位置:首页 > 移动开发 > IOS开发

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要求:
$ 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"
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: