HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

关于unipush的一点使用心得

unipush

社区不行,自己研究了很久,先记录一下。
关于如何触发unipush的receive事件,之前有看见好几年的帖子都没人回复。头疼。
首先后端在使用代码测试时,虽然使用了透传的代码,但是app接收到的还是通知,而不是透传,透传和通知是两回事儿。
在app进程未被杀死时,也就是app在线的情况下,payload的数据格式不是按照标准的格式才会触发receive事件。
标准payload事件 String payload=“{title:xxxx,content:yyyy,payload:{{title:xxxx,content:yyyy,payload:{id:1000,id2:1002,id3:1003}}}}”
具体现在也还没有试过这样的标准的事件是不是不能触发receive,我看社区别人说的都不行。
至于什么是不标准的格式呢?这样的就行{"UUID":"androidPushMsg232","title":"测试","content":"测试离线ddd","payload":"测试离线ddd"},数据格式不标准不是指数据格式错误,而是自定义数据格式,只要不同于标准的数据格式就行了。

另外比较重要的是,如果你推送消息,在app打开的时候,通知栏如果有信息,那这就不是透传,而是通知。
在app进程被杀死后,透传就会直接到通知栏,并且只能触发click事件。
透传在app打开时,是不会发送到通知栏的,而是直接发送数据,不会进行页面上的一些展示。

另外我们后端的大佬用文档说的透传方法,在服务端发起的透传,在app上也是通知,这个坑导致一个上午都以为发起的通知是透传,也是为什么不触发receive的原因

继续阅读 »

社区不行,自己研究了很久,先记录一下。
关于如何触发unipush的receive事件,之前有看见好几年的帖子都没人回复。头疼。
首先后端在使用代码测试时,虽然使用了透传的代码,但是app接收到的还是通知,而不是透传,透传和通知是两回事儿。
在app进程未被杀死时,也就是app在线的情况下,payload的数据格式不是按照标准的格式才会触发receive事件。
标准payload事件 String payload=“{title:xxxx,content:yyyy,payload:{{title:xxxx,content:yyyy,payload:{id:1000,id2:1002,id3:1003}}}}”
具体现在也还没有试过这样的标准的事件是不是不能触发receive,我看社区别人说的都不行。
至于什么是不标准的格式呢?这样的就行{"UUID":"androidPushMsg232","title":"测试","content":"测试离线ddd","payload":"测试离线ddd"},数据格式不标准不是指数据格式错误,而是自定义数据格式,只要不同于标准的数据格式就行了。

另外比较重要的是,如果你推送消息,在app打开的时候,通知栏如果有信息,那这就不是透传,而是通知。
在app进程被杀死后,透传就会直接到通知栏,并且只能触发click事件。
透传在app打开时,是不会发送到通知栏的,而是直接发送数据,不会进行页面上的一些展示。

另外我们后端的大佬用文档说的透传方法,在服务端发起的透传,在app上也是通知,这个坑导致一个上午都以为发起的通知是透传,也是为什么不触发receive的原因

收起阅读 »

团队全职长期接APP,中后台开发

5+App开发 混合开发 外包

开发经验:5年。
UNI经验:从UNI发布至今一直使用,前以及开发了10多个UNIAPP项目
UNI项目其中包含:淘宝客项目、教育类项目、区块链项目、商城项目等其他小中大型APP

说明:团队承接,长期接外包,公司个人均可
技术方向:uniappvuepythonphpflutternodejsreactgolang

联系方式q:1910563900
微信:BGF-FF

继续阅读 »

开发经验:5年。
UNI经验:从UNI发布至今一直使用,前以及开发了10多个UNIAPP项目
UNI项目其中包含:淘宝客项目、教育类项目、区块链项目、商城项目等其他小中大型APP

说明:团队承接,长期接外包,公司个人均可
技术方向:uniappvuepythonphpflutternodejsreactgolang

联系方式q:1910563900
微信:BGF-FF

收起阅读 »

app兼h5项目开发经验记录

uniapp

App
1、ios 软键盘弹收起后,需将页面归位,代码如下:

        uni.pageScrollTo({  
          scrollTop: 0,  
          duration: 300  
    });  

2、安卓输入框被软键盘遮挡:
如果,输入框在scroll-view里:
1)输入框focus时,改变scroll-view 的scroll-top 属性值【bug:h5生效,app里不生效】

3、行内样式不能使用upx 单位,需要的话,可使用 uni.upx2px(**) 转换

         computed: {  
            pageTop(){  
            return uni.upx2px(this.top);  
        }  
      }  

4、uniapp 官方bug: uni.getImageInfo() app端 网络图片无法加载
社区问题: https://ask.dcloud.net.cn/question/71511

5、uniapp 官方bug:measureText() 目前只支持小程序和h5
https://ask.dcloud.net.cn/question/70374

继续阅读 »

App
1、ios 软键盘弹收起后,需将页面归位,代码如下:

        uni.pageScrollTo({  
          scrollTop: 0,  
          duration: 300  
    });  

2、安卓输入框被软键盘遮挡:
如果,输入框在scroll-view里:
1)输入框focus时,改变scroll-view 的scroll-top 属性值【bug:h5生效,app里不生效】

3、行内样式不能使用upx 单位,需要的话,可使用 uni.upx2px(**) 转换

         computed: {  
            pageTop(){  
            return uni.upx2px(this.top);  
        }  
      }  

4、uniapp 官方bug: uni.getImageInfo() app端 网络图片无法加载
社区问题: https://ask.dcloud.net.cn/question/71511

5、uniapp 官方bug:measureText() 目前只支持小程序和h5
https://ask.dcloud.net.cn/question/70374

收起阅读 »

教你如何优雅的使用uni.xxxxx 异步接口

uniapp 教程 异步方法

//配置 main.js  
//-------------------------main.js  
const $uni = new Proxy(uni, {//Proxy代理  
    get: (target, prop) => {  
        return (options) => {  
            return new Promise((resolve, reject) => {  
                target[prop]({  
                    success: resolve,  
                    fail: reject,  
                    ...options  
                })  
            })  
        }  
    },  
    set: (target, prop, value) => {  
        target[prop] = value  
    }  
})  
Vue.prototype.$uni = $uni // 挂载到vue上    可以直接this.$uni.xxxx()  

//如何使用  
//----------------------------.vue文件  
const userInfo= await this.$uni.getUserInfo()  
console.log(userInfo)  

const res=await  this.uni.getLocation({type: 'wgs84'})  
console.log('当前位置的经度:' + res.longitude);  
console.log('当前位置的纬度:' + res.latitude);  

//or  
try {  
const res=await  this.$uni.getLocation({type: 'wgs84'})  
console.log('当前位置的经度:' + res.longitude);  
console.log('当前位置的纬度:' + res.latitude);  
} catch (error) {  
    //todo    相当于 fail事件  
}  

//----------------.js文件  

import Vue from 'vue'  

const $uni= Vue.prototype.$uni  

const userInfo= await $uni.getLocation({type: 'wgs84'})  
console.log(userInfo)  

const res= await $uni.getLocation({type: 'wgs84'})  
console.log('当前位置的经度:' + res.longitude);  
console.log('当前位置的纬度:' + res.latitude);  
//or  
try {  
const res=await  $uni.getLocation({type: 'wgs84'})  
console.log('当前位置的经度:' + res.longitude);  
console.log('当前位置的纬度:' + res.latitude);  
} catch (error) {  
    //todo     相当于 fail事件  
}  

