HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

2

2

2

1

1

1

uniCloud

uniCloud

背景和优势
20年前,restful接口开发开始流行,服务器编写接口,客户端调用接口,传输json。

现在,替代restful的新模式来了。

云对象,服务器编写API,客户端调用API,不再开发传输json的接口。思路更清晰、代码更精简。

比如服务端编写一个云对象todo,该对象有add、get、remove、update等方法。客户端的js则可以直接import这个todo云对象,直接调用add等方法。

服务器示例代码如下:

HBuilderX中在uniCloud/cloudfunctions目录新建云函数,选择类型为云对象,起名为todo。打开云对象入口index.obj.js,添加一个add方法。

// 云对象名:todo
module.exports = {
add(title, content) {
title = title.trim()
content = content.trim()
if(!title || !content) {
return {
errCode: 'INVALID_TODO',
errMsg: 'TODO标题或内容不可为空'
}
}
// ...其他逻辑
return {
errCode: 0,
errMsg: '创建成功'
}
}
}
复制代码
然后在客户端的js中,import这个todo对象,调用它的add方法

const todo = uniCloud.importObject('todo') //第一步导入云对象
async function addTodo () {
try {
const res = await todo.add('title demo', 'content demo') //导入云对象后就可以直接调用该对象的方法了,注意使用异步await
uni.showToast({
title: '创建成功'
})
} catch (e) {
// 符合uniCloud响应体规范 https://doc.dcloud.net.cn/uniCloud/cf-functions?id=resformat,自动抛出此错误
uni.showModal({
title: '创建失败',
content: e.errMsg,
showCancel: false
})
}
}
复制代码
可以看到云对象的代码非常清晰,代码行数也只有33行。

而同样的逻辑,使用传统的接口方式则需要更多代码,见下:

// 传统方式调用云函数-云函数代码
// 云函数名:todo
// 云函数入口index.js内容如下
'use strict';
exports.main = async (event, context) => {
const {
method,
params
} = event
switch(method) {
case 'add': {
let {
title,
content
} = params
title = title.trim()
content = content.trim()
if(!title || !content) {
return {
errCode: 'INVALID_TODO',
errMsg: 'TODO标题或内容不可为空'
}
}
// ...省略其他逻辑
return {
errCode: 0,
errMsg: '创建成功'
}
}
}
return {
errCode: 'METHOD_NOT_FOUND',
errMsg: Method[${method}] not found
}
};

// 传统方式调用云函数-客户端代码
async function addToDo () {
try {
const res = await uniCloud.callFunction({
name: 'todo',
data: {
method: 'add',
params: {
title: 'title demo',
content: 'content demo'
}
}
})
const {
errCode,
errMsg
} = res.result
if(errCode) {
uni.showModal({
title: '创建失败',
content: errMsg,
showCancel: false
})
return
}
uni.showToast({
title: '创建成功'
})
} catch (e) {
uni.showModal({
title: '创建失败',
content: e.message,
showCancel: false
})
}
}
复制代码
以上传统开发需要68行代码,对比云对象的33行代码,不但工作量大,而且逻辑也不如云对象清晰。

_注:以上例子仅用于方便初学者理解。实际开发中对于简单的数据库操作,使用clientDB在前端直接操作数据库是更简单、代码更少的方案,都不需要写云端代码。

总结下云对象带来的好处:

更清晰的逻辑
更精简的代码
更少的协作成本(以及矛盾~)
客户端调用时在ide里有完善的代码提示,方法参数均可提示。(传输json可没法在ide里提示)
默认支持uniCloud响应体规范,方便错误拦截和统一处理
快速上手
创建云对象
云对象,其实是对云函数的封装。和创建云函数一样,在uniCloud/cloudfunctions目录右键新建云函数,选择云对象类型,输入云对象名称创建云对象,此处以云对象todo为例,创建的云对象包含一个index.obj.js。

创建云对象

一个空的云对象内容如下

// cloudfunctions/todo/index.obj.js
module.exports = {

}
复制代码
默认云对象模板是不包含任何方法的,我们为此对象添加一个add方法作为示例。

// cloudfunctions/todo/index.obj.js
module.exports = {
add: function(title = '', content = '') {
title = title.trim()
content = content.trim()
if(!title || !content) {
return {
errCode: 'INVALID_TODO',
errMsg: 'TODO标题或内容不可为空'
}
}
// ...其他逻辑,如操作todo数据表添加数据
return {
errCode: 0,
errMsg: '创建成功'
}
}
}
复制代码
至此云对象todo已经有了一个可以访问的方法了。接下来看如何使用客户端调用此云对象内的方法

