关于 ping-orchestration-ios-sdk
用于生成 Ping Orchestration iOS SDK 认证集成代码或完整 Xcode 示例应用的技能,支持 Journey、DaVinci 和 OIDC Web 流程,并可配置回调与收集器。
主要功能
- 01生成 Xcode 示例项目
- 02生成 SDK 集成文件
- 03配置 Journey、DaVinci 和 OIDC Web
- 04选择回调与收集器层级
- 05分析 Journey 导出 JSON
适用场景
- 01创建 iOS 认证示例应用
- 02为现有应用添加 Ping SDK 认证
- 03根据 Journey 导出文件定制回调视图
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ pingidentity/ ping-sdk-agent-skills --skill ping-orchestration-ios-sdk安装后在 Claude Code 中输入/ping-orchestration-ios-sdk确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/App.swift.davinci.templateApp.swift.journey.templateApp.swift.oidc.templateAppDaVinci.swift.templateAppJourney.swift.templateAppOidc.swift.templateAuthenticatedView.swift.davinci.templateAuthenticatedView.swift.journey.templateAuthenticatedView.swift.oidc.templateassets/callbacks/BooleanAttributeInputCallbackView.swift.templateChoiceCallbackView.swift.templateConfirmationCallbackView.swift.templateConsentMappingCallbackView.swift.templateDeviceBindingCallbackView.swift.templateDeviceProfileCallbackView.swift.templateDeviceSigningVerifierCallbackView.swift.templateFidoAuthenticationCallbackView.swift.templateFidoRegistrationCallbackView.swift.templateIdpCallbackView.swift.templateKbaCreateCallbackView.swift.templateNameCallbackView.swift.templateNumberAttributeInputCallbackView.swift.templatePasswordCallbackView.swift.templatePingOneProtectEvaluationCallbackView.swift.templatePingOneProtectInitializeCallbackView.swift.templatePollingWaitCallbackView.swift.templateReCaptchaEnterpriseCallbackView.swift.templateSelectIdpCallbackView.swift.templateStringAttributeInputCallbackView.swift.templateTermsAndConditionsCallbackView.swift.template详细介绍
技能参数
调用 /ping-orchestration-ios-sdk <args> 时,此技能接受一个可选的参数字符串。
无参数调用 — 向导模式
不带参数调用时(/ping-orchestration-ios-sdk),运行向导:
步骤 W1 — 确定意图,使用 AskUserQuestion:
"What would you like to do?"
Options:
A) Build a new sample app — scaffold a complete Xcode project with full Ping branding
B) Integrate into existing app — generate only the SDK files to drop into your project
C) Browse the reference guide — show the full SDK reference
D) Something else — let me describe what I need
- A / B → 步骤 W1b,然后是步骤 W2
- A:继续执行自动创建项目(读取
assets/project-scaffolding.md) - B:仅生成 SDK 集成文件(不搭建 Xcode 项目)
- A:继续执行自动创建项目(读取
- C → 从“Ping SDK for iOS — Integration Guide”开始显示参考指南。停止。
- D → 提出后续自由文本问题,然后据此分流。
步骤 W1b — 提供 Journey 导出选项(仅选项 A 和 B)。
询问:
“你有 Journey 导出的 JSON 文件吗?如果有,请粘贴或附上,我会进行分析,以生成一个能够恰好处理 Journey 所用回调的示例。”
- 如果已提供:在步骤 W2 之前运行第 13 节(Journey 导出分析)。根据分析结果预填
journeyName、callbackTier和serverUrl。 - 如果未提供:正常继续步骤 W2。
步骤 W2 — 收集配置(A 和 B 均为必需)。
如果尚未确定 flowType,先确定该值,然后在一次 AskUserQuestion 调用中仅询问与该流程相关的参数。存在默认值时显示默认值。所有必填字段都有值后才能继续。
通用参数(所有流程):
| 参数 | 必填 | 默认值 | 描述 |
|---|---|---|---|
appName | 是 | — | 用于 Swift 类型、PingHeaderView 标题和 bundle ID 的应用名称。例如:PingDemo |
outputPath | 是(仅选项 A) | — | 写入 Xcode 项目的目录(绝对路径) |
flowType | 是 | — | oidc-web · journey · davinci |
clientId | 是 | — | OAuth 2.0 客户端 ID |
redirectUri | 是 | — | OAuth 2.0 重定向 URI(自定义 scheme,例如 myapp://callback) |
discoveryEndpoint | 是 | — | 完整的 OIDC 发现 URL,以 /.well-known/openid-configuration 结尾 |
scopes | 否 | openid profile email | OAuth 2.0 作用域,以空格分隔 |
Journey 专属附加参数:
| 参数 | 必填 | 默认值 | 描述 |
|---|---|---|---|
serverUrl | 是 | — | PingAM/AIC 服务基础 URL(末尾不带 /) |
realm | 否 | alpha | 身份验证域 |
cookieName | 否 | iPlanetDirectoryPro | 会话 Cookie 名称 |
journeyName | 否 | Login | Journey 树名称 |
callbackTier | 是 | — | basic、standard 或 full(见第 12 节) |
DaVinci 专属附加参数:
| 参数 | 必填 | 默认值 | 描述 |
|---|---|---|---|
callbackTier | 是 | — | basic、standard 或 full(见第 12 节) |
acrValues | 否 | (无) | 可选的 ACR 值字符串——未提供时完全省略 |
OIDC Web 专属附加参数:
| 参数 | 必填 | 默认值 | 描述 |
|---|---|---|---|
acrValues | 否 | (无) | 可选的 ACR 值字符串——未提供时完全省略 |
提供 callbackTier 选项(仅适用于 Journey 和 DaVinci):
- Basic —
NameCallback、PasswordCallback、TextOutputCallback、ChoiceCallback(Journey)/TextCollector、PasswordCollector、SubmitCollector、LabelCollector、FlowCollector(DaVinci)。依赖项最少。 - Standard — 包含 Basic 的全部内容,另外还包括:属性回调、FIDO、Protect、设备绑定/配置文件、SelectIdp、条款与条件、KBA、PollingWait、ConsentMapping。不包含第三方社交 SDK。
- Full fat — 包含 Standard 的全部内容,另外还包括
IdpCallback/IdpCollector/SocialCollectorView(社交登录)和ReCaptchaEnterpriseCallback。需要 Facebook、Google 和 ReCaptcha Enterprise SPM 依赖项。 验证规则: redirectUri必须使用自定义 scheme(不能是http://或https://)。discoveryEndpoint必须以https://开头,并以/.well-known/openid-configuration结尾。如果用户提供的是基础 AM URL,请构造<serverUrl>/oauth2/<realm>/.well-known/openid-configuration并确认。scopes必须包含openid。如果缺少,请将其添加到开头并提醒用户。serverUrl(仅限 Journey)必须以https://开头,且末尾不能有/。
步骤 W3 — 确认并生成。 用简短表格汇总收集到的值,询问“准备生成了吗?这些信息看起来正确吗?”,然后在确认后继续。
参数(带参数调用)
| 参数 | 语法 | 用途 |
|---|---|---|
create-sample | create-sample "<description>" | 生成完整且可运行的示例应用 |
app-name | app-name "<name>" | 设置应用名称。如果省略,默认为 "MyApp"。 |
output-path | output-path "<path>" | 将文件写入磁盘。如果省略,则以内联方式输出。 |
create-sample "<description>"
-
分析描述——识别流程类型、模块以及隐含的屏幕。
-
提供 Journey 导出选项——如果流程类型是
journey(或可能是),请询问:"你有 Journey 导出 JSON 吗?我可以分析它,并根据 Journey 实际使用的回调定制示例。" 如果用户提供了文件,请在继续之前运行第 13 节。使用其输出确定
callbackTier和屏幕。 -
仅在流程类型确实含糊不清时,提出一个澄清问题。通过一次
AskUserQuestion收集已检测流程类型所需的参数(参见 W2 表格),并明确提供“我可以使用占位值”选项。 -
根据
app-name确定应用名称;如果未提供,则使用"MyApp"。 -
使用
assets/中的模板文件生成。替换所有PLACEHOLDER_*令牌(参见下表)。使用@Observable @MainActorViewModel——绝不要使用ObservableObject/@Published。应用 Ping Identity 品牌风格(第 11 节)。OIDC Web 流程——读取并替换:
App.swift.oidc.template、AppOidc.swift.template、OidcLoginViewModel.swift.template、ContentView.swift.oidc.template、LoginView.swift.oidc.template、AuthenticatedView.swift.oidc.template、Theme.swift.templateJourney 流程——读取并替换:
App.swift.journey.template、AppJourney.swift.template、JourneyLoginViewModel.swift.template、ContentView.swift.journey.template、LoginView.swift.journey.template、AuthenticatedView.swift.journey.template、ContinueNodeView.swift.journey.template、Theme.swift.template然后,对于
callbackTier中的每个回调,读取并替换assets/callbacks/中对应的文件:- 基础:
NameCallbackView、PasswordCallbackView、ChoiceCallbackView、TextOutputCallbackView - 标准: 包含基础级别的所有内容,另加
TextInputCallbackView、ValidatedUsernameCallbackView、ValidatedPasswordCallbackView、StringAttributeInputCallbackView、NumberAttributeInputCallbackView、BooleanAttributeInputCallbackView、ConfirmationCallbackView、TermsAndConditionsCallbackView、KbaCreateCallbackView、ConsentMappingCallbackView、PollingWaitCallbackView、SelectIdpCallbackView、FidoRegistrationCallbackView、FidoAuthenticationCallbackView、DeviceProfileCallbackView、DeviceBindingCallbackView、DeviceSigningVerifierCallbackView、PingOneProtectInitializeCallbackView、PingOneProtectEvaluationCallbackView - 完整: 包含标准级别的所有内容,另加
IdpCallbackView、ReCaptchaEnterpriseCallbackView
在
ContinueNodeView.swift.journey.template中,将// PLACEHOLDER_CALLBACK_CASES替换为适用于相应级别的switch分支,并将// PLACEHOLDER_TIER_IMPORTS替换为相应的 import 语句。将// PLACEHOLDER_SELF_ADVANCING_TYPES替换为该级别的自推进类型。 DaVinci 流程 — 读取并替换:App.swift.davinci.template、AppDaVinci.swift.template、DaVinciLoginViewModel.swift.template、ContentView.swift.davinci.template、LoginView.swift.davinci.template、AuthenticatedView.swift.davinci.template、CollectorNodeView.swift.davinci.template、Theme.swift.template然后,对于
callbackTier中的每个收集器,读取并替换assets/collectors/中匹配的文件:- 基础:
TextCollectorView、PasswordCollectorView、SubmitCollectorView、LabelCollectorView、FlowCollectorView - 标准: 包含基础级的所有内容,以及
MultiSelectCheckBoxView、MultiSelectComboBoxView、SingleSelectDropdownView、SingleSelectRadioView、PhoneNumberCollectorView、ProtectCollectorView、FidoRegistrationCollectorView、FidoAuthenticationCollectorView、DeviceRegistrationCollectorView、DeviceAuthenticationCollectorView - 完整版: 包含标准级的所有内容,以及
SocialCollectorView
在
CollectorNodeView.swift.davinci.template中,将// PLACEHOLDER_COLLECTOR_CASES替换为该级别对应的switch分支,并相应替换// PLACEHOLDER_TIER_IMPORTS和// PLACEHOLDER_SELF_ADVANCING_TYPES。 - 基础:
-
交付:
- 使用
output-path时:读取assets/project-scaffolding.md并严格遵循步骤 0–G。每种流程类型对应的 SPM 产品请参阅该文档中的步骤 C。 - 不使用
output-path时:以内联形式输出每个文件,并使用// --- <Filename>.swift ---标题。
- 使用
模板占位符
| 占位符 | 替换为 |
|---|---|
PLACEHOLDER_APP_NAME | 解析后的应用名称(例如 PingDemo) |
PLACEHOLDER_APP_NAME_UPPER | 全大写的应用名称(例如 PINGDEMO)——用于构造 Keychain 账户键 |
PLACEHOLDER_CLIENT_ID | clientId 值 |
PLACEHOLDER_REDIRECT_URI | redirectUri 值 |
PLACEHOLDER_DISCOVERY_ENDPOINT | discoveryEndpoint 值 |
PLACEHOLDER_SCOPES | 以逗号分隔并加引号的作用域,用于 Set([...])(例如 "openid", "profile", "email") |
PLACEHOLDER_SERVER_URL | serverUrl(仅 Journey/DaVinci) |
PLACEHOLDER_REALM | realm(仅 Journey/DaVinci) |
PLACEHOLDER_COOKIE_NAME | cookieName(仅 Journey/DaVinci) |
PLACEHOLDER_JOURNEY_NAME | journeyName(仅 Journey,默认值为 "Login") |
PLACEHOLDER_ACR_VALUES_LINE | DaVinci/OIDC:完整的 oidcConfig.acrValues = "..." 行——如果未提供,则完全省略该行 |
PLACEHOLDER_KEYCHAIN_ACCOUNT | Keychain 账户键——使用 "<APP_NAME_UPPER>_ACCESS_TOKEN" 模式(例如 "PINGDEMO_ACCESS_TOKEN") |
用户选择占位符模式时:使用 "yourClientId"、"yourapp://callback"、"https://your-tenant.forgeblocks.com/am/oauth2/alpha/.well-known/openid-configuration"。
自动创建项目(提供 output-path 时)
读取 assets/project-scaffolding.md 并严格遵循步骤 0–G。关键提示:
- 使用
18.0作为部署目标。 - 布局为扁平结构——
.xcodeproj、源代码目录和Config/直接并列于<output-path>/下。 PBXFileSystemSynchronizedRootGroup表示放入源代码目录的 Swift 文件会自动包含在项目中。- 在 Ruby 脚本中使用
project.objects.each(不要使用project.root_object.objects)来遍历所有项目对象。
示例:
/ping-orchestration-ios-sdk create-sample "a simple username/password login using Journey"
/ping-orchestration-ios-sdk create-sample "OIDC centralized login with token display and sign-out" app-name "PingDemo" output-path "/Users/me/Dev"
/ping-orchestration-ios-sdk create-sample "Journey login with FIDO passkey registration and biometric auth" app-name "FidoSample"
Ping SDK for iOS — 集成指南
代码示例的命名约定
始终使用完整、明确的变量名和参数名。不要使用缩写:vm → viewModel、cb → nameCallback、cfg → journeyConfig。这适用于 switch 语句中的局部绑定和闭包参数(不要使用 $0)。
流程类型 — 选择集成路径
Journey(原生/嵌入式) — 完全使用应用内的 SwiftUI 视图呈现身份验证 UI。不会启动浏览器。不需要使用 onOpenURL / OpenURLMonitor。
OIDC Web(OidcWebClient) — 使用 SFSafariViewController 或 ASWebAuthenticationSession。必须配置 onOpenURL → OpenURLMonitor.shared.handleOpenURL(url),并处理应用恢复到前台的情况。
DaVinci — 与 Journey(原生)类似,但使用收集器而非回调。社交 IdP 收集器(IdpCollector)会启动浏览器,因此如果 DaVinci 流程使用这些收集器,请配置 onOpenURL。
模块一览
只选择所需模块 — SDK 采用模块化设计:
| 模块 | 导入 | 适用场景 |
|---|---|---|
PingOrchestrate | 核心引擎 — Node、ContinueNode、SuccessNode、FailureNode、ErrorNode | 始终需要(在任何使用节点类型的文件中显式导入) |
PingJourneyPlugin | Callback 协议 | 任何声明 any Callback 参数的文件 |
PingJourney | import PingJourney | PingOne AIC / AM Journey 流程,所有具体回调类型 |
PingDavinci | import PingDavinci | PingOne DaVinci 流程 |
PingDavinciPlugin | import PingDavinciPlugin | Collector 协议 — 任何使用 any Collector 或按收集器类型进行分支判断的文件 |
PingOidc | import PingOidc | OIDC / OAuth 令牌处理、User / userinfo、OidcWebClient |
PingBrowser | import PingBrowser | 集中式登录 — BrowserType、BrowserMode、BrowserLauncher、OpenURLMonitor |
PingLogger | import PingLogger | 统一日志记录 |
PingStorage | import PingStorage | Keychain 令牌/会话存储 |
PingExternalIdP | import PingExternalIdP | 社交登录(Apple/Google/Facebook) |
PingFido | import PingFido | FIDO2 / WebAuthn |
PingProtect | import PingProtect | PingOne Protect 风险信号 |
PingBinding | import PingBinding | 设备绑定与签名 |
PingOath | import PingOath | TOTP / HOTP MFA |
PingPush | import PingPush | 推送通知 MFA |
1 — SDK 包参考
.package(url: "https://github.com/ForgeRock/ping-ios-sdk", from: "2.0.0")
// Add only the products your target needs:
.product(name: "PingJourney", package: "ping-ios-sdk"),
.product(name: "PingOidc", package: "ping-ios-sdk"),
.product(name: "PingBrowser", package: "ping-ios-sdk"), // OidcWebClient flows
CocoaPods:
pod 'PingJourney'
pod 'PingStorage'
pod 'PingLogger'
2 — 节点类型参考
// PingOrchestrate — import in any file pattern-matching on nodes
struct FailureNode: Node {
let cause: Error // NON-OPTIONAL — use cause.localizedDescription
}
struct ErrorNode: Node {
let message: String // NON-OPTIONAL
let status: Int?
}
struct SuccessNode: Node {
let session: Session // NON-OPTIONAL
}
// ContinueNode — more input needed (callbacks for Journey, collectors for DaVinci)
Node 不符合 Equatable — 使用单独的 Bool 标志跟踪状态变化:
var isRegistered = false // set in next() based on returned node type
3 — 回调属性类型
Journey 回调属性是非可选字符串。不要使用 ?? fallback。基础层级属性:NameCallback.name/prompt、PasswordCallback.password/prompt、TextInputCallback.text/prompt、ChoiceCallback.selectedIndex/choices、TextOutputCallback.message/messageType。
标准层级注意事项(猜错会导致编译错误):ValidatedUsernameCallback → .username(不是 .value);ValidatedPasswordCallback → .password;ConfirmationCallback → .options/.selectedIndex: Int?;TermsAndConditionsCallback → .accepted: Bool;KbaCreateCallback → .selectedQuestion/.selectedAnswer;SelectIdpCallback → .value = provider.provider(没有 .setProvider())。
编写任何标准层级回调视图前,请阅读 references/journey-callbacks.md 中完整的「关键 API 事实」部分。
4 — AppJourney 单例
import PingJourney
import PingOrchestrate
import PingLogger
@MainActor
class AppJourney {
static let shared = AppJourney()
let journey: Journey
private init() {
journey = Journey.createJourney { journeyConfig in
journeyConfig.serverUrl = "https://your-server.example.com/am"
journeyConfig.realm = "alpha"
journeyConfig.cookie = "your-cookie-name"
journeyConfig.logger = LogManager.standard
journeyConfig.module(PingJourney.OidcModule.config) { oidcConfig in
oidcConfig.clientId = "yourClientId"
oidcConfig.scopes = ["openid", "profile", "email"]
oidcConfig.redirectUri = "yourapp://callback"
oidcConfig.discoveryEndpoint = "https://your-server.example.com/am/oauth2/alpha/.well-known/openid-configuration"
oidcConfig.logger = LogManager.standard
}
}
}
}
在向导中收集的 journeyName 会在调用时传递给 journey.start():
node = await AppJourney.shared.journey.start(journeyName)
有关多流程应用(Journey + DaVinci + OIDC Web)和令牌存储自定义设置,请参阅 references/advanced-patterns.md。
5 — Journey 身份验证流程
ViewModel 模式
import Foundation
import Observation
import PingJourney
import PingOrchestrate
@Observable
@MainActor
final class LoginViewModel {
var node: Node?
var isLoading = false
let journeyName: String
init(journeyName: String = "Login") {
self.journeyName = journeyName
}
func start() async {
isLoading = true
defer { isLoading = false }
node = await AppJourney.shared.journey.start(journeyName)
}
func next(continueNode: ContinueNode) async {
isLoading = true
defer { isLoading = false }
node = await continueNode.next()
}
func reset() { node = nil }
}
使用 @State 而非 @StateObject 持有模型:
@State private var loginViewModel = LoginViewModel()
顶层节点分支
import PingOrchestrate
import PingJourney
switch loginViewModel.node {
case let successNode as SuccessNode:
AuthenticatedView(successNode: successNode, onSignOut: loginViewModel.reset)
case let failureNode as FailureNode:
ErrorView(message: failureNode.cause.localizedDescription, onRetry: retry)
case let errorNode as ErrorNode:
ErrorView(
message: errorNode.message.isEmpty ? "An error occurred." : errorNode.message,
onRetry: retry
)
case let continueNode as ContinueNode:
LoginView(continueNode: continueNode, isLoading: loginViewModel.isLoading,
onNext: { Task { await loginViewModel.next(continueNode: continueNode) } })
default:
LoginView(continueNode: nil, isLoading: loginViewModel.isLoading,
onNext: { Task { await loginViewModel.start() } })
}
回调渲染器(ContinueNodeView)
Callback 位于 PingJourneyPlugin 中——与 PingOrchestrate 一起导入。在函数签名中使用 any Callback(Swift 6 存在类型语法)。当 hasSelfAdvancingCallback 为真时隐藏 Next 按钮(FIDO、Confirmation、Protect、SelectIdp、DeviceBinding 回调会自行推进)。
将 assets/ContinueNodeView.swift.journey.template 用作完整实现参考。编写任何回调视图代码之前,务必阅读 references/journey-callbacks.md。
6 — 会话生命周期
let user: User? = await AppJourney.shared.journey.journeyUser()
if let user = user {
let result = await user.userinfo(cache: false)
switch result {
case .success(let info):
info.forEach { key, value in print("\(key): \(String(describing: value))") }
case .failure(let error):
print(error.localizedDescription)
}
}
_ = await AppJourney.shared.journey.signOff()
7 — 应用入口点集成
Journey / DaVinci 原生流程——无需 onOpenURL:
@main
struct MyApp: App {
var body: some Scene {
WindowGroup { ContentView() }
}
}
OIDC Web 和社交 IdP 回调——三个必需的接线点:
import PingBrowser
@main
struct MyApp: App {
@Environment(\.scenePhase) private var scenePhase
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
OpenURLMonitor.shared.handleOpenURL(url)
}
.onChange(of: scenePhase) { _, phase in
if phase == .active,
BrowserLauncher.currentBrowser.isInProgress {
BrowserLauncher.currentBrowser.handleAppActivation()
}
}
}
}
}
URL scheme 注册——在使用 GENERATE_INFOPLIST_FILE = YES 的脚手架项目中,使用 Ruby 代码片段(assets/project-scaffolding.md 中的步骤 D)。不要手动编写 Info.plist(会导致 Multiple commands produce Info.plist),也不要使用 xcconfig 方括号语法(会导致 error: expected to find '=' in macro condition)。
8 — OIDC Web 流程(OidcWebClient)
当 IDP 托管登录页面时,选择 OIDC Web。当你需要在应用的 SwiftUI 视图中显示凭据字段时,选择 Journey 原生流程(第 5 节)。
authorize() 是 async throws,返回 Result<User, OidcError>。有两种错误通道:throws 用于网络 / OIDC 发现失败,Result.failure 用于授权错误。务必在包裹 switch result 的 do/catch 中同时处理这两种错误。
有关 AppOidc 单例、OidcWebClientConfig API、OidcError 情况、BrowserType 的权衡、OidcLoginViewModel 和品牌化的 OidcLoginView,请阅读 references/oidc-web-reference.md。
9 — DaVinci 流程
DaVinci 使用收集器而非回调。@Observable ViewModel 模式与 Journey 相同。将 PingJourney.OidcModule 替换为 PingDavinci.OidcModule。DaVinci 不使用 serverUrl、realm 或 cookieName。
hasSelfAdvancingCollector 类型:SubmitCollector、FlowCollector、FidoRegistrationCollector、FidoAuthenticationCollector、DeviceRegistrationCollector、DeviceAuthenticationCollector。
**重要:**调用 next() 之前,务必设置 submitCollector.value = submitCollector.id 和 flowCollector.value = flowCollector.id——这两个值会决定 POST 请求体中的 actionKey 和 eventType。
有关 AppDaVinci 单例、完整的 CollectorNodeView、各个收集器视图、DaVinciAuthenticatedView 和会话生命周期,请阅读 references/davinci-collectors.md。
10 — Swift 6 注意事项
- 将 ViewModel 标记为
@MainActor——这是@Observable属性的正确隔离方式。 - 在
@MainActor代码中使用Task { },以调用异步 SDK。 - 在函数签名中写
any Callback/any Collector——这是 Swift 6 存在类型语法的要求。 Node不符合Equatable。使用 Bool 标记跟踪状态转换,不要使用.onChange(of: node)。- 对使用
UserDefaults的@Observable类,务必添加import Foundation。
11 — Ping Identity 视觉品牌
将 assets/Theme.swift.template 原样写入 <AppName>/Theme.swift(替换 PLACEHOLDER_APP_NAME)。其中包含:PingPrimaryButtonStyle、PingHeaderView、PingTextField、PingSecureField、FidoIconView、ErrorMessageView、LoadingOverlay、ErrorView。关键设计参数:Color.pingRed = #A31300,圆角半径 15pt(按钮)/ 8pt(字段),标题内边距 32pt,内容内边距 24pt。
将 assets/Logo.imageset/ 复制到 Assets.xcassets/Logo.imageset/,并使用 Image("Logo") 引用。对任何 Form 应用 .tint(.pingRed)。
12 — 回调 / 收集器层级
完整层级细分和选择指南:references/callback-tiers.md
**摘要:**基础版(用户名/密码/选项,不含额外 SPM)→ 标准版(增加 FIDO、Protect、设备绑定、条款与条件、KBA、基于浏览器的社交 IdP)→ 完整版(增加原生 Apple/Google/Facebook 社交登录 + ReCaptcha Enterprise)。向导的 callbackTier 参数直接对应这些层级。
常见陷阱
请参阅 references/common-pitfalls.md — 涵盖 Swift 6 错误、OIDC 重定向问题、DaVinci 收集器注意事项、SPM 构建错误和模拟器问题。
13 — Journey 导出分析
当用户提供 Journey 导出 JSON 时,先按照 references/journey-export-analysis.md 运行完整分析,再编写任何代码(步骤 A1–A8:枚举树、映射节点→回调、检查脚本、确定层级、提取配置)。