手动滑稽,有赞吗

继续阅读 »

//配置 main.js  
//-------------------------main.js  
const $uni = new Proxy(uni, {//Proxy代理  
    get: (target, prop) => {  
        return (options) => {  
            return new Promise((resolve, reject) => {  
                target[prop]({  
                    success: resolve,  
                    fail: reject,  
                    ...options  
                })  
            })  
        }  
    },  
    set: (target, prop, value) => {  
        target[prop] = value  
    }  
})  
Vue.prototype.$uni = $uni // 挂载到vue上    可以直接this.$uni.xxxx()  

//如何使用  
//----------------------------.vue文件  
const userInfo= await this.$uni.getUserInfo()  
console.log(userInfo)  

const res=await  this.uni.getLocation({type: 'wgs84'})  
console.log('当前位置的经度:' + res.longitude);  
console.log('当前位置的纬度:' + res.latitude);  

//or  
try {  
const res=await  this.$uni.getLocation({type: 'wgs84'})  
console.log('当前位置的经度:' + res.longitude);  
console.log('当前位置的纬度:' + res.latitude);  
} catch (error) {  
    //todo    相当于 fail事件  
}  

//----------------.js文件  

import Vue from 'vue'  

const $uni= Vue.prototype.$uni  

const userInfo= await $uni.getLocation({type: 'wgs84'})  
console.log(userInfo)  

const res= await $uni.getLocation({type: 'wgs84'})  
console.log('当前位置的经度:' + res.longitude);  
console.log('当前位置的纬度:' + res.latitude);  
//or  
try {  
const res=await  $uni.getLocation({type: 'wgs84'})  
console.log('当前位置的经度:' + res.longitude);  
console.log('当前位置的纬度:' + res.latitude);  
} catch (error) {  
    //todo     相当于 fail事件  
}  

手动滑稽,有赞吗

收起阅读 »

uni怎么写全局背景颜色。

背景图

我在公共css里面写了渐变背景
body {
width: 100%;
height: 100%;
background: linear-gradient(red, yellow);
}
可是当页面过长,或者超过一屏幕的时候,背景颜色就repeat-y了,不是整个背景就一个渐变。
请问我应该把这个渐变背景样式下载哪个选择器上面啊。求解答

继续阅读 »

我在公共css里面写了渐变背景
body {
width: 100%;
height: 100%;
background: linear-gradient(red, yellow);
}
可是当页面过长,或者超过一屏幕的时候,背景颜色就repeat-y了,不是整个背景就一个渐变。
请问我应该把这个渐变背景样式下载哪个选择器上面啊。求解答

收起阅读 »

uni-app接入android原生环信客服(非IM)

环信 客服

介绍

  • 原因:最近用uni-app需要集成环信客服,直接webview加载环信H5版本的客服,在android上有问题,ios上能用。所以就接入了环信android原生的客服功能。

  • 基本思路:综合时间、成本、效果等方面的考虑,决定把客服所有的功能由原生activity客服页面去实现,uni-app端只负责启动这个原生activity客服页面。

  • 实现效果:基于环信客服访客端demo集成,支持文字、语音、表情、拍照、图片、视频、文件,进入客服页面时可设置访客信息、发送订单或者轨迹消息。

效果图

http://docs.easemob.com/cs/start

集成步骤

github地址(给个star吧)

继续阅读 »

介绍

  • 原因:最近用uni-app需要集成环信客服,直接webview加载环信H5版本的客服,在android上有问题,ios上能用。所以就接入了环信android原生的客服功能。

  • 基本思路:综合时间、成本、效果等方面的考虑,决定把客服所有的功能由原生activity客服页面去实现,uni-app端只负责启动这个原生activity客服页面。

  • 实现效果:基于环信客服访客端demo集成,支持文字、语音、表情、拍照、图片、视频、文件,进入客服页面时可设置访客信息、发送订单或者轨迹消息。

效果图

http://docs.easemob.com/cs/start

集成步骤

github地址(给个star吧)

收起阅读 »

Loading图总有一款适合你的

loading uniapp

总有一款适合你的Loading

github地址,喜欢的可以star下哦

插件预览图

使用教程

1.插件代码拷贝

  • 下载后把components目录下countUp.vue文件拷贝到自己项目目录下

2.插件全局配置

  • 在项目里main.js中配置如下代码
import loading from './components/loading/loading.vue'  

Vue.component('loading',loading)  

3.插件使用

  • vue页面使用
<template>  
    <view>  
        <loading ></loading>  
    </view>  
</template>  

<script>  
</script>  

<style>  
</style>  

还收集了一些其他的Loding

Loaders.css

css-loaders

SVG-Loaders

暂时收集了这些,我相信能满足大部分人的需求了

兼容性

uni-app项目中使用都兼容

继续阅读 »

总有一款适合你的Loading

github地址,喜欢的可以star下哦

插件预览图

使用教程

1.插件代码拷贝

  • 下载后把components目录下countUp.vue文件拷贝到自己项目目录下

2.插件全局配置

  • 在项目里main.js中配置如下代码
import loading from './components/loading/loading.vue'  

Vue.component('loading',loading)  

3.插件使用

  • vue页面使用
<template>  
    <view>  
        <loading ></loading>  
    </view>  
</template>  

<script>  
</script>  

<style>  
</style>  

还收集了一些其他的Loding

Loaders.css

css-loaders

SVG-Loaders

暂时收集了这些,我相信能满足大部分人的需求了

兼容性

uni-app项目中使用都兼容

收起阅读 »

uni.navigateTo 的url不能用变量拼接的方式吗 求大神解答

navigateto

uni.navigateTo({
url:"/pages/member/"+k+"/"+k
})
如上写法 页面会报错navigateTo:fail page /pages/member/set_phone/set_phone is not found,可是我页面是真实存在的。
如果真不能这样写的话 那应该怎么写 难道一个一个判断 然后写???

继续阅读 »

uni.navigateTo({
url:"/pages/member/"+k+"/"+k
})
如上写法 页面会报错navigateTo:fail page /pages/member/set_phone/set_phone is not found,可是我页面是真实存在的。
如果真不能这样写的话 那应该怎么写 难道一个一个判断 然后写???

收起阅读 »

uniapp NFC可读写nfcv格式的nfc标签

NFC uniapp

需求:

在实现了可读写nfc(ndef)标签后,需要兼容另一个标签格式nfcv。

uniapp下nfc如果没有读到参考 uniapp 实现 NFC标签读取 和 写入

读:

java参考代码