客户端调用
客户端通过uniCloud.importObject方法获取云对象的实例,并可以通过此实例调用云对象内的方法。用法如下

const todo = uniCloud.importObject('todo')
const res = await todo.add('title demo', 'content demo')
复制代码
通过代码块cco可以快捷的输入以下代码:

const todo = uniCloud.importObject('todo')
复制代码
实际业务中需要考虑错误捕获,调用方式有两种:

try catch
const todo = uniCloud.importObject('todo')
try {
const res = await todo.add('title demo', 'content demo') //导入云对象后就可以直接调用该对象的方法了,注意使用异步await
console.log(res)
} catch (e) {
console.log(e.errCode)
console.log(e.errMsg)
}
复制代码
then catch(promise写法)
const todo = uniCloud.importObject('todo')
todo.add('title demo', 'content demo').then(res => {
console.log(res)
}).catch(e => {
console.log(e.errCode)
console.log(e.errMsg)
})
复制代码
importObject参数说明

interface ImportObjectOptions {
/**

  • 是否移除自动展示的ui
    */
    customUI?: boolean;
    /**
  • loading界面配置
    */
    loadingOptions?: ImportObjectLoadingOptions;
    /**
  • 错误提示配置
    */
    errorOptions?: ImportObjectErrorOptions;
    /**
  • 使用安全网络的方法及安全网络类型
    */
    secretMethods?: Record<string, 'none' | 'request' | 'response' | 'both'>;
    /**
  • 转化云对象内未捕获的错误或客户端网络错误
    */
    parseSystemError?: (params: ParseSystemErrorParams) => Promise<ParsedSystemError> | ParsedSystemError;
    }
    复制代码
    云对象的API
    云对象作为云函数的一种,可以调用所有node的API和uniCloud的API。

uniCloud有众多API,另见:uniCloud的API清单

除上述API之外,云对象的this对象还有一批专用方法来获取当前请求的上下文信息。

与云函数入参时的context不同,云对象没有context。它通过this对象挂载的几个内置方法来获取上下文信息。请注意开发者避免在this上挂载同名方法。

获取客户端信息
HBuilderX 3.4.9起此接口可获取所有客户端getSystemInfo返回的客户端信息,完整字段列表参考:getSystemInfo

接口形式

this.getClientInfo()

示例:

module.exports = {
add: function() {
const clientInfo = this.getClientInfo()
// clientInfo = {
// clientIP,
// appId,
// deviceId,
// source,
// //... 其他getSystemInfoSync返回值
// }
}
}
复制代码
返回值

getClientInfo返回的信息,是在客户端的uni.getSystemInfo的基础之上,增加了一些额外的信息。

除了getSystemInfo返回字段外,还包含以下信息

属性名 类型 说明
clientIP string 客户端ip
userAgent string 客户端ua,注意非本地运行环境下客户端getSystemInfoSync也会获取ua参数并上传给云对象,但是云对象会从http请求头里面获取ua而不是clientInfo里面的ua
source string 调用来源,返回值见下。HBuilderX 3.5.1+
requestId string 请求id。HBuilderX 3.5.1+
scene string 场景值。客户端uni.getLaunchOptionsSync返回的scene参数,新增于HBuilderX 3.5.1
getClientInfo().source,返回云函数调用来源,它的值域为:

取值 说明
client uni-app客户端导入云对象调用
function 由其他云函数或云对象调用
http 云对象URL化后通过http访问调用 HBuilderX 3.5.2+
timing 定时任务调用云对象 HBuilderX 3.5.2+
server 云函数上传并运行
注意事项

客户端上报的信息在理论上存在被篡改可能,实际业务中应验证前端传来的数据的合法性
除了clientIP外,其他客户端信息只有使用uni-app客户端以云对象的方式调用才能获取
云对象与云函数内获取客户端platform稍有不同,云函数未拉齐vue2、vue3版本app平台的platform值,vue2为app-plus,vue3为app。云对象无论客户端是vue2还是vue3,在app平台获取的platform均为app。这一点在使用uni-id时需要特别注意,详情见:uni-id文档 preferedAppPlatform
获取云端信息
接口形式

this.getCloudInfo()

示例

继续阅读 »

背景和优势
20年前,restful接口开发开始流行,服务器编写接口,客户端调用接口,传输json。

现在,替代restful的新模式来了。

云对象,服务器编写API,客户端调用API,不再开发传输json的接口。思路更清晰、代码更精简。

