我的GitHub
0%

推送嘛,集成或更新的时候肯定要各种测试,但又不可能给所有线上用户集体推送。
这时候这个测试需求就暴露出来了。
其实非常简单

###不过测试前,需要确定你的客户端能和个推平台放置的p12对应的上。
####如何看能否对应的上?
#####获取token
token绑定
打开xcode,搜索这个方法:

1
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken

搜到了是不是,接下来运行起来,得到这个token,复制一下。
#####打开个推官网
个推官网
#####打开应用配置,拉到最下面
测试一下
#####点击测试一下
然后把你刚刚获得的token复制上去,监测一下看是否成功。如果成功会弹出提示,并且对应的设备会收到一条测试推送消息(走的是苹果的apns通道)

###如果是不成功,那么我猜大概率是证书环境没匹配上,你放的是生产环境但你客户端是开发环境,这时候怎么办?也有办法,看下面这篇。

好了好了,上面的准备工作已经做好,进入正题
返回注册的cid

1
- (void)GeTuiSdkDidRegisterClient:(NSString *)clientId

这里拿到cid
然后,新建一个txt文件(mac端的同学,还是拜托windows的同学新建给你吧,别问我为什么。。。因为字符集不一样啊我日)

然后把cid放到txt文件里
像这样

打开个推官网,创建推送
把你的txt文件放上去

阅读全文 »

因为公司业务关系,希望在iOS在后台的时候能存活去做某些业务操作(其实就是流氓的后台保活=。=)
最早是使用后台定位实现的,信心满满的去上架,结果被苹果爸爸锤的头都破了。连续被拒3次,申请打电话苹果代表也锤了我一顿,申请苹果审核申诉又被锤了一顿。想一想我真是头铁啊emmm
闲话不多说,开始这篇博客的主题
我们的业务其实很简单,就是后台有业务数据来了,客户端哪怕在后台也要处理。

#苹果的保活无非三种方式
###1.后台播放无声音乐
###2.后台定位
###3.推送唤醒

##播放音乐
直接被我pass了,我觉得机审都会把我拒了。。。压根不考虑

##后台定位
正好我们的业务吐过硬扯还能和业务办公场所扯上关系,头铁就是上!通过后台定位让app保活,成功实现了。不过被🍎爸爸锤了。方案二,pass

##推送唤醒
那这时候,就剩这最后一种方式了。
推送分两种,本地推送和远程推送。
####本地推送
本地推送其实类似于一个定时器,到了触发的时候就会触发。一般有很多用来做闹钟或者提示用户签到一类的场景。不过根据我的测试,当应用在后台三分钟后,被挂起以后这个本地推送就不再继续推送了。(也不知道是不是我的测试问题,反正我是没能保活成功。)

####静默推送(不用voip,有的个推运营人员是非技术,回答让你搞voip,别被忽悠了)
静默推送其实是远程推送的一种。在个推中,都属于透传消息(建议看一下这一篇)个推:什么是普通推送?什么是透传消息?
相信在看我这个前老哥们肯定也搜过其他的博客文章
无一例外肯定提到了:

1
2
3
4
5
6
{
"aps": {
"content-available": 1,
"sound": "com.gexin.ios.silence"
}
}

这个东西,不过万一有没搜到的呢emmm我还是再说一遍
content-available属性。0是非静默,1是静默
个推中,无声,sound属性:com.gexin.ios.silence(其他的推送是不是如此我就不太清楚了)
###alert,body切记不要写东西,要传值往最后看
###接下来是傻瓜式教程
###准备工作
####1.推送证书准备好
这部分太多教程了,随便搜一个搞一下就差不多了,不上图了。记得导出的p12要放到个推平台哟。
####2.打开开关
推送开关

允许后台推送

####3.代码部分
静默推送回调
就在这个里面了,当被挂起的时候,收到推送,执行这里的代码,唤醒app30s左右的时间,让你尽情的做自己想做的事😝。
我把生命周期写在下面,也好复制粘贴去搜索,我是不是很体贴=。=

阅读全文 »

写的都是自己的一些感受=。=可能都是野路子=。=大牛就别看了=。=
代码:https://github.com/XingXiaoWu/mobx-/tree/master
先声明一句话:

#MobX 会对在追踪函数执行过程中读取现存的可观察属性做出反应。
以下所有内容基于rn0.56版本以前(即低于0.56),语法是ES6语法(可能有ES7,反正我是分不清=。=)
#一、引入Mobx
首先,安装yarn(别问我为什么不用npm)

##1.在项目根目录下输入
yarn add mobx mobx-react
此命令作用:引入mobx和mobx-react
##2.在项目根目录下继续输入(看清楚你的版本选择命令)

1
2
3
4
5
6
7
//这是0.57以下的
yarn add babel-plugin-transform-decorators-legacy babel-preset-react-native-stage-0
//这是0.57及以上的
yarn add @babel/core --dev
yarn add @babel/plugin-proposal-decorators --dev
yarn add @babel/plugin-transform-runtime --dev
yarn add @babel/runtime --dev

此命令作用:能够使用@标签

##3.用IDE打开项目
打开.babelrc文件
修改为(看清楚版本)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//0.57以下
{
"presets": ["react-native"],
"plugins": ["transform-decorators-legacy"]
}

//0.57及以上
{
"presets": ["module:metro-react-native-babel-preset"],
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/transform-runtime", {
"helpers": true,
"regenerator": false
}]
]
}

至此,如果没报错,环境配置完毕,可以正常使用mobx了
#二、mobx的常用标签
##1.定义observable
observable,顾名思义,可以被观察的,即被观察者
一般用于数据,几乎所有数据都可以被观察,无论是对象、数组、类。 循环数据结构、引用,都可以被观察,只要给他们打上@ observable的标签,他们都可以被观察。

我写了三种导出模式,相互的优劣和使用方式还需要琢磨
#ThingModel=>static定义的,导出的是ThingModel这个class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import {observable} from "mobx"

export class ThingModel {
@observable
static thing = "我是数据1"

@action
static setThing(value) {
ThingModel.thing = value
}
}

//使用
ThingModel.thing
ThingModel. setThing(xxx)
阅读全文 »

角色权限判断,这应该是大部分应用都有的功能,那么如何进行权限判断?

这里说下我们用的思路,可能会比较low,各位看官别笑。

打个比方,我现在有A,B,C,D四个功能。要对某个账户是否具有这些功能权限做判断。
我们有个list,里面存放着这个账户所有的权限。(list从哪来?当然是后台请求回来啊!)
A功能对应的权限是=>0,以此类推B=>1,C=>2,D=>3这样子。

那么如果我有一个账号拥有所有权限,list就是【0,1,2,3】,如果没有B权限就是【0,2,3,4】这样子,相信大家都应该明白我要表达的意思了。

那么要判断是否拥有某个权限,这时候肯定就会说,遍历一下list不就知道了?那如果权限比较多呢?而且你不觉得这样更low么=。=
所以这个时候,我们可以使用位运算打成这个目的:

先说我们用来判断的方法:

1
2
3
4
//判断是否存在权限,permission是所有权限展示中的某个权限,只有一个&,不是两个,别搞错了
hasPermission(permission) {
return permission & GlobalValue.authValue
}

再定义一个包含了所有权限的声明

1
2
3
4
5
6
7
8
//所有权限列表展示
export let Permission = {
A: Math.pow(2, 0), //A功能
B: Math.pow(2, 1), //B功能
C: Math.pow(2, 2), //C功能
D: Math.pow(2, 3), //D功能
...
}

在我们和后台请求拿到某个角色所包含的list的时候,对list做如下操作

阅读全文 »

背景:rn好多前端或者安卓转的旁友对iOS的打包不太熟悉,所以写个图文教程版,目前我对应的版本是0.4+版本和0.5+版本(反正我也是随便写的,参考为主)
PS:账号的申请还有各种证书的申请我就不多提了,自己百度查下。。
###1.打开项目中iOS文件夹,新建一个名为bundle的文件夹
如图
###2.打开终端,cd到整个项目根目录
如图中的catten目录
###3.输入命令行,版本不同命令不一样哟,注意看
区分标准是你是否存在index.ios.js,存在肯定是老版本,用下面这个命令

1
react-native bundle --entry-file index.ios.js --bundle-output ./ios/bundle/index.ios.jsbundle --platform ios --assets-dest ./ios/bundle --dev false