private void readNfcv(Tag tag) {  
        System.out.println("进入readNfcv方法");  
        NfcV tech = NfcV.get(tag);  
        if (tech != null) {  
            try {  
                tech.connect();  
                if (tech.isConnected()) {  
                    byte[] tagUid = tag.getId();  
                    System.out.println("ceshi00000:"+tagUid);  
                    // store tag UID for use in addressed commands  
                    //读第一个block  
                    byte[] read = new byte[]{  
                            (byte) 0x22,  // FLAGS  
                            (byte) 0x23,  // 20-READ_SINGLE_BLOCK,23-所有块  
                            0, 0, 0, 0, 0, 0, 0, 0,  
                            0, 0  
                    };  
                    System.out.println("ceshi:"+read);  
                    System.arraycopy(tagUid, 0, read, 2, tagUid.length);  // paste tag UID into command  
                    byte[] res = tech.transceive(read);  
                    System.out.println("2222222:"+res);  
                    if (res != null) {  
                        System.out.println("标记:"+ Arrays.toString(res));  
                        //nfcContent.append("NfcV: " + new String(res, Charset.forName("utf-8")));  
                        if(res[1] == 'H'&& res[2] =='J' && res[3] == 'I'){  
                            int blockAddress = 1;  
                            int blocknum = res[4];  
                            byte[] cmd = new byte[]{  
                                    (byte) 0x22,  // FLAGS  
                                    (byte) 0x23,  // 20-READ_SINGLE_BLOCK,23-所有块  
                                    0, 0, 0, 0, 0, 0, 0, 0,  
                                    (byte) (blockAddress & 0x0ff), (byte) (blocknum - 1 & 0x0ff)  
                            };  
                            System.arraycopy(tagUid, 0, cmd, 2, tagUid.length);  // paste tag UID into command  
                            byte[] response = tech.transceive(cmd);  
                            System.out.println("主要数据"+Arrays.toString(response));  
                            if (response != null) {  
                                nfcContent.append("NfcV: " + new String(response, Charset.forName("utf-8")));  
                            }  
                        }  
                    }  
                }  
            } catch (IOException e) {  
                e.printStackTrace();  
            }finally {  
                try {  
                    tech.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
    }

js实现代码:

        readNfcV(intent) {  
            var NfcV = plus.android.importClass('android.nfc.tech.NfcV');  
            var tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);  
            // console.log(tag);  
            console.log(NfcV.get(tag));  
            var tech = NfcV.get(tag);  
            if (tech != null) {  
                try {  
                    tech.connect();  
                    console.log(tech.isConnected());  
                    if (tech.isConnected()) {  
                        console.log('卡片字节数组ID:' + tag.getId());  
                        var tagUid = tag.getId();  
                        var buffer = [];  
                        buffer[0] = 0x22;  
                        buffer[1] = 0x23;  
                        for (var i in tagUid) {  
                            // console.log(tagUid[i]);  
                            buffer.push(tagUid[i]);  
                        }  
                        buffer[10] = 0;  
                        buffer[11] = 0;  
                        console.log(buffer);  
                        var res = tech.transceive(buffer);  
                        if (res != null) {  
                            console.log('标记:' + res);  
                            if (String.fromCharCode(res[1]) == 'H' && String.fromCharCode(res[2]) == 'J' && String.fromCharCode(res[3]) == 'I') {  
                                var blockAddress = 1;  
                                var blocknum = res[4];  
                                var cmd = [];  
                                cmd[0] = 0x22;  
                                cmd[1] = 0x23;  
                                for (var i in tagUid) {  
                                    // console.log(tagUid[i]);  
                                    cmd.push(tagUid[i]);  
                                }  
                                cmd[10] = blockAddress & 0x0ff;  
                                cmd[11] = (blocknum - 1) & 0x0ff;  
                                console.log(cmd);  
                                var response = tech.transceive(cmd);  
                                var str = ""  
                                for(var j in response){  
                                    // console.log(typeof response[j])  
                                    str +=this.bytesToString(response[j]);  
                                }  
                                console.log(str)  

                            }  
                        }  

                    }  
                } catch (e) {  
                    //TODO handle the exception  
                }  
            }else{  
                this.readNfc(intent)  
            }  
        },

写:

JAVA参考代码

 public void writeNfcV(View view) {  
        System.out.println("进入writeNfcV方法");  
        NfcV nfcV = null;  
        Intent intent = getIntent();  
        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);  
        if (tag == null) {  
            nfcContent.append("未检测到NFC标签!");  
            return;  
        }  
        try {  
            nfcV = NfcV.get(tag);  
            nfcV.connect();  
            System.out.println("最大字节数:"+nfcV.getMaxTransceiveLength());  
            byte[] ID = nfcV.getTag().getId();  
            byte[] cmd = new byte[15];  
            cmd[0] = (byte) 0x22;  
            cmd[1] = (byte) 0x21;  
            System.arraycopy(ID, 0, cmd, 2, ID.length); // UID  
            byte[] input = nfcWrite.getText().toString().getBytes();  
            int count = (input.length+3)/4;//存储block数(用户输入)  
            System.out.println("count:"+count);  
            byte[] data = new byte[(count+1)*4];  
            System.out.println("data000:"+Arrays.toString(data));  
            byte[] mark = new byte[4];  
            mark[0] = 'H';  
            mark[1] = 'J';  
            mark[2] = 'I';  
            mark[3] = (byte) count;  
            System.out.println("MARK01:"+Arrays.toString(mark));  
            System.arraycopy(mark,0,data,0,4);  
            System.out.println("DATA01:"+Arrays.toString(data));  
            System.arraycopy(input,0,data,4,input.length);  
            System.out.println("DATA02:"+Arrays.toString(data));  
            for (int i = 0; i < count+1; i++) {  
                cmd[10] = (byte)(i & 0x0ff);  
                System.arraycopy(data, i*4, cmd, 11, 4);  
                byte[] rsp = nfcV.transceive(cmd);  
                if(rsp[0] == 0x00 && i==count) {  
                    Toast.makeText(this, "已成功写入数据!", Toast.LENGTH_LONG).show();  
                }  
            }  
        } catch (IOException e) {  
            e.printStackTrace();  
        }finally {  
            try {  
                if(nfcV != null) {  
                    nfcV.close();  
                }  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
    }

JS实现代码

__writeNfcV(intent) {  
                try {  
                    waiting = plus.nativeUI.showWaiting('请将NFC标签靠近!');  
                    waiting.setTitle('请勿移开标签\n正在写入...');  
                    // var text = document.getElementById('text').value;  
                    console.log('text=' + this.writeCode);  
                    var textBytes = plus.android.invoke(this.writeCode, 'getBytes');  
                    var textRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA, plus.android.invoke('text/plain', 'getBytes'), plus.android  
                        .invoke('', 'getBytes'), textBytes);  
                    var message = new NdefMessage([textRecord]);  
                    var NfcV = plus.android.importClass('android.nfc.tech.NfcV');  
                    // var NdefFormatable = plus.android.importClass('android.nfc.tech.NdefFormatable');  
                    var tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);  
                    var nfcv = NfcV.get(tag);  
                    console.log('标签格式:' + nfcv);  
                    var bytesId = intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);  
                    console.log('bytesId:' + bytesId);  
                    // console.log(nfcv.getTag().getId())  
                    if (nfcv != null) {  
                        nfcv.connect();  
                        var size = nfcv.getMaxTransceiveLength();  
                        this.writeCode = '123456';//编辑内容  
                        var strVal = this.writeCode;  
                        console.log("zijie:" + this.stringToByte(strVal))  
                        var strValByte = this.stringToByte(strVal);  
                        console.log(strValByte.length)  
                        console.log('最大字节数:' + size);  
                        var cmd = [];  
                        cmd[0] = 0x22;  
                        cmd[1] = 0x21;  
                        for (var i in bytesId) {  
                            // console.log(tagUid[i]);  
                            cmd.push(bytesId[i]);  
                        }  
                        console.log(cmd);  
                        var count = parseInt(strValByte.length + 3)/4 ;//存block数  
                        console.log("count:"+count);  
                        var datav = new Array((count+1)*4);  
                        console.log("datav:"+datav)  
                        // var mark = [];  
                        // mark[0]="72"; //H  
                        // mark[1] = "74";//J  
                        // mark[2] = "73";//I  
                        // mark[3]=count;  
                        datav[0] = 72;  
                        datav[1] = 74;  
                        datav[2] = 73;  
                        datav[3] = count;  
                        // console.log("datav01:"+datav)  
                        for(var j=0;j<strValByte.length;j++){  
                            // console.log(strValByte[j]);  
                            datav[4+j] = strValByte[j]  
                        }  
                        console.log("datav02:"+datav);  
                        for(var i = 0;i<count+1;i++){  
                            cmd[10] = i&0x0ff;  
                            for(var s=0;s<4;s++){  
                                cmd[11+s] = datav[4*i+s];  
                            }  
                            // cmd[11+i]=datav[i*4+i]  

                            console.log("cmd22:"+cmd);  
                            var rsp = nfcv.transceive(cmd);  
                            console.log(rsp[0])  
                            if(rsp[0] == 0x00&& i == count){  
                                console.log('写入数据成功.');  
                                uni.showToast({  
                                    title: '写入数据成功.',  
                                    icon: 'none'  
                                });  

                            }  
                        }  

                        waiting.close();  
                        return;  

                    } else {  
                        console.log("未检测到NFCV标签");  
                        waiting.close();  
                        this.__write(intent);  
                        return;  
                    }  
                } catch (e) {  
                    console.log('error=' + e);  
                    waiting.close();  
                    uni.showToast({  
                        title: 'NFC标签写入失败,请重新贴近手机',  
                        icon: 'none'  
                    });  
                }  
            },  