比如服务端编写一个云对象todo,该对象有add、get、remove、update等方法。客户端的js则可以直接import这个todo云对象,直接调用add等方法。

服务器示例代码如下:

HBuilderX中在uniCloud/cloudfunctions目录新建云函数,选择类型为云对象,起名为todo。打开云对象入口index.obj.js,添加一个add方法。

// 云对象名:todo
module.exports = {
add(title, content) {
title = title.trim()
content = content.trim()
if(!title || !content) {
return {
errCode: 'INVALID_TODO',
errMsg: 'TODO标题或内容不可为空'
}
}
// ...其他逻辑
return {
errCode: 0,
errMsg: '创建成功'
}
}
}
复制代码
然后在客户端的js中,import这个todo对象,调用它的add方法

const todo = uniCloud.importObject('todo') //第一步导入云对象
async function addTodo () {
try {
const res = await todo.add('title demo', 'content demo') //导入云对象后就可以直接调用该对象的方法了,注意使用异步await
uni.showToast({
title: '创建成功'
})
} catch (e) {
// 符合uniCloud响应体规范 https://doc.dcloud.net.cn/uniCloud/cf-functions?id=resformat,自动抛出此错误
uni.showModal({
title: '创建失败',
content: e.errMsg,
showCancel: false
})
}
}
复制代码
可以看到云对象的代码非常清晰,代码行数也只有33行。

而同样的逻辑,使用传统的接口方式则需要更多代码,见下:

// 传统方式调用云函数-云函数代码
// 云函数名:todo
// 云函数入口index.js内容如下
'use strict';
exports.main = async (event, context) => {
const {
method,
params
} = event
switch(method) {
case 'add': {
let {
title,
content
} = params
title = title.trim()
content = content.trim()
if(!title || !content) {
return {
errCode: 'INVALID_TODO',
errMsg: 'TODO标题或内容不可为空'
}
}
// ...省略其他逻辑
return {
errCode: 0,
errMsg: '创建成功'
}
}
}
return {
errCode: 'METHOD_NOT_FOUND',
errMsg: Method[${method}] not found
}
};

// 传统方式调用云函数-客户端代码
async function addToDo () {
try {
const res = await uniCloud.callFunction({
name: 'todo',
data: {
method: 'add',
params: {
title: 'title demo',
content: 'content demo'
}
}
})
const {
errCode,
errMsg
} = res.result
if(errCode) {
uni.showModal({
title: '创建失败',
content: errMsg,
showCancel: false
})
return
}
uni.showToast({
title: '创建成功'
})
} catch (e) {
uni.showModal({
title: '创建失败',
content: e.message,
showCancel: false
})
}
}
复制代码
以上传统开发需要68行代码,对比云对象的33行代码,不但工作量大,而且逻辑也不如云对象清晰。

_注:以上例子仅用于方便初学者理解。实际开发中对于简单的数据库操作,使用clientDB在前端直接操作数据库是更简单、代码更少的方案,都不需要写云端代码。

总结下云对象带来的好处:

更清晰的逻辑
更精简的代码
更少的协作成本(以及矛盾~)
客户端调用时在ide里有完善的代码提示,方法参数均可提示。(传输json可没法在ide里提示)
默认支持uniCloud响应体规范,方便错误拦截和统一处理
快速上手
创建云对象
云对象,其实是对云函数的封装。和创建云函数一样,在uniCloud/cloudfunctions目录右键新建云函数,选择云对象类型,输入云对象名称创建云对象,此处以云对象todo为例,创建的云对象包含一个index.obj.js。

创建云对象

一个空的云对象内容如下

// cloudfunctions/todo/index.obj.js
module.exports = {

}
复制代码
默认云对象模板是不包含任何方法的,我们为此对象添加一个add方法作为示例。

// cloudfunctions/todo/index.obj.js
module.exports = {
add: function(title = '', content = '') {
title = title.trim()
content = content.trim()
if(!title || !content) {
return {
errCode: 'INVALID_TODO',
errMsg: 'TODO标题或内容不可为空'
}
}
// ...其他逻辑,如操作todo数据表添加数据
return {
errCode: 0,
errMsg: '创建成功'
}
}
}
复制代码
至此云对象todo已经有了一个可以访问的方法了。接下来看如何使用客户端调用此云对象内的方法

客户端调用
客户端通过uniCloud.importObject方法获取云对象的实例,并可以通过此实例调用云对象内的方法。用法如下

