PythonIDE Docs
中文
简体中文

原生能力入口

MiniApp 常用原生能力入口,不是完整模块总表。

主文档已合并:入口模型、Python 模块表、AppUI 原生组件入口表与侧边栏导航均由 schema 生成,请优先阅读 **iOS 原生能力**。本文仅保留历史深链,不再维护独立能力表。

MiniApp 场景下的原生能力配方:按任务选模块、组合调用、状态写回界面。

边界:这是 MiniApp 配方索引,不是可 import 的模块。完整 API 以各模块文档为准;系统调用放在按钮回调,不要写在 body() 里。

#模块概览

说明
导入按场景 import 对应模块(如 photosstorage
适合做什么选图、定位、通知、持久化、网络列表等 MiniApp 常见流
调用时机按钮、刷新、选择器触发;body() 只读 State
数据分工设置 storage、记录 database、密钥 keychain、ZIP/IPA 用 archive
反馈成功/取消/失败都写回 State 或 HUD

#快速开始

保存主题并触发触觉反馈:

python
import haptics
import storage

storage.set("demo.theme", "Dark")
if haptics.is_supported():
    haptics.notification("success")
print("已保存:", storage.get("demo.theme"))

#每个 MiniApp 的能力声明

新建或准备分发的 MiniApp 应在 miniapp.json 声明最小能力。字段缺失表示旧应用兼容 profile;字段存在且为 [] 表示明确不允许任何受控能力。已有 MiniApp 不需要改代码,新能力只做增量声明。

下面的字段、能力名和外部输入选择器直接来自 MiniApp contract;不要自行改成蛇形命名。

json
{
  "name": "External Input Tool",
  "entry": "main.py",
  "runtime": "appui",
  "capabilities": [
    "network",
    "file_system"
  ],
  "allowedNetworkHosts": [
    "api.example.com",
    "*.cdn.example.com"
  ],
  "externalInputs": {
    "kinds": [
      "file",
      "url"
    ],
    "fileExtensions": [
      "ipa"
    ],
    "urlSchemes": [
      "https"
    ],
    "urlHosts": [
      "apps.apple.com"
    ],
    "acceptsMultiple": true,
    "acceptsMixedKinds": false
  },
  "intentEntry": "handle_external.py"
}

#Manifest 字段

字段类型必填说明
capabilitiesarray<string> | omitted最小原生能力集合;缺失时使用旧应用兼容 profile,空数组表示不允许受控能力。
allowedNetworkHostsarray<string>声明式 MiniApp 可访问的 HTTPS/WSS 主机;*.example.com 仅匹配子域;最多 128 条且 UTF-8 总计不超过 32768 字节。
externalInputsobject声明此 MiniApp 可从系统分享页接收的内容;缺失或没有任何选择器时不会出现在可运行 MiniApp 列表。
intentEntrystring处理外部启动请求的可选 Python 入口;缺失时由常规 entry 接收。

#合法能力名

installed_appsexternal_navigationnetworkmedia_previewmediahapticsclipboard_readclipboard_writecameradevice_infostorageshared_storagefile_systemphotoshealthcontactslocationmotionbluetoothnfcmicrophonenotificationscalendaralarmsbiometricunsafe_native

#externalInputs 字段

可用 kindsfileurltextimageaudiovideodata

字段类型默认值说明
kindsarray<externalInputKind>[]允许的输入类别;file 可匹配除 URL 外的文件型内容。
contentTypesarray<string>[]允许的 Uniform Type Identifier,例如 public.image
fileExtensionsarray<string>[]允许的文件扩展名,不区分大小写,可写 ipa.ipa
urlSchemesarray<string>[]允许的 URL scheme,例如 https
urlHostsarray<string>[]允许的 URL 主机;*.example.com 仅匹配子域。
urlPathPrefixesarray<string>[]允许的 URL 路径前缀。
acceptsMultiplebooleanfalse是否允许一次接收多个项目。
acceptsMixedKindsbooleanfalse多项目时是否允许混合不同输入类别。

外部启动时用 miniapp_runtime.launch_request() 读取当前运行的只读请求;普通启动返回 NoneLaunchItem.open() 只接受只读模式,授权路径会在该次 MiniApp 运行结束时失效。读取这一条 host-issued 输入不要求 file_system;写输出或访问其他普通路径仍按模块能力规则检查。

#模块能力要求

模块必须全部声明至少声明一个本次外部输入只读例外
_ctypesunsafe_native--
alarmalarms--
archivefile_system-可读取准确的 LaunchItem.path
audio_recordermicrophone--
background_downloadnetworkfile_system-可读取准确的 LaunchItem.path
biometricbiometric--
ble_peripheralbluetooth--
bluetoothbluetooth--
calendar_eventscalendar--
cameracamera--
clipboard-clipboard_readclipboard_write-
contactscontacts--
ctypesunsafe_native--
databasestorage--
file_pickerfile_system--
healthhealth--
http_servernetworkfile_system--
keychainstorage--
locationlocation--
mailexternal_navigation--
messageexternal_navigation--
motionmotion--
musicmedia--
music_playermedia--
native_imagefile_system-可读取准确的 LaunchItem.path
networknetwork--
nfcnfc--
notificationnotifications--
photos-photoscamera-
shazammicrophonemedia--
shortcutsexternal_navigation--
speechmicrophone--
speech_recognitionmicrophone--
sshnetwork--
storagestorage--
video_recordercameramicrophone--
vision-photoscamera-
vision_helper-photoscamera-
weathernetwork--
websocketnetwork--

#分享页如何选择 MiniApp

系统分享页不会在多个候选中“随便打开第一个”。它会用每个已安装 MiniApp 的 externalInputs 对本次全部输入做匹配,并把所有匹配项显示为 MiniApp 卡片;用户点了哪一张卡片,请求就只绑定到哪一个 MiniApp。没有匹配项时仍可按普通导入/预览处理。

匹配规则是确定的:

  • kinds 未填写时由更具体的类型、扩展名或 URL 选择器决定;file 可接收除 URL 以外的文件型输入。
  • 对文件输入,contentTypes 使用 UTI 一致性/继承关系匹配,和 fileExtensions 之间是“任一匹配”;图片、音频和视频会按实际文件类型识别,不按分享传输层的泛型 public.file-url 误判。
  • 对 URL 输入,已填写的 urlSchemesurlHostsurlPathPrefixes 必须同时满足;*.example.com 不包含裸域 example.com
  • 每个输入都必须匹配;多项目还需 acceptsMultiple=true,混合类别还需 acceptsMixedKinds=true

分享扩展只负责暂存输入和记录用户选择。主 App 打开后会再次校验目标 MiniApp 仍存在、工作区没有切换、运行时支持外部请求且当前声明仍匹配;如果安装状态或声明已经变化,输入会安全回退为普通导入,不会误交给另一个 MiniApp,也不会因为目标失效直接丢失。

#miniapp_runtime 启动请求

普通启动时 launch_request() 返回 None;外部启动时返回非空、只读的 LaunchRequest。如果当前业务必须由外部输入启动,可使用 launch_request(required=True),缺少请求时会抛 LaunchRequestError

python
import miniapp_runtime

request = miniapp_runtime.launch_request(required=True)
item = request.first

print(request.id, request.source, request.target_miniapp_id)
print(item.kind, item.name, item.content_type, item.size)

if item.url:
    print("原始 URL:", item.url)
else:
    with item.open("rb") as stream:
        header = stream.read(64)
        print(header)
对象字段/方法说明
LaunchRequestid / source / created_at / target_miniapp_id本次请求、来源、创建时间与已选目标;对象不可修改。
LaunchRequestitems / first非空输入元组与第一项。
LaunchRequestfiles / urls本次受管文件路径与原始 URL 的只读元组。
LaunchItemid / kind / name / content_type / size输入的稳定元数据。
LaunchItempath / url宿主暂存路径;URL 输入还提供规范化原始 URL。
LaunchItemopen("rb") / open("r") / open("rt")只读打开;写入、追加和更新模式会被拒绝。
LaunchItemread_bytes(max_bytes=...) / read_text(...)有明确大小上限的便捷读取;大文件应使用 open() 流式处理。
模块函数has_launch_request()判断本次运行是否带外部请求。

intentEntry 只是在外部请求存在时选择另一份 Python 入口文件;请求内容仍统一从 miniapp_runtime 读取。未填写、文件名为空或普通启动时继续运行原 entry,因此不会改变旧 MiniApp 的启动写法。请求授权与当前运行票据绑定:运行结束后不要保存 LaunchItem.path 供下次使用;需要跨运行继续使用时,应在当前授权有效期间导入工作区,或交给会立即接管文件的宿主持久任务(例如 background_download.upload())。

#Server MiniApp 启动就绪握手

这组 API 只用于自己启动 HTTP 监听器的 Server MiniApp;AppUI、console、scene、ui 和 widget 不需要调用。旧 Server MiniApp 仍保留兼容启动路径,新代码建议在正常路由之前响应宿主为本次运行生成的健康请求,让宿主确认“当前端口确实属于这一代 Python 运行”,而不是误连上一次遗留的监听器。

python
import miniapp_runtime


def handle_http_request(path, headers):
    if miniapp_runtime.is_health_request(path, headers):
        # 返回 (JSON 字符串, 200, no-store + 本次运行 token header)
        return miniapp_runtime.health_response()

    return route_application_request(path, headers)
API语义
health_config()返回只读配置:enabledprotocolpathheadertoken。仅受管 Server 运行会启用。
is_health_request(path, headers=None)判断请求路径以及可用时的 token header 是否属于本次运行。框架能提供 header 时应一并传入。
health_payload()返回宿主期望的 JSON 可序列化字典。未启用时明确失败。
health_headers()返回 no-store、JSON content type 与本次 token header。
health_response()返回适合 Flask 类接口的 (body, status, headers) 三元组。

健康 token 只用于本次启动证明,不是业务 API 密钥;不要打印、持久化、返回给普通业务路由,也不要自己固定 path/header/token。运行结束后这些值失效。

其中 storage 只允许当前 MiniApp 的私有数据;使用 storage.shared 还需显式声明 shared_storage

宿主持久任务与普通运行资源是两类生命周期:Timer、普通网络请求、传感器和订阅在当前 session 停止时取消;后台文件传输由宿主任务中心继续持有。用 miniapp_runtime.durable_tasks() / durable_task() 查询自己的任务;provider="background_transfer" 标识当前 provider,记录中的 origin_session_id 指向创建运行。一旦换成新运行,origin_run_state 明确为 interrupted,表示旧 Python 调用栈没有恢复。传输的暂停、恢复、重试和取消使用 background_download

声明式 profile 的网络规则:

  • network 只表示允许发起网络操作,还必须匹配 allowedNetworkHosts
  • 只允许 HTTPS / WSS;*.example.com 匹配子域,不匹配根域 example.com
  • WebView.allowed_hosts 可以缩小 manifest 范围,不能扩大;Swift 会把 Python node/IR 当作不可信输入,用 native lease 中不可变的 manifest 重新求有效 host 集合,无效或越权输入 fail closed。导航、HTTPS/WSS 子资源、Cookie 与网站数据共用该集合,FTP/FTPS/自定义 hierarchical network scheme 默认阻断。
  • 导入受控模块、AppUI 系统组件和真正调用原生能力时都会经过同一策略并留下审计记录。
  • 每次运行有独立 session 与 generation;停止时会自动取消计时器、网络、WebSocket、传感器和订阅资源,并拒绝旧会话迟到事件。

权限声明不是系统授权的替代品:先通过 MiniApp 自身能力策略后,iOS 仍会在需要时显示系统权限界面。用户拒绝、设备不支持和主动取消都必须有可见失败路径。


#AppUI 示例

设备信息、快照、通知组合;所有原生调用在按钮回调里。

python
import appui
import device
import haptics
import notification
import storage

SNAPSHOT_KEY = "native.snapshot"
REMINDER_ID = "native.snapshot.reminder"

state = appui.State(
    message="就绪",
    rows=[
        {"id": "model", "title": "型号", "value": "点击刷新"},
        {"id": "battery", "title": "电量", "value": "—"},
    ],
)


def row_key(row):
    return row["id"]


def row_view(row):
    return appui.LabeledContent(row["title"], value=row["value"])


def refresh_device_info():
    state.rows = [
        {"id": "model", "title": "型号", "value": device.model()},
        {"id": "battery", "title": "电量", "value": f"{device.battery_level():.0%}"},
    ]
    state.message = "设备信息已刷新"


def save_snapshot():
    storage.set_json(SNAPSHOT_KEY, state.rows)
    if haptics.is_supported():
        haptics.notification("success")
    state.message = "快照已保存"


def remind_later():
    perm = notification.request_permission()
    if not perm.get("granted"):
        state.message = "未获得通知权限"
        return

    result = notification.schedule(
        REMINDER_ID,
        "原生快照",
        "查看已保存的设备快照",
        delay=60,
    )
    state.message = f"已调度提醒: {result}"


def body():
    return appui.NavigationStack(
        appui.List([
            appui.Section("设备", [
                appui.ForEach(state.rows, row_builder=row_view, key=row_key),
            ]),
            appui.Section("操作", [
                appui.Button("刷新设备", action=refresh_device_info),
                appui.Button("保存快照", action=save_snapshot)
                .button_style("bordered_prominent"),
                appui.Button("1 分钟后提醒", action=remind_later),
            ], footer=state.message),
        ]).navigation_title("原生能力")
    )


appui.run(body, state=state)

#API 参考

#能力入口

能力主文档典型用途
定位与地图location坐标、地理编码、指南针
相册与相机photos选图、拍照、保存
通知与实时活动notificationlive_activity提醒、角标、锁屏状态
持久化storagedatabasekeychain设置、记录、密钥
ZIP / IPAarchive原生检查、列表、事务化解压、确定性创建
设备与传感器devicemotion屏幕、电池、传感器
触觉与声音hapticssound反馈、提示音
网络networkwebsocketHTTP、实时连接
编辑器工具栏keyboard插入代码片段

#场景配方

目标推荐组合说明
选图并保存设置appui + photos + storage按钮触发 picker,路径写入 State
下载并存相册network + photos下载到本地再 save_video
地图展示位置permission + location + MapView先查权限再定位
健康面板permission + health + Chart拒绝时显示空状态
提醒任务notification + storage稳定 identifier 调度/取消
播放历史database + List大列表勿塞 storage
安全配置biometric + keychain + Form密钥进 keychain
传感器面板motion + haptics高频数据避免每帧重建 UI
远端列表network + List + .refreshable请求放刷新回调
IPA / ZIP 工具FileImporter + archive + List先检查/列表,再由按钮触发解压

#使用规则

  • 权限类能力先查状态,再请求。
  • 触觉、通知、声音不要在循环里连续触发。
  • 写代码前打开对应模块文档,按公开 Python API 调用。

#常见错误

错误写法后果修正
body() 里请求权限/发通知刷新时反复弹窗放进按钮回调
token 写入 storage 或日志泄露风险keychain
权限拒绝后空白页用户不知原因显示说明与重试按钮
用户取消选择未处理崩溃或脏状态判断 None 并保留原状态
网络失败清空旧数据体验差保留缓存并显示错误

#相关文档

文档用途
iOS 原生能力入口模型、模块表与权限矩阵
全部模块总览按分类浏览模块
运行时选择appui / scene / widget 选型
permission统一权限查询与申请
photos相册与相机
notification本地通知

#使用原则

  • 原生能力放在用户触发的回调里,不要写在 body() 构建期
  • 先查最小模块,再组合;避免一次 import 多个无关模块
  • 返回值、取消和拒绝都要给用户可见反馈

#失败路径

情况处理
权限拒绝提示去设置开启,并保留当前页面
设备不支持is_available() 或模块返回值判断
用户取消不要当作成功继续写数据

#预期效果

运行示例后,界面应出现文档描述的目标结果;若与预期不符,请按「失败路径」排查。