stringToByte(str) {   
                var bytes = new Array();  
                var len, c;  
                len = str.length;  
                for (var i = 0; i < len; i++) {  
                    c = str.charCodeAt(i);  
                    if (c >= 0x010000 && c <= 0x10FFFF) {  
                        bytes.push(((c >> 18) & 0x07) | 0xF0);  
                        bytes.push(((c >> 12) & 0x3F) | 0x80);  
                        bytes.push(((c >> 6) & 0x3F) | 0x80);  
                        bytes.push((c & 0x3F) | 0x80);  
                    } else if (c >= 0x000800 && c <= 0x00FFFF) {  
                        bytes.push(((c >> 12) & 0x0F) | 0xE0);  
                        bytes.push(((c >> 6) & 0x3F) | 0x80);  
                        bytes.push((c & 0x3F) | 0x80);  
                    } else if (c >= 0x000080 && c <= 0x0007FF) {  
                        bytes.push(((c >> 6) & 0x1F) | 0xC0);  
                        bytes.push((c & 0x3F) | 0x80);  
                    } else {  
                        bytes.push(c & 0xFF);  
                    }  
                }  
                return bytes;  

            },

ps:
写的时候被自己蠢到了,java btye[]浪费了写时间,结果在最简单的w3c文档中看到byte在js中属于number。

继续阅读 »

需求:

在实现了可读写nfc(ndef)标签后,需要兼容另一个标签格式nfcv。

uniapp下nfc如果没有读到参考 uniapp 实现 NFC标签读取 和 写入

读:

java参考代码