const todo = uniCloud.importObject('todo')
const res = await todo.add('title demo', 'content demo')
复制代码
通过代码块cco可以快捷的输入以下代码:

const todo = uniCloud.importObject('todo')
复制代码
实际业务中需要考虑错误捕获,调用方式有两种:

try catch
const todo = uniCloud.importObject('todo')
try {
const res = await todo.add('title demo', 'content demo') //导入云对象后就可以直接调用该对象的方法了,注意使用异步await
console.log(res)
} catch (e) {
console.log(e.errCode)
console.log(e.errMsg)
}
复制代码
then catch(promise写法)
const todo = uniCloud.importObject('todo')
todo.add('title demo', 'content demo').then(res => {
console.log(res)
}).catch(e => {
console.log(e.errCode)
console.log(e.errMsg)
})
复制代码
importObject参数说明

interface ImportObjectOptions {
/**

  • 是否移除自动展示的ui
    */
    customUI?: boolean;
    /**
  • loading界面配置
    */
    loadingOptions?: ImportObjectLoadingOptions;
    /**
  • 错误提示配置
    */
    errorOptions?: ImportObjectErrorOptions;
    /**
  • 使用安全网络的方法及安全网络类型
    */
    secretMethods?: Record<string, 'none' | 'request' | 'response' | 'both'>;
    /**
  • 转化云对象内未捕获的错误或客户端网络错误
    */
    parseSystemError?: (params: ParseSystemErrorParams) => Promise<ParsedSystemError> | ParsedSystemError;
    }
    复制代码
    云对象的API
    云对象作为云函数的一种,可以调用所有node的API和uniCloud的API。

uniCloud有众多API,另见:uniCloud的API清单

除上述API之外,云对象的this对象还有一批专用方法来获取当前请求的上下文信息。

与云函数入参时的context不同,云对象没有context。它通过this对象挂载的几个内置方法来获取上下文信息。请注意开发者避免在this上挂载同名方法。

获取客户端信息
HBuilderX 3.4.9起此接口可获取所有客户端getSystemInfo返回的客户端信息,完整字段列表参考:getSystemInfo

接口形式

this.getClientInfo()

示例:

module.exports = {
add: function() {
const clientInfo = this.getClientInfo()
// clientInfo = {
// clientIP,
// appId,
// deviceId,
// source,
// //... 其他getSystemInfoSync返回值
// }
}
}
复制代码
返回值

getClientInfo返回的信息,是在客户端的uni.getSystemInfo的基础之上,增加了一些额外的信息。

除了getSystemInfo返回字段外,还包含以下信息

属性名 类型 说明
clientIP string 客户端ip
userAgent string 客户端ua,注意非本地运行环境下客户端getSystemInfoSync也会获取ua参数并上传给云对象,但是云对象会从http请求头里面获取ua而不是clientInfo里面的ua
source string 调用来源,返回值见下。HBuilderX 3.5.1+
requestId string 请求id。HBuilderX 3.5.1+
scene string 场景值。客户端uni.getLaunchOptionsSync返回的scene参数,新增于HBuilderX 3.5.1
getClientInfo().source,返回云函数调用来源,它的值域为:

取值 说明
client uni-app客户端导入云对象调用
function 由其他云函数或云对象调用
http 云对象URL化后通过http访问调用 HBuilderX 3.5.2+
timing 定时任务调用云对象 HBuilderX 3.5.2+
server 云函数上传并运行
注意事项

客户端上报的信息在理论上存在被篡改可能,实际业务中应验证前端传来的数据的合法性
除了clientIP外,其他客户端信息只有使用uni-app客户端以云对象的方式调用才能获取
云对象与云函数内获取客户端platform稍有不同,云函数未拉齐vue2、vue3版本app平台的platform值,vue2为app-plus,vue3为app。云对象无论客户端是vue2还是vue3,在app平台获取的platform均为app。这一点在使用uni-id时需要特别注意,详情见:uni-id文档 preferedAppPlatform
获取云端信息
接口形式

this.getCloudInfo()

示例

收起阅读 »

2

2

2

从 3 天开发到收到苹果 4.3:一个 AI 项目的上架翻车记录

苹果审核

从 3 天开发到收到苹果 4.3:一个 AI 项目的上架翻车记录

最近接到一个客户的项目。

项目很简单。

客户使用 Cursor 配合 ChatGPT 开发,前后只用了不到一周时间,一个 AI 工具类 APP 就已经完成。

页面漂亮,功能正常,测试也没有发现明显问题。

客户原本以为提交审核后几天就能上线。

结果收到的第一条消息却是:

