ios9Tips

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://

二、iOS9新特性:更灵活的后台定位

#import "ViewController.h"
#import "WGS84TOGCJ02.h"
#import "AFNetworking.h"
#import 

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 *)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:第一次启动程序后,系统将只请求⓵的权限,⓶的权限系统不会请求,只会在下一次启动应用时请求⓶
    }
    //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 无法匹配而导致下载失败的情况*

  1. iOS9以后,企业级分发ipa包将遭到与Mac上dmg安装包一样的待遇:默认不能安装,也不再出现“信任按钮”
  2. iOS9以后,企业分发时可能存在:下载的ipa包与网页两者的 bundle ID 无法匹配而导致下载失败的情况
  3. 企业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 
#import 


@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 

@interface PersonModel : NSObject

@property(nonatomic,copy) NSString *pName,*pId,*pImageName;

@end

ViewController:
#import 

@interface ViewController : UIViewController

-(void)showDetailViewControllerWithPId:(NSString *)pId;

@end


#import "ViewController.h"
#import "PersonModel.h"
#import "DetailViewController.h"
#import 
#import 


@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 *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 

@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,9)