private void readNfcv(Tag tag) {  
        System.out.println("进入readNfcv方法");  
        NfcV tech = NfcV.get(tag);  
        if (tech != null) {  
            try {  
                tech.connect();  
                if (tech.isConnected()) {  
                    byte[] tagUid = tag.getId();  
                    System.out.println("ceshi00000:"+tagUid);  
                    // store tag UID for use in addressed commands  
                    //读第一个block  
                    byte[] read = new byte[]{  
                            (byte) 0x22,  // FLAGS  
                            (byte) 0x23,  // 20-READ_SINGLE_BLOCK,23-所有块  
                            0, 0, 0, 0, 0, 0, 0, 0,  
                            0, 0  
                    };  
                    System.out.println("ceshi:"+read);  
                    System.arraycopy(tagUid, 0, read, 2, tagUid.length);  // paste tag UID into command  
                    byte[] res = tech.transceive(read);  
                    System.out.println("2222222:"+res);  
                    if (res != null) {  
                        System.out.println("标记:"+ Arrays.toString(res));  
                        //nfcContent.append("NfcV: " + new String(res, Charset.forName("utf-8")));  
                        if(res[1] == 'H'&& res[2] =='J' && res[3] == 'I'){  
                            int blockAddress = 1;  
                            int blocknum = res[4];  
                            byte[] cmd = new byte[]{  
                                    (byte) 0x22,  // FLAGS  
                                    (byte) 0x23,  // 20-READ_SINGLE_BLOCK,23-所有块  
                                    0, 0, 0, 0, 0, 0, 0, 0,  
                                    (byte) (blockAddress & 0x0ff), (byte) (blocknum - 1 & 0x0ff)  
                            };  
                            System.arraycopy(tagUid, 0, cmd, 2, tagUid.length);  // paste tag UID into command  
                            byte[] response = tech.transceive(cmd);  
                            System.out.println("主要数据"+Arrays.toString(response));  
                            if (response != null) {  
                                nfcContent.append("NfcV: " + new String(response, Charset.forName("utf-8")));  
                            }  
                        }  
                    }  
                }  
            } catch (IOException e) {  
                e.printStackTrace();  
            }finally {  
                try {  
                    tech.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
    }

js实现代码:

        readNfcV(intent) {  
            var NfcV = plus.android.importClass('android.nfc.tech.NfcV');  
            var tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);  
            // console.log(tag);  
            console.log(NfcV.get(tag));  
            var tech = NfcV.get(tag);  
            if (tech != null) {  
                try {  
                    tech.connect();  
                    console.log(tech.isConnected());  
                    if (tech.isConnected()) {  
                        console.log('卡片字节数组ID:' + tag.getId());  
                        var tagUid = tag.getId();  
                        var buffer = [];  
                        buffer[0] = 0x22;  
                        buffer[1] = 0x23;  
                        for (var i in tagUid) {  
                            // console.log(tagUid[i]);  
                            buffer.push(tagUid[i]);  
                        }  
                        buffer[10] = 0;  
                        buffer[11] = 0;  
                        console.log(buffer);  
                        var res = tech.transceive(buffer);  
                        if (res != null) {  
                            console.log('标记:' + res);  
                            if (String.fromCharCode(res[1]) == 'H' && String.fromCharCode(res[2]) == 'J' && String.fromCharCode(res[3]) == 'I') {  
                                var blockAddress = 1;  
                                var blocknum = res[4];  
                                var cmd = [];  
                                cmd[0] = 0x22;  
                                cmd[1] = 0x23;  
                                for (var i in tagUid) {  
                                    // console.log(tagUid[i]);  
                                    cmd.push(tagUid[i]);  
                                }  
                                cmd[10] = blockAddress & 0x0ff;  
                                cmd[11] = (blocknum - 1) & 0x0ff;  
                                console.log(cmd);  
                                var response = tech.transceive(cmd);  
                                var str = ""  
                                for(var j in response){  
                                    // console.log(typeof response[j])  
                                    str +=this.bytesToString(response[j]);  
                                }  
                                console.log(str)  

                            }  
                        }  

                    }  
                } catch (e) {  
                    //TODO handle the exception  
                }  
            }else{  
                this.readNfc(intent)  
            }  
        },

写:

JAVA参考代码

 public void writeNfcV(View view) {  
        System.out.println("进入writeNfcV方法");  
        NfcV nfcV = null;  
        Intent intent = getIntent();  
        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);  
        if (tag == null) {  
            nfcContent.append("未检测到NFC标签!");  
            return;  
        }  
        try {  
            nfcV = NfcV.get(tag);  
            nfcV.connect();  
            System.out.println("最大字节数:"+nfcV.getMaxTransceiveLength());  
            byte[] ID = nfcV.getTag().getId();  
            byte[] cmd = new byte[15];  
            cmd[0] = (byte) 0x22;  
            cmd[1] = (byte) 0x21;  
            System.arraycopy(ID, 0, cmd, 2, ID.length); // UID  
            byte[] input = nfcWrite.getText().toString().getBytes();  
            int count = (input.length+3)/4;//存储block数(用户输入)  
            System.out.println("count:"+count);  
            byte[] data = new byte[(count+1)*4];  
            System.out.println("data000:"+Arrays.toString(data));  
            byte[] mark = new byte[4];  
            mark[0] = 'H';  
            mark[1] = 'J';  
            mark[2] = 'I';  
            mark[3] = (byte) count;  
            System.out.println("MARK01:"+Arrays.toString(mark));  
            System.arraycopy(mark,0,data,0,4);  
            System.out.println("DATA01:"+Arrays.toString(data));  
            System.arraycopy(input,0,data,4,input.length);  
            System.out.println("DATA02:"+Arrays.toString(data));  
            for (int i = 0; i < count+1; i++) {  
                cmd[10] = (byte)(i & 0x0ff);  
                System.arraycopy(data, i*4, cmd, 11, 4);  
                byte[] rsp = nfcV.transceive(cmd);  
                if(rsp[0] == 0x00 && i==count) {  
                    Toast.makeText(this, "已成功写入数据!", Toast.LENGTH_LONG).show();  
                }  
            }  
        } catch (IOException e) {  
            e.printStackTrace();  
        }finally {  
            try {  
                if(nfcV != null) {  
                    nfcV.close();  
                }  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
    }

JS实现代码

__writeNfcV(intent) {  
                try {  
                    waiting = plus.nativeUI.showWaiting('请将NFC标签靠近!');  
                    waiting.setTitle('请勿移开标签\n正在写入...');  
                    // var text = document.getElementById('text').value;  
                    console.log('text=' + this.writeCode);  
                    var textBytes = plus.android.invoke(this.writeCode, 'getBytes');  
                    var textRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA, plus.android.invoke('text/plain', 'getBytes'), plus.android  
                        .invoke('', 'getBytes'), textBytes);  
                    var message = new NdefMessage([textRecord]);  
                    var NfcV = plus.android.importClass('android.nfc.tech.NfcV');  
                    // var NdefFormatable = plus.android.importClass('android.nfc.tech.NdefFormatable');  
                    var tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);  
                    var nfcv = NfcV.get(tag);  
                    console.log('标签格式:' + nfcv);  
                    var bytesId = intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);  
                    console.log('bytesId:' + bytesId);  
                    // console.log(nfcv.getTag().getId())  
                    if (nfcv != null) {  
                        nfcv.connect();  
                        var size = nfcv.getMaxTransceiveLength();  
                        this.writeCode = '123456';//编辑内容  
                        var strVal = this.writeCode;  
                        console.log("zijie:" + this.stringToByte(strVal))  
                        var strValByte = this.stringToByte(strVal);  
                        console.log(strValByte.length)  
                        console.log('最大字节数:' + size);  
                        var cmd = [];  
                        cmd[0] = 0x22;  
                        cmd[1] = 0x21;  
                        for (var i in bytesId) {  
                            // console.log(tagUid[i]);  
                            cmd.push(bytesId[i]);  
                        }  
                        console.log(cmd);  
                        var count = parseInt(strValByte.length + 3)/4 ;//存block数  
                        console.log("count:"+count);  
                        var datav = new Array((count+1)*4);  
                        console.log("datav:"+datav)  
                        // var mark = [];  
                        // mark[0]="72"; //H  
                        // mark[1] = "74";//J  
                        // mark[2] = "73";//I  
                        // mark[3]=count;  
                        datav[0] = 72;  
                        datav[1] = 74;  
                        datav[2] = 73;  
                        datav[3] = count;  
                        // console.log("datav01:"+datav)  
                        for(var j=0;j<strValByte.length;j++){  
                            // console.log(strValByte[j]);  
                            datav[4+j] = strValByte[j]  
                        }  
                        console.log("datav02:"+datav);  
                        for(var i = 0;i<count+1;i++){  
                            cmd[10] = i&0x0ff;  
                            for(var s=0;s<4;s++){  
                                cmd[11+s] = datav[4*i+s];  
                            }  
                            // cmd[11+i]=datav[i*4+i]  

                            console.log("cmd22:"+cmd);  
                            var rsp = nfcv.transceive(cmd);  
                            console.log(rsp[0])  
                            if(rsp[0] == 0x00&& i == count){  
                                console.log('写入数据成功.');  
                                uni.showToast({  
                                    title: '写入数据成功.',  
                                    icon: 'none'  
                                });  

                            }  
                        }  

                        waiting.close();  
                        return;  

                    } else {  
                        console.log("未检测到NFCV标签");  
                        waiting.close();  
                        this.__write(intent);  
                        return;  
                    }  
                } catch (e) {  
                    console.log('error=' + e);  
                    waiting.close();  
                    uni.showToast({  
                        title: 'NFC标签写入失败,请重新贴近手机',  
                        icon: 'none'  
                    });  
                }  
            },  
stringToByte(str) {   
                var bytes = new Array();  
                var len, c;  
                len = str.length;  
                for (var i = 0; i < len; i++) {  
                    c = str.charCodeAt(i);  
                    if (c >= 0x010000 && c <= 0x10FFFF) {  
                        bytes.push(((c >> 18) & 0x07) | 0xF0);  
                        bytes.push(((c >> 12) & 0x3F) | 0x80);  
                        bytes.push(((c >> 6) & 0x3F) | 0x80);  
                        bytes.push((c & 0x3F) | 0x80);  
                    } else if (c >= 0x000800 && c <= 0x00FFFF) {  
                        bytes.push(((c >> 12) & 0x0F) | 0xE0);  
                        bytes.push(((c >> 6) & 0x3F) | 0x80);  
                        bytes.push((c & 0x3F) | 0x80);  
                    } else if (c >= 0x000080 && c <= 0x0007FF) {  
                        bytes.push(((c >> 6) & 0x1F) | 0xC0);  
                        bytes.push((c & 0x3F) | 0x80);  
                    } else {  
                        bytes.push(c & 0xFF);  
                    }  
                }  
                return bytes;  

            },

ps:
写的时候被自己蠢到了,java btye[]浪费了写时间,结果在最简单的w3c文档中看到byte在js中属于number。

收起阅读 »

iOS平台配置应用使用广告标识(IDFA)

iOS IDFA