Guideline 4.3 - Design - Spam

很多开发者看到这里都会有一个疑问:

同样是 AI 项目,为什么别人能过,而自己却被判定为重复内容?

实际上问题往往不是出在 AI,而是出在开发过程中那些容易被忽略的细节。

这类项目通常长什么样?

我们拆包分析后发现,很多 AI 生成项目都有一个共同特点。

首页是聊天界面。

第二页是模型列表。

第三页是会员中心。

第四页是个人中心。

换句话说,虽然 APP 名称不同,但整体结构几乎一致。

今天叫 AI 助手。

明天叫 AI 创作。

后天叫 AI 写作。

从用户角度看可能是不同产品。

但从苹果审核角度看,它们更像是同一个产品的多个版本。

这也是很多开发者第一次收到 4.3 的主要原因。

苹果真的会检测代码吗?

很多人认为苹果审核员只是打开 APP 看几分钟。

实际上远不止如此。

一个 IPA 上传后,苹果首先进行自动化分析。

检测内容通常包括:

  • Bundle 信息
  • 资源文件
  • Framework 依赖
  • 页面结构
  • 类名与方法名
  • 权限申请情况
  • 历史提交记录

特别是 AI 生成代码。

由于大量开发者使用相同提示词生成项目,因此经常会出现类似情况:

HomePage
ChatPage
MinePage
VipPage

甚至整个目录结构都高度一致。

虽然用户看不到这些内容,但审核系统是可以分析到的。

UI 改了为什么还是收到 4.3?

这是很多人踩过的坑。

收到 4.3 后第一反应通常是:

换图标。

换配色。

换截图。

换启动页。

然后重新提交。

结果还是被拒。

原因很简单。

苹果审核从来不是只看 UI。

很多项目表面已经改头换面。

但实际上:

功能没变。

代码没变。

业务逻辑没变。

用户路径没变。

审核员看到后仍然会认为属于重复内容。

一个真实案例

去年有个 AI 写作项目。

第一次提交时:

功能包括:

  • AI 对话
  • AI 写作
  • AI 翻译

结果收到 4.3。

后来我们重新梳理产品定位。

保留 AI 能力的同时增加:

  • 写作素材库
  • 行业模板中心
  • 历史创作管理
  • 文案分类系统
  • 每日创作任务

同时重新调整页面结构。

将聊天入口从首页移动到二级页面。

增加内容运营模块。

重新设计会员体系。

第二次提交后顺利通过审核。

整个过程中代码修改量并不算大。

真正变化的是产品定位。

AI 项目最容易忽略的问题

很多开发者把重点放在代码上。

实际上苹果更关注的是产品本身。

如果审核员打开 APP 后发现:

第一页聊天。

第二页会员。

第三页个人中心。

无论你用的是 AI 写的代码,还是程序员手写的代码,最终都会面临同样的问题。

因为苹果审核的是产品,而不是开发工具。

我的建议

如果准备使用 AI 开发 APP 上架 App Store,可以把 AI 当成开发助手,而不是产品经理。

代码可以让 AI 写。

页面可以让 AI 画。

但是产品定位一定要自己设计。

在提交审核之前,至少要回答下面三个问题:

第一,这个 APP 解决什么问题?

第二,它和市场上已有产品有什么区别?

第三,用户为什么要下载它而不是别人?

如果这三个问题答不出来。

即使没有收到 4.3,也只是时间问题。

写在最后

很多人觉得 AI 降低了开发门槛。

这一点没错。

但同时也降低了同质化产品出现的门槛。

过去做一个 APP 需要一个团队。

现在一个人加一套 AI 工具就能完成。

这也是为什么近两年苹果对于 4.3 的审核越来越严格。

被拒的原因从来不是因为用了 AI。

而是因为太多人用 AI 做出了相似的产品。

真正能通过审核的项目,往往不是代码写得最复杂的,而是定位最清晰、差异化最明显的。

继续阅读 »

从 3 天开发到收到苹果 4.3:一个 AI 项目的上架翻车记录

最近接到一个客户的项目。

项目很简单。

客户使用 Cursor 配合 ChatGPT 开发,前后只用了不到一周时间,一个 AI 工具类 APP 就已经完成。

页面漂亮,功能正常,测试也没有发现明显问题。

客户原本以为提交审核后几天就能上线。

结果收到的第一条消息却是:

Guideline 4.3 - Design - Spam

很多开发者看到这里都会有一个疑问:

同样是 AI 项目,为什么别人能过,而自己却被判定为重复内容?