如果你是index.js,那就是新版本啦,用下面这个命令

1
react-native bundle --entry-file index.js --bundle-output ./ios/bundle/index.jsbundle --platform ios --assets-dest ./ios/bundle --dev false

###4.把上面命令输入终端并按下回车
等待时间可能要一会
如图
###4.补,差点漏了一点注意事项
可能有的旁友项目中有webview承载html页面
这时候你命令行打出来的包可能就要注意一下了
如下图,我这是bundle文件夹下assets文件夹里的资源文件夹
如图
实际上我这个html有多少文件呢
实际这么多
你会发现少了东西,这时候怎么办,很简单,去你项目中把相关缺少的文件复制到assets里就行

###5.打开Xcode项目
直接拖过去
直接拖过去
记得选下面这个哟,蓝色的物理文件夹
选下面这个
选完长这样
选完长这样
###6.这时候打开appdelegate(我改了东西,里面代码是一样的,不过我的位置不一样,你们看下就知道了)
把jsCodeLocation的来源换成你打的包的来源(其他人怎么打包、调试、开发我不清楚,反正我是这么做的,而且已经上架两个项目了,我确定是可用的)
js的地址

还是老样子,index.ios.js填

1
jsCodeLocation = [NSURL URLWithString:[[NSBundle mainBundle] pathForResource:@"index.ios.jsbundle" ofType:nil]];

index.js填

1
jsCodeLocation = [NSURL URLWithString:[[NSBundle mainBundle] pathForResource:@"index.jsbundle" ofType:nil]];
阅读全文 »

其实iPhone的指纹识别有很多教程,但其实有两套策略,而且好多都只写了一套,而且有的你会发现,错误码压根就试不出来啊,还以为苹果给的错误码都是瞎扯淡的。

首先,需要导入苹果的安全策略库LocalAuthentication。
导入库
这个库提供了指纹识别这一安全策略库公开发者们使用。
对于开发者来说,只需要调用几个很简单的方法便可以进行指纹识别了。至于是如何识别,这些苹果并没有公开,开发者只能获取成功或失败状态以及message。
###一.判断设备是否支持指纹识别

1
2
3
4
5
LAContext *context = [[LAContext alloc] init];
NSError *error = nil;

// 判断设置是否支持指纹识别(iPhone5s+、iOS8+支持)
BOOL support = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error];

###但是注意了,这里的support如果为NO并不代表设备就不支持指纹识别了。
因为当出现:
1.设备支持TouchID但TouchId被锁时:LAErrorTouchIDLockout
2.设备支持TouchID但没有设置密码:LAErrorPasscodeNotSet(但实际上会走LAErrorTouchIDNotEnrolled)
3.设备支持TouchID,设置了密码,但没有设置TouchID:LAErrorTouchIDNotEnrolled
这几种情况统统都会报Support为NO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
if (support) {
return AllRight;
}else{
switch (error.code) {
// 没有设置指纹(没有设置密码也会走到这),但是支持指纹识别
case LAErrorTouchIDNotEnrolled:{
return TouchOK;
}
break;
// 理论上是没有设置密码,至今没有尝试出什么情况下走这个,希望有试出来场景的兄弟告知一下我
case LAErrorPasscodeNotSet:{
return TouchOK;
}
break;
// 在使用touchID的场景中,错误太多次(根据策略不同为5次到6次)而导致touchID被锁不可用
case LAErrorTouchIDLockout:{
NSLog(@"密码封锁");
return TouchOK;
}
// 设备不支持指纹识别
default:{
return AllError;
}
break;
}
}
}

###二.指纹识别如何唤起(其实是安全策略的唤起)
其实整个LocalAuthentication库是一个安全策略库,我们所说的指纹只是安全策略的一种,主要为生物技术的使用。这种策略有非常多的种类,现在常用的指纹,iPhone X的face ID,没准以后还会有虹膜等更多的安全策略出现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

[authenticationContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:@"通过Home键验证已有手机指纹" reply:^(BOOL success, NSError * _Nullable error) {
// 指纹识别错误调用分为以下情况,
// 点击取消按钮 : domain = com.apple.LocalAuthentication code = -2
// 点击输入密码按钮 : domain = com.apple.LocalAuthentication code = -3
// 输入密码重新进入指纹系统 : domain = com.apple.LocalAuthentication code = -8
// 指纹三次错误 : domain = com.apple.LocalAuthentication code = -1
// 指纹验证成功 : error = nil
if (error) {
switch (error.code) {
// 指纹识别3次失败进入这里
case LAErrorAuthenticationFailed:
NSLog(@"验证失败");
break;
// 指纹识别时,点击取消
case LAErrorUserCancel:
NSLog(@"点击取消按钮");
break;
// 指纹识别时,点击输入密码按钮
case LAErrorUserFallback:
NSLog(@"点击输入密码按钮");
break;
// 没有在设备上设置密码(我没试出来这一项,有试出来的兄弟评论告诉我一下)
case LAErrorPasscodeNotSet:
NSLog(@"没有在设备上设置密码");
break;
// 设备上TouchID不可用,例如未打开(我没试出来这一项,有试出来的兄弟评论告诉我一下)
case LAErrorTouchIDNotAvailable:
NSLog(@"设备不支持TouchID");
break;
// 没有设置TouchID
case LAErrorTouchIDNotEnrolled:
NSLog(@"设备没有注册TouchID");
break;
// 设备TouchID被锁,且只会在iOS9以上设备出现
case LAErrorTouchIDLockout:
NSLog(@"TouchID被锁");
break;
// 由于不可抗拒力,应用进入后台(其实很简单,你写两个测试demo,在一个启动指纹时开启另一个项目,你的指纹项目就会因为不可抗力进入后台,这时候就会走到这)
case LAErrorSystemCancel:
NSLog(@"由于系统阻止,转入后台");
break;
default:
// 直接写失败吧,也没别的原因了,进入这里都很难了
break;
}
return ;
}
else{
NSLog(@"识别成功");
}
}];

一般用上面写的这段代码就行了
接下来咱们说说策略

1
2
3
4
5
6
/// @param policy 策略
/// @param localizedReason 提示语
/// @param reply Reply block 回调的代码块
[myContext evaluatePolicy:<#(LAPolicy)#> localizedReason:<#(nonnull NSString *)#> reply:^(BOOL success, NSError * _Nullable error) {

}];

咱们来谈谈policy,这玩意有两个枚举值

阅读全文 »

其实集成个推的途中一直很坎坷,不知道到底他们的推送是个什么东西,透传,普通推送,什么情况才是正常的。这里写一下,没有代码,只是阐述正确情况下推送通道应该产生的效果。

ios的消息是分两部分的 一部分是走apn的通知栏消息 另一部分是走个推通道的透传消息 这两部分是服务端推送代码里面集成个推的后端同事会设定好的 分别是两个不同的方法 如果消息下发的时候 你客户端是在后台的(也就是客户端是离线)那么会收到apn的通知 透传消息就进离线了 只有你下次在线的时候 (也就是下次应用到前台的时候)才会下发下来 如果下发的时候应用是在前台的 那这样的话客户端就直接收到透传消息了

应用退到后台,或者杀进程,cid离线才会推送苹果apn,客户端才会收到apn通知栏提醒的,cid在线直接走个推通道,客户端收到个推透传消息,如果需要展示通知栏需要自己客户端定义处理解析个推透传消息

阅读全文 »

背景:公司的老项目,使用的是uiwebview,我也没用过,第一次做混合开发,所以想直接使用wkwebview去替换当前的uiwebview,毕竟性能上提升了4倍,还有很多乱七八糟的优化等,废话不多说。

###1.首先wkwebview有三个代理方法WKUIDelegate,WKScriptMessageHandler,WKNavigationDelegate

###2.wkwebview创建的时候可以写配置
有一个属性的集合,叫WKWebViewConfiguration
其中还有一些首选项的配置WKPreferences
例如我是这么创建的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//    wkwebview属性的集合
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
// webview一些首选项的配置
WKPreferences *preferences = [[WKPreferences alloc]init];
// 在没有用户交互的情况下,是否允许js打开窗口,macOS默认是yes,iOS默认是no
preferences.javaScriptCanOpenWindowsAutomatically = YES;
// webview的最小字体大小
// preferences.minimumFontSize = 40.0;