此文档将不再维护,请参考新文档:https://uniapp.dcloud.io/tutorial/app-ios-idfa

HBuilderX 3.2.9+ 版本已适配 iOS15 请更新

  • 将 HX 升级到 3.2.9 及以上版本,建议使用最新的版本;
  • 如果项目是通过 cli 创建的需要将依赖也升级到新版本;

概述

12年9月份iOS6发布,IDFA面世,主要用于给开发者跟踪应用中广告的投放效果,但很多应用(或三方SDK)会获取IDFA作为设备唯一标识使用。
iOS14.5 发布之后,苹果要求应用获取 IDFA 时,需弹出用户许可收集跟踪数据的授权框,如果没有弹出授权框则可能会被App Store审核拒绝,提示违反5.1.2规则:

Guideline 5.1.2 - Legal - Privacy - Data Use and Sharing  

We noticed you do not use App Tracking Transparency to request the user's permission before tracking their activity across apps and websites. The app privacy information you provided in App Store Connect indicates you collect data in order to track the user, including Device ID and Precise Location.  

Starting with iOS 14.5, apps on the App Store need to receive the user’s permission through the AppTrackingTransparency framework before collecting data used to track them. This requirement protects the privacy of App Store users.  

Next Steps  

Here are two ways to resolve this issue:  

- You can remove the tracking functionality from your app and update your app privacy information in App Store Connect.  
- If you decide to continue tracking users, you must implement App Tracking Transparency and request permission before collecting data used to track the user or device.  

Resources  

- See Frequently Asked Questions about the new requirements for apps that track users.  
- Learn more about designing appropriate permission requests.

请使用 HBuilderX 3.2.9+ 版本,按照下面步骤配置开启广告标识(IDFA),重新打包提交审核`

如何判断是否需要开启广告标识(IDFA)?

1.只要您的应用使用了uni-AD广告模块,就需要开启 IDFA;
2.使用的 HX 版本低于 3.2.15 版本并且应用使用了新浪微博登录和分享、一键登录、友盟统计 其中一个或多个功能模块,这些SDK内会触发获取IDFA,所以需要开启 IDFA (注:HX 3.2.15及以上版本更新了这些三方SDK,不在获取IDFA)

一些uni原生插件也可能会读取IDFA,因此碰到App Store审核不通过,提示违反5.1.2规则且内容中包含App Tracking Transparency都可以通过配置开启广告标识(IDFA)解决。

uni-AD中的广告基础功能并不会访问IDFA,没有勾选三方广告SDK时不需要访问开启广告标识(IDFA)
注:对于非广告类的三方SDK,我们会密切关注其官方的版本更新,待官方出了不包含IDFA的版本我们会尽快适配升级

配置开启广告标识(IDFA)

在 manifest.json 文件的 “App常用其它设置” 中可勾选开启(注意HBuilder X 2.4以上为默认勾选),需提交云端打包才会生效

配置 NSUserTrackingUsageDescription 隐私描述
开启广告标识(IDFA)后,云端打包默认隐私描述为“请放心,开启权限不会获取您在其他站点的隐私信息,该权限仅用于标识设备并保障服务安全与提示浏览体验”,也可以在 manifest.json 文件的 “App权限配置” 项的“iOS隐私信息访问的许可描述”下配置 NSUserTrackingUsageDescription 隐私描述:

注:使用低版本HBuilderX时可以切换到manifest.json的源码视图,手动在"privacyDescription"节点下添加 NSUserTrackingUsageDescription 字段配置隐私描述。

隐私描述是为了告诉用户,应用为什么要跟踪用户及访问设备的IDFA,配置的描述内容会展示在授权框上,参考以下建议描述说明:

  • 包含uni-AD功能时填写: 请放心,开启权限不会获取您在其他站点的隐私信息,该权限仅用于标识设备、第三方广告、并保障服务安全与提示浏览体验
  • 不包含uni-AD功能时填写:请放心,开启权限不会获取您在其他站点的隐私信息,该权限仅用于标识设备并保障服务安全与提示浏览体验

运行时弹出授权提示框显示效果如下:

配置 “App 隐私”

开启广告标识(IDFA)后,提交App Store审核之前,需要在App Store Connect 配置 “App 隐私”。
首先点击编辑“数据类型”,勾选 “是,我们会从此App收集数据” 选项;然后点下一步,


因为uni框架会收集崩溃数据,所以需要勾选列表中的“崩溃数据”选项,另外再勾选“设备 ID”选项;
如果您使用了uni-AD,则勾选“广告数据”选项,如果没有,则不勾选;
如下图所示,然后发布。

注意:编辑“数据类型”时,需根据自己应用使用到的数据,灵活勾选;比如使用了定位,则勾选定位相关的选项
再编辑“设备 ID” (如使用了uni-AD则再勾选“第三方广告”选项)

编辑“崩溃数据”

编辑“广告数据”(如果上一步勾选了)

完成界面

代码中如何获取IDFA标识

plus.device.getInfo({//需要勾选IDFA  
    success:function(e){  
        console.log('idfa =  '+JSON.stringify(e.idfa));  
    },  
    fail:function(e){  
        console.log('getDeviceInfo failed: '+JSON.stringify(e));  
    }  
});

也可通过native.js获取:

var idfa = '';  
var manager = plus.ios.invoke('ASIdentifierManager', 'sharedManager');  
if(plus.ios.invoke(manager, 'isAdvertisingTrackingEnabled')){  
    var identifier = plus.ios.invoke(manager, 'advertisingIdentifier');  
    idfa = plus.ios.invoke(identifier, 'UUIDString');  
    plus.ios.deleteObject(identifier);  
}  
plus.ios.deleteObject(manager);  
console.log('idfa = '+idfa);

参考Uni插件示例:https://ext.dcloud.net.cn/plugin?id=726

5+SDK离线打包

配置参考文档:https://nativesupport.dcloud.net.cn/AppDocs/usesdk/ios

注意事项

配置NSUserTrackingUsageDescription仍然审核不通过,提示违反5.1.1规则:

如果配置了开启广告标识(IDFA)并且也配置了 NSUserTrackingUsageDescription隐私描述,但是应用还是被App Stroe审核拒绝,且提示违反5.1.1规则:

Guideline 5.1.1 - Legal - Privacy - Data Collection and Storage    

We noticed that your app requests the user's consent to access the AppTrackingTransparency framework, but doesn't sufficiently explain the use of the AppTrackingTransparency framework in the purpose string.  

To help users make informed decisions about how their data is used, all permission request alerts need to specify how your app will use the requested information.    

Next Steps    

Please revise the relevant purpose string in your app's Info.plist file to specify why your app needs access to the user's AppTrackingTransparency framework.  

You can modify your app's Info.plist file using the property list editor in Xcode.  

Resources    

- See example of helpful, informative purpose strings.  
- Review a list of relevant property list keys.

则可能是描述内容过于简单,没有准确说明应用为什么要跟踪用户及访问设备的IDFA,可参考上文的建议更新NSUserTrackingUsageDescription描述内容

配置NSUserTrackingUsageDescription仍然审核不通过,提示违反2.1规则:

如果配置了开启广告标识(IDFA)并且也配置了 NSUserTrackingUsageDescription隐私描述,但是应用还是被App Stroe审核拒绝,且提示违反2.1规则:

Guideline 2.1 - Information Needed  

We're looking forward to completing the review of your app, but we need more information to continue. Specifically, we noticed that your app uses the AppTrackingTransparency framework, but we haven't been able to locate the App Tracking Transparency permission request.  

Next Steps  

Please indicate where in your app we can find the AppTrackingTransparency permission request. The request should appear before any data is collected that could be used to track the user.  

Apps that track user's activity must implement App Tracking Transparency and request permission before collecting data used to track.  

Resources  

See Frequently Asked Questions about the new requirements for apps that track users.  

Since your App Store Connect status is Metadata Rejected, we do NOT require a new binary. To revise the metadata, visit App Store Connect to select your app and revise the desired metadata values. Once you’ve completed all changes, reply to this message in Resolution Center and we will continue the review.

则可能是App Store Connect配置 “App 隐私”的选项不对,请参考上文重新 配置 “App 隐私”中的选项。

配置NSUserTrackingUsageDescription后真机运行不弹出授权提示框

如果按照上述描述勾选了IDFA 和配置了NSUserTrackingUsageDescription隐私描述,但是真机运行App启动时没有弹出授权提示框,可能的原因是,手机的系统版本是iOS14以下的,或者是iOS14.5的手机,但是手机“设置-隐私-跟踪”里,系统默认将“跟踪选项”关闭了且灰色不可设置;目前这种情况还不确定是iOS的bug,还是是针对地区特殊对待,解决办法:可以将账号切换成一个美区的,这时“跟踪选项”是可以操作的;或者将手机设置还原成默认设置,这时App启动时也能弹出授权提示框,但只会弹出一次。

继续阅读 »

此文档将不再维护,请参考新文档:https://uniapp.dcloud.io/tutorial/app-ios-idfa

HBuilderX 3.2.9+ 版本已适配 iOS15 请更新

  • 将 HX 升级到 3.2.9 及以上版本,建议使用最新的版本;
  • 如果项目是通过 cli 创建的需要将依赖也升级到新版本;

概述

12年9月份iOS6发布,IDFA面世,主要用于给开发者跟踪应用中广告的投放效果,但很多应用(或三方SDK)会获取IDFA作为设备唯一标识使用。
iOS14.5 发布之后,苹果要求应用获取 IDFA 时,需弹出用户许可收集跟踪数据的授权框,如果没有弹出授权框则可能会被App Store审核拒绝,提示违反5.1.2规则:

Guideline 5.1.2 - Legal - Privacy - Data Use and Sharing  

We noticed you do not use App Tracking Transparency to request the user's permission before tracking their activity across apps and websites. The app privacy information you provided in App Store Connect indicates you collect data in order to track the user, including Device ID and Precise Location.  

Starting with iOS 14.5, apps on the App Store need to receive the user’s permission through the AppTrackingTransparency framework before collecting data used to track them. This requirement protects the privacy of App Store users.  

Next Steps  

Here are two ways to resolve this issue:  

- You can remove the tracking functionality from your app and update your app privacy information in App Store Connect.  
- If you decide to continue tracking users, you must implement App Tracking Transparency and request permission before collecting data used to track the user or device.  

Resources  

- See Frequently Asked Questions about the new requirements for apps that track users.  
- Learn more about designing appropriate permission requests.

请使用 HBuilderX 3.2.9+ 版本,按照下面步骤配置开启广告标识(IDFA),重新打包提交审核`