实际上问题往往不是出在 AI,而是出在开发过程中那些容易被忽略的细节。

这类项目通常长什么样?

我们拆包分析后发现,很多 AI 生成项目都有一个共同特点。

首页是聊天界面。

第二页是模型列表。

第三页是会员中心。

第四页是个人中心。

换句话说,虽然 APP 名称不同,但整体结构几乎一致。

今天叫 AI 助手。

明天叫 AI 创作。

后天叫 AI 写作。

从用户角度看可能是不同产品。

但从苹果审核角度看,它们更像是同一个产品的多个版本。

这也是很多开发者第一次收到 4.3 的主要原因。

苹果真的会检测代码吗?

很多人认为苹果审核员只是打开 APP 看几分钟。

实际上远不止如此。

一个 IPA 上传后,苹果首先进行自动化分析。

检测内容通常包括:

  • Bundle 信息
  • 资源文件
  • Framework 依赖
  • 页面结构
  • 类名与方法名
  • 权限申请情况
  • 历史提交记录

特别是 AI 生成代码。

由于大量开发者使用相同提示词生成项目,因此经常会出现类似情况:

HomePage
ChatPage
MinePage
VipPage

甚至整个目录结构都高度一致。

虽然用户看不到这些内容,但审核系统是可以分析到的。

UI 改了为什么还是收到 4.3?

这是很多人踩过的坑。

收到 4.3 后第一反应通常是:

换图标。

换配色。

换截图。

换启动页。

然后重新提交。

结果还是被拒。

原因很简单。

苹果审核从来不是只看 UI。

很多项目表面已经改头换面。

但实际上:

功能没变。

代码没变。

业务逻辑没变。

用户路径没变。

审核员看到后仍然会认为属于重复内容。

一个真实案例

去年有个 AI 写作项目。

第一次提交时:

功能包括:

  • AI 对话
  • AI 写作
  • AI 翻译

结果收到 4.3。

后来我们重新梳理产品定位。

保留 AI 能力的同时增加:

  • 写作素材库
  • 行业模板中心
  • 历史创作管理
  • 文案分类系统
  • 每日创作任务

同时重新调整页面结构。

将聊天入口从首页移动到二级页面。

增加内容运营模块。

重新设计会员体系。

第二次提交后顺利通过审核。

整个过程中代码修改量并不算大。

真正变化的是产品定位。

AI 项目最容易忽略的问题

很多开发者把重点放在代码上。

实际上苹果更关注的是产品本身。

如果审核员打开 APP 后发现:

第一页聊天。

第二页会员。

第三页个人中心。

无论你用的是 AI 写的代码,还是程序员手写的代码,最终都会面临同样的问题。

因为苹果审核的是产品,而不是开发工具。

我的建议

如果准备使用 AI 开发 APP 上架 App Store,可以把 AI 当成开发助手,而不是产品经理。

代码可以让 AI 写。

页面可以让 AI 画。

但是产品定位一定要自己设计。

在提交审核之前,至少要回答下面三个问题:

第一,这个 APP 解决什么问题?

第二,它和市场上已有产品有什么区别?

第三,用户为什么要下载它而不是别人?

如果这三个问题答不出来。

即使没有收到 4.3,也只是时间问题。

写在最后

很多人觉得 AI 降低了开发门槛。

这一点没错。

但同时也降低了同质化产品出现的门槛。

过去做一个 APP 需要一个团队。

现在一个人加一套 AI 工具就能完成。

这也是为什么近两年苹果对于 4.3 的审核越来越严格。

被拒的原因从来不是因为用了 AI。

而是因为太多人用 AI 做出了相似的产品。

真正能通过审核的项目,往往不是代码写得最复杂的,而是定位最清晰、差异化最明显的。

收起阅读 »

1

1

1

图片URL格式化工具

对图片相对路径自动拼接CDN域名,富文本图片批量处理 HTML实体解码

详细描述:## 功能

  1. formatImage(url) — 智能补全图片地址

    • 完整 http/https 地址原样返回
    • 协议相对地址 //xxx.com 转为 https://xxx.com
    • 相对路径自动拼接 CDN 域名
  2. formatRichText(html) — 富文本处理

    • 批量替换图片 src 为完整 CDN 地址
    • 自动添加图片自适应样式(max-width:100%)
    • 解码 HTML 实体(& < > 等)

适用场景

  • 商品详情、文章内容的富文本展示
  • 用户头像、商品图片等资源路径补全
  • 后端返回的图片相对路径转完整 URL