configuration.preferences = preferences;

self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
self.webView.UIDelegate = self;
self.webView.navigationDelegate = self;
[self.view addSubview:self.webView];

###3.加载网页,本地加载和加载服务器网页
######1.加载本地网页
将html与css和js拖进项目,使用蓝色物理文件夹放入。
加载本地网页,使用

1
[self.webView loadFileURL:htmlPathUrl allowingReadAccessToURL:folderPathUrl];

其中htmlPathUrl代表html的路径
其中folderPathUrl代表存放js与css的路径
只有这样才能加载完整的网页,否则你可能加载不出css样式和js方法。
例如:

1
2
3
4
5
6
7
8
9
10
#pragma mark 加载url展示页面
//加载本地网页
-(void)loadUrl{
NSString *folderPath = [[NSBundle mainBundle] pathForResource:@"sdk" ofType:@""];
NSURL *folderPathUrl = [NSURL fileURLWithPath:folderPath];

NSString *htmlPath = [folderPath stringByAppendingString:@"/h5/index.html"];
NSURL *htmlPathUrl = [NSURL fileURLWithPath:htmlPath];
[self.webView loadFileURL:htmlPathUrl allowingReadAccessToURL:folderPathUrl];
}

当然,如果网页有ajax请求,注意跨域问题。试试jsonp。这里就提一下,略过。

######2.加载服务器网页
这就没什么好说的了。。直接加载就行。
例如:

阅读全文 »

其实就是用workspace把两个项目连起来
凉白开记录了,懒得写了。。
地址:http://www.jianshu.com/p/6c9b380cfe5c?utm_source=desktop&utm_medium=timeline
如果修改了文件位置或者啥的,报头文件找不到,去示例代码工程把

49F27CAE-95C4-4F32-80EC-543ADFD05B49.png
把sdk的link删掉再加上,重新运行就好了

//—————以下是凉白开总结的——————//

自从上次写完SDK并接入别人的项目中之后就一直没怎么管过这东西了,昨天一人突然问我调试SDK怎么弄,我说不能。(结果今天啪啪啪自己打脸了) 恩他解决了一个SDK不能调试的一个超级大问题,征得同意后我来记录一下,自己记录一下其次文后并附上他的简书地址。

1.新建workspace。

img

新建workspace

*2.新建文件夹,将*workspace,demo以及SDK**放入文件夹中。****

img

新建文件夹及放入

阅读全文 »

前人挖坑后人填。。。。
阐述一下背景:
 App更新迭代,需要跟后台做一个版本控制更新的开关。正常来说应该是将xcode的version与App Store的version对应,并通过该值判断开关是否打开。
 但是问题来了。。上一位iOS工程师一直是使用的build进行版本控制,本地xocde的version比App Store的version高出不少。
 先说一说Version。分为xcode的version和App Store的version。理论上应该两者一致。方便维护。

###xcode的version
可以通过

1
2
//  获得version号
NSString *versionCode =[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];

获取,获取的是info.plist文件中的
335E8913-16AA-4B95-B4CB-63B50AEF28F6.png

###App Store的version
则是通过调取接口:
https://itunes.apple.com/lookup?id=你的app的id号;
调用方式:post
返回结果:

1
2
3
NSArray *array = responseObject[@"results"];
NSDictionary *dict = [array lastObject];
NSLog(@"当前版本为:%@", dict[@"version"]);

代码如下:

1
2
3
4
5
6
7
8
9
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:@"https://itunes.apple.com/lookup?id=414478124" parameters:nil success:^(AFHTTPRequestOperation * _Nonnull operation, id _Nonnull responseObject) {
NSArray *array = responseObject[@"results"];
NSDictionary *dict = [array lastObject];
NSLog(@"当前版本为:%@", dict[@"version"]);
} failure:^(AFHTTPRequestOperation * _Nullable operation, NSError * _Nonnull error) {
NSLog @"请求失败";
}];
}

正常来说都是对外暴露的版本,xcode与App Store的版本理应一致。方便维护。

###build:
开发内部测试版本号,写多少都行,内部开发测试用的,写多少都行,最好是不要对外暴露。因为每次打包都需要往上+,所以用于版本控制效果不好。

阅读全文 »