如何判断是否需要开启广告标识(IDFA)?

1.只要您的应用使用了uni-AD广告模块,就需要开启 IDFA;
2.使用的 HX 版本低于 3.2.15 版本并且应用使用了新浪微博登录和分享、一键登录、友盟统计 其中一个或多个功能模块,这些SDK内会触发获取IDFA,所以需要开启 IDFA (注:HX 3.2.15及以上版本更新了这些三方SDK,不在获取IDFA)

一些uni原生插件也可能会读取IDFA,因此碰到App Store审核不通过,提示违反5.1.2规则且内容中包含App Tracking Transparency都可以通过配置开启广告标识(IDFA)解决。

uni-AD中的广告基础功能并不会访问IDFA,没有勾选三方广告SDK时不需要访问开启广告标识(IDFA)
注:对于非广告类的三方SDK,我们会密切关注其官方的版本更新,待官方出了不包含IDFA的版本我们会尽快适配升级

配置开启广告标识(IDFA)

在 manifest.json 文件的 “App常用其它设置” 中可勾选开启(注意HBuilder X 2.4以上为默认勾选),需提交云端打包才会生效

配置 NSUserTrackingUsageDescription 隐私描述
开启广告标识(IDFA)后,云端打包默认隐私描述为“请放心,开启权限不会获取您在其他站点的隐私信息,该权限仅用于标识设备并保障服务安全与提示浏览体验”,也可以在 manifest.json 文件的 “App权限配置” 项的“iOS隐私信息访问的许可描述”下配置 NSUserTrackingUsageDescription 隐私描述:

注:使用低版本HBuilderX时可以切换到manifest.json的源码视图,手动在"privacyDescription"节点下添加 NSUserTrackingUsageDescription 字段配置隐私描述。

隐私描述是为了告诉用户,应用为什么要跟踪用户及访问设备的IDFA,配置的描述内容会展示在授权框上,参考以下建议描述说明:

  • 包含uni-AD功能时填写: 请放心,开启权限不会获取您在其他站点的隐私信息,该权限仅用于标识设备、第三方广告、并保障服务安全与提示浏览体验
  • 不包含uni-AD功能时填写:请放心,开启权限不会获取您在其他站点的隐私信息,该权限仅用于标识设备并保障服务安全与提示浏览体验

运行时弹出授权提示框显示效果如下:

配置 “App 隐私”

开启广告标识(IDFA)后,提交App Store审核之前,需要在App Store Connect 配置 “App 隐私”。
首先点击编辑“数据类型”,勾选 “是,我们会从此App收集数据” 选项;然后点下一步,


因为uni框架会收集崩溃数据,所以需要勾选列表中的“崩溃数据”选项,另外再勾选“设备 ID”选项;
如果您使用了uni-AD,则勾选“广告数据”选项,如果没有,则不勾选;
如下图所示,然后发布。

注意:编辑“数据类型”时,需根据自己应用使用到的数据,灵活勾选;比如使用了定位,则勾选定位相关的选项
再编辑“设备 ID” (如使用了uni-AD则再勾选“第三方广告”选项)

编辑“崩溃数据”

编辑“广告数据”(如果上一步勾选了)

完成界面

代码中如何获取IDFA标识

plus.device.getInfo({//需要勾选IDFA  
    success:function(e){  
        console.log('idfa =  '+JSON.stringify(e.idfa));  
    },  
    fail:function(e){  
        console.log('getDeviceInfo failed: '+JSON.stringify(e));  
    }  
});

也可通过native.js获取:

var idfa = '';  
var manager = plus.ios.invoke('ASIdentifierManager', 'sharedManager');  
if(plus.ios.invoke(manager, 'isAdvertisingTrackingEnabled')){  
    var identifier = plus.ios.invoke(manager, 'advertisingIdentifier');  
    idfa = plus.ios.invoke(identifier, 'UUIDString');  
    plus.ios.deleteObject(identifier);  
}  
plus.ios.deleteObject(manager);  
console.log('idfa = '+idfa);