使用示例


import { setImageOrigin, formatImage } from '@/utils/image'  

// 1. 在 App.vue 中配置 CDN 域名  
setImageOrigin('https://cdn.example.com')  

// 2. 格式化图片  
formatImage('/uploads/photo.jpg')  
// → https://cdn.example.com/uploads/photo.jpg  

// 3. 处理富文本  
formatRichText('<img src="/uploads/1.jpg" />')  

兼容性  
支持 uni-app 全平台(H5、iOS App、Android App、各小程序)  
继续阅读 »

对图片相对路径自动拼接CDN域名,富文本图片批量处理 HTML实体解码

详细描述:## 功能

  1. formatImage(url) — 智能补全图片地址

    • 完整 http/https 地址原样返回
    • 协议相对地址 //xxx.com 转为 https://xxx.com
    • 相对路径自动拼接 CDN 域名
  2. formatRichText(html) — 富文本处理

    • 批量替换图片 src 为完整 CDN 地址
    • 自动添加图片自适应样式(max-width:100%)
    • 解码 HTML 实体(& < > 等)

适用场景

  • 商品详情、文章内容的富文本展示
  • 用户头像、商品图片等资源路径补全
  • 后端返回的图片相对路径转完整 URL

使用示例


import { setImageOrigin, formatImage } from '@/utils/image'  

// 1. 在 App.vue 中配置 CDN 域名  
setImageOrigin('https://cdn.example.com')  

// 2. 格式化图片  
formatImage('/uploads/photo.jpg')  
// → https://cdn.example.com/uploads/photo.jpg  

// 3. 处理富文本  
formatRichText('<img src="/uploads/1.jpg" />')  

兼容性  
支持 uni-app 全平台(H5、iOS App、Android App、各小程序)  
收起阅读 »

4人团队全职接单,从广州回南宁创业,10年以上经验,时间充裕,可上门考察,直接技术一对一,求靠谱单子!

外包接单

【南宁本地·全栈开发团队】10年老兵返乡创业,4人全职,无销售套路,直接技术一对一!

✅ 我们能做: 小程序 · APP · 网站 · 公众号 · 后台管理系统 · 定制化企业软件
✅ 我们的底气: 前广州一线全栈,人均10年+开发经验,全部技术骨干出身
✅ 您的权益: 源码一次性全交付 · 后台不加密不锁死 · 同城可上门面谈,全国线上协作
✅ 特别说明: 初创期精力过剩,同行大量甩单也接,价格美丽

📍 南宁本地可考察,欢迎随时来坐坐
📱 微信:anma1001(加我备注“接单”,直接发需求)

继续阅读 »

【南宁本地·全栈开发团队】10年老兵返乡创业,4人全职,无销售套路,直接技术一对一!

✅ 我们能做: 小程序 · APP · 网站 · 公众号 · 后台管理系统 · 定制化企业软件
✅ 我们的底气: 前广州一线全栈,人均10年+开发经验,全部技术骨干出身
✅ 您的权益: 源码一次性全交付 · 后台不加密不锁死 · 同城可上门面谈,全国线上协作
✅ 特别说明: 初创期精力过剩,同行大量甩单也接,价格美丽

📍 南宁本地可考察,欢迎随时来坐坐
📱 微信:anma1001(加我备注“接单”,直接发需求)

收起阅读 »

iOS 性能监控与调试工具选择 整合iOS性能监控和调试

iOS