参考Uni插件示例:https://ext.dcloud.net.cn/plugin?id=726

5+SDK离线打包

配置参考文档:https://nativesupport.dcloud.net.cn/AppDocs/usesdk/ios

注意事项

配置NSUserTrackingUsageDescription仍然审核不通过,提示违反5.1.1规则:

如果配置了开启广告标识(IDFA)并且也配置了 NSUserTrackingUsageDescription隐私描述,但是应用还是被App Stroe审核拒绝,且提示违反5.1.1规则:

Guideline 5.1.1 - Legal - Privacy - Data Collection and Storage    

We noticed that your app requests the user's consent to access the AppTrackingTransparency framework, but doesn't sufficiently explain the use of the AppTrackingTransparency framework in the purpose string.  

To help users make informed decisions about how their data is used, all permission request alerts need to specify how your app will use the requested information.    

Next Steps    

Please revise the relevant purpose string in your app's Info.plist file to specify why your app needs access to the user's AppTrackingTransparency framework.  

You can modify your app's Info.plist file using the property list editor in Xcode.  

Resources    

- See example of helpful, informative purpose strings.  
- Review a list of relevant property list keys.

则可能是描述内容过于简单,没有准确说明应用为什么要跟踪用户及访问设备的IDFA,可参考上文的建议更新NSUserTrackingUsageDescription描述内容

配置NSUserTrackingUsageDescription仍然审核不通过,提示违反2.1规则:

如果配置了开启广告标识(IDFA)并且也配置了 NSUserTrackingUsageDescription隐私描述,但是应用还是被App Stroe审核拒绝,且提示违反2.1规则:

Guideline 2.1 - Information Needed  

We're looking forward to completing the review of your app, but we need more information to continue. Specifically, we noticed that your app uses the AppTrackingTransparency framework, but we haven't been able to locate the App Tracking Transparency permission request.  

Next Steps  

Please indicate where in your app we can find the AppTrackingTransparency permission request. The request should appear before any data is collected that could be used to track the user.  

Apps that track user's activity must implement App Tracking Transparency and request permission before collecting data used to track.  

Resources  

See Frequently Asked Questions about the new requirements for apps that track users.  

Since your App Store Connect status is Metadata Rejected, we do NOT require a new binary. To revise the metadata, visit App Store Connect to select your app and revise the desired metadata values. Once you’ve completed all changes, reply to this message in Resolution Center and we will continue the review.

则可能是App Store Connect配置 “App 隐私”的选项不对,请参考上文重新 配置 “App 隐私”中的选项。

配置NSUserTrackingUsageDescription后真机运行不弹出授权提示框

如果按照上述描述勾选了IDFA 和配置了NSUserTrackingUsageDescription隐私描述,但是真机运行App启动时没有弹出授权提示框,可能的原因是,手机的系统版本是iOS14以下的,或者是iOS14.5的手机,但是手机“设置-隐私-跟踪”里,系统默认将“跟踪选项”关闭了且灰色不可设置;目前这种情况还不确定是iOS的bug,还是是针对地区特殊对待,解决办法:可以将账号切换成一个美区的,这时“跟踪选项”是可以操作的;或者将手机设置还原成默认设置,这时App启动时也能弹出授权提示框,但只会弹出一次。

收起阅读 »

如何在 uniapp 项目中,使用“插件市场” 的原生插件

uniapp原生插件 插件市场 uniapp插件

插件市场 : http://ext.dcloud.net.cn/?cat1=5&cat2=51

1. 第一步: 首先在HBuilder x 中新建一个uniapp 项目,如果已建好 uniapp 项目则跳过该步骤。

第二步: 在 ”插件市场中 “ 选择你需要的原生插件 -》 选择购买 或 试用-》 弹出一个 uniapp 应用选择框,勾选你要在哪一个uniapp项目中使用该插件。

第三步: 回到HBuilder x 工程中,找到刚才 勾选中的 uniapp 项目,打开配置文件-》manifest.json -》选择“App原生插件配置” -》选择 “ 云端插件 “,弹出一个插件选择框,勾选你要使用的插件即可。

第四步: 你进行正常的云打包,就可以使用相关插件的功能, 如果你是想在本地测试使用该插件,还需要制作自定义基座,分别是android 或 ios 的,主要是把插件打包进基座里,以便可以在本地测试应用, 制作自定义基座方法, HBx 菜单栏 -》发行-》原生App 云打包-》填写好相关的证书文件后-》勾选自定义基座-》执行打包。

第五步: 等待云打包成功, 你检查 unpackage / debug 文件夹下会出一个: android_debug.apk 或者 iOS_debug.ipa 安装包,这就是测试使用的自定义基座安装包。

第六步:等自定义基座制作完成后, 在HBx 菜单栏-》运行 -》运行到手机或者模拟器-》运行基座选择-》 选择 自定义基座 即可。

第七步: 运行项目到 手机 或者 模拟器上,就可以使用该插件的功能。

第八步:根据插件相关的文档 ,使用插件相关的方法即可。

继续阅读 »

插件市场 : http://ext.dcloud.net.cn/?cat1=5&cat2=51

1. 第一步: 首先在HBuilder x 中新建一个uniapp 项目,如果已建好 uniapp 项目则跳过该步骤。

第二步: 在 ”插件市场中 “ 选择你需要的原生插件 -》 选择购买 或 试用-》 弹出一个 uniapp 应用选择框,勾选你要在哪一个uniapp项目中使用该插件。

第三步: 回到HBuilder x 工程中,找到刚才 勾选中的 uniapp 项目,打开配置文件-》manifest.json -》选择“App原生插件配置” -》选择 “ 云端插件 “,弹出一个插件选择框,勾选你要使用的插件即可。

第四步: 你进行正常的云打包,就可以使用相关插件的功能, 如果你是想在本地测试使用该插件,还需要制作自定义基座,分别是android 或 ios 的,主要是把插件打包进基座里,以便可以在本地测试应用, 制作自定义基座方法, HBx 菜单栏 -》发行-》原生App 云打包-》填写好相关的证书文件后-》勾选自定义基座-》执行打包。

第五步: 等待云打包成功, 你检查 unpackage / debug 文件夹下会出一个: android_debug.apk 或者 iOS_debug.ipa 安装包,这就是测试使用的自定义基座安装包。

第六步:等自定义基座制作完成后, 在HBx 菜单栏-》运行 -》运行到手机或者模拟器-》运行基座选择-》 选择 自定义基座 即可。

第七步: 运行项目到 手机 或者 模拟器上,就可以使用该插件的功能。

第八步:根据插件相关的文档 ,使用插件相关的方法即可。

收起阅读 »

关于uniapp原生插件打包 FileProvider冲突问题。

云打包冲突 插件 FileProvider 冲突

1、新建XXFileProvider继承自FileProvider
2、注册改为<provider android:name="xxx.xx.xx.XXFileProvider"
android:authorities="${applicationId}.xxx.fileProvider"
3、authorities不要跟主项目有冲突即可

(XX xx 表示开发者任意填写)

继续阅读 »

1、新建XXFileProvider继承自FileProvider
2、注册改为<provider android:name="xxx.xx.xx.XXFileProvider"
android:authorities="${applicationId}.xxx.fileProvider"
3、authorities不要跟主项目有冲突即可

(XX xx 表示开发者任意填写)

收起阅读 »