'''开发同学应该都遇到过这种情况:排查 App 性能问题时,Mac 上开着 Instruments 跑耗时分析,Xcode 控制台挂着看日志输出,再用 iMazing 导一份沙箱文件出来——几个工具来回切,一套流程走下来半天就过去了。这还只是性能排查,如果还要分析崩溃日志、检查文件完整性、解密 App 数据,工具链还会拉得更长,每个工具都有自己的连接方式和操作逻辑。

常见方案:各司其职,但互不连通

Instruments 是苹果官方的性能分析工具,Time Profiler 查 CPU 热点、Allocations 看内存分配、GPU Driver 看渲染性能,每个模板都对应一个独立场景。工具本身功能强大,但启动慢、调试流程偏重,适合做深度优化时用,日常快速查看性能数据显得有些笨重。

Xcode Console 配合 OSLog 能输出 App 运行时日志,但日志量大时筛选过滤不太顺手,也没法保留长时间的历史记录。Crash 日志分析依赖 symbolicatecrash 脚本或者第三方平台,手动符号化操作比较繁琐。

文件管理这块,iMazing 和爱思助手能查看 iOS 设备的文件系统,但主要面向媒体文件备份和传输,对 App 沙箱目录的访问能力有限。要提取 App 运行时生成的数据文件,通常还需要越狱配合。

KeyMob:整合调试流程的另一种选择

KeyMob 把这几类需求集成到了一个工具里。性能监控面板能实时显示 CPU、GPU、内存占用和 FPS 帧率,数据以图表形式展示,时间线上的波动一目了然。卡顿检测会记录掉帧时的堆栈信息,不用像用 Instruments 那样先配置再重现。跑微信小程序或者 Unity3D 开发的 App 时同样能监控,兼容性覆盖得比较全。另外还能查看设备长达六个月的使用记录,每个 App 的启动时间、运行时长和电量消耗都有据可查。

日志查看功能可以按进程和 App 名称过滤输出,定位问题比在 Xcode Console 翻原始输出更直接。崩溃日志模块能直接导出设备的 crash report,自动完成符号化——设备连上电脑、选中日志、点击导出,三步操作就行,不需要跑 symbolicatecrash 命令。

文件管理部分可以浏览 App 的沙箱目录结构,把配置文件和数据缓存拖到电脑本地分析。解密功能提取 App 运行中保存的图片、音频和配置文件,查看数据存储格式和内容比较方便。USB 连接即可操作,不需要越狱。App 管理功能还能直接安装、卸载应用,查看进程信息和网络请求状态。

选择思路

Instruments 在深度性能分析上仍然不可替代。日常调试和快速排查场景下,KeyMob 把几个高频需求整合到一起,能减少工具间切换的成本。可以根据项目阶段和调试深度来搭配使用,各取所长就行。'''

继续阅读 »

'''开发同学应该都遇到过这种情况:排查 App 性能问题时,Mac 上开着 Instruments 跑耗时分析,Xcode 控制台挂着看日志输出,再用 iMazing 导一份沙箱文件出来——几个工具来回切,一套流程走下来半天就过去了。这还只是性能排查,如果还要分析崩溃日志、检查文件完整性、解密 App 数据,工具链还会拉得更长,每个工具都有自己的连接方式和操作逻辑。

常见方案:各司其职,但互不连通

Instruments 是苹果官方的性能分析工具,Time Profiler 查 CPU 热点、Allocations 看内存分配、GPU Driver 看渲染性能,每个模板都对应一个独立场景。工具本身功能强大,但启动慢、调试流程偏重,适合做深度优化时用,日常快速查看性能数据显得有些笨重。

Xcode Console 配合 OSLog 能输出 App 运行时日志,但日志量大时筛选过滤不太顺手,也没法保留长时间的历史记录。Crash 日志分析依赖 symbolicatecrash 脚本或者第三方平台,手动符号化操作比较繁琐。

文件管理这块,iMazing 和爱思助手能查看 iOS 设备的文件系统,但主要面向媒体文件备份和传输,对 App 沙箱目录的访问能力有限。要提取 App 运行时生成的数据文件,通常还需要越狱配合。

KeyMob:整合调试流程的另一种选择

KeyMob 把这几类需求集成到了一个工具里。性能监控面板能实时显示 CPU、GPU、内存占用和 FPS 帧率,数据以图表形式展示,时间线上的波动一目了然。卡顿检测会记录掉帧时的堆栈信息,不用像用 Instruments 那样先配置再重现。跑微信小程序或者 Unity3D 开发的 App 时同样能监控,兼容性覆盖得比较全。另外还能查看设备长达六个月的使用记录,每个 App 的启动时间、运行时长和电量消耗都有据可查。

日志查看功能可以按进程和 App 名称过滤输出,定位问题比在 Xcode Console 翻原始输出更直接。崩溃日志模块能直接导出设备的 crash report,自动完成符号化——设备连上电脑、选中日志、点击导出,三步操作就行,不需要跑 symbolicatecrash 命令。

文件管理部分可以浏览 App 的沙箱目录结构,把配置文件和数据缓存拖到电脑本地分析。解密功能提取 App 运行中保存的图片、音频和配置文件,查看数据存储格式和内容比较方便。USB 连接即可操作,不需要越狱。App 管理功能还能直接安装、卸载应用,查看进程信息和网络请求状态。

选择思路

Instruments 在深度性能分析上仍然不可替代。日常调试和快速排查场景下,KeyMob 把几个高频需求整合到一起,能减少工具间切换的成本。可以根据项目阶段和调试深度来搭配使用,各取所长就行。'''

收起阅读 »