mcpField
P
ping-orchestration-ios-sdk

ping-orchestration-ios-sdk

github.com/pingidentity安全146 次安装13 周前更新
代码仓库

关于 ping-orchestration-ios-sdk

用于生成 Ping Orchestration iOS SDK 认证集成代码或完整 Xcode 示例应用的技能,支持 Journey、DaVinci 和 OIDC Web 流程,并可配置回调与收集器。

主要功能

  1. 01生成 Xcode 示例项目
  2. 02生成 SDK 集成文件
  3. 03配置 Journey、DaVinci 和 OIDC Web
  4. 04选择回调与收集器层级
  5. 05分析 Journey 导出 JSON

适用场景

  1. 01创建 iOS 认证示例应用
  2. 02为现有应用添加 Ping SDK 认证
  3. 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.template
App.swift.journey.template
App.swift.oidc.template
AppDaVinci.swift.template
AppJourney.swift.template
AppOidc.swift.template
AuthenticatedView.swift.davinci.template
AuthenticatedView.swift.journey.template
AuthenticatedView.swift.oidc.template
assets/callbacks/
BooleanAttributeInputCallbackView.swift.template
ChoiceCallbackView.swift.template
ConfirmationCallbackView.swift.template
ConsentMappingCallbackView.swift.template
DeviceBindingCallbackView.swift.template
DeviceProfileCallbackView.swift.template
DeviceSigningVerifierCallbackView.swift.template
FidoAuthenticationCallbackView.swift.template
FidoRegistrationCallbackView.swift.template
IdpCallbackView.swift.template
KbaCreateCallbackView.swift.template
NameCallbackView.swift.template
NumberAttributeInputCallbackView.swift.template
PasswordCallbackView.swift.template
PingOneProtectEvaluationCallbackView.swift.template
PingOneProtectInitializeCallbackView.swift.template
PollingWaitCallbackView.swift.template
ReCaptchaEnterpriseCallbackView.swift.template
SelectIdpCallbackView.swift.template
StringAttributeInputCallbackView.swift.template
TermsAndConditionsCallbackView.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 项目)
  • 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 emailOAuth 2.0 作用域,以空格分隔

Journey 专属附加参数:

参数必填默认值描述
serverUrl是—PingAM/AIC 服务基础 URL(末尾不带 /)
realm否alpha身份验证域
cookieName否iPlanetDirectoryPro会话 Cookie 名称
journeyName否LoginJourney 树名称
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-samplecreate-sample "<description>"生成完整且可运行的示例应用
app-nameapp-name "<name>"设置应用名称。如果省略,默认为 "MyApp"。
output-pathoutput-path "<path>"将文件写入磁盘。如果省略,则以内联方式输出。
create-sample "<description>"
  1. 分析描述——识别流程类型、模块以及隐含的屏幕。

  2. 提供 Journey 导出选项——如果流程类型是 journey(或可能是),请询问:

    "你有 Journey 导出 JSON 吗?我可以分析它,并根据 Journey 实际使用的回调定制示例。" 如果用户提供了文件,请在继续之前运行第 13 节。使用其输出确定 callbackTier 和屏幕。

  3. 仅在流程类型确实含糊不清时,提出一个澄清问题。通过一次 AskUserQuestion 收集已检测流程类型所需的参数(参见 W2 表格),并明确提供“我可以使用占位值”选项。

  4. 根据 app-name 确定应用名称;如果未提供,则使用 "MyApp"。

  5. 使用 assets/ 中的模板文件生成。替换所有 PLACEHOLDER_* 令牌(参见下表)。使用 @Observable @MainActor ViewModel——绝不要使用 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.template

    Journey 流程——读取并替换: 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。

  6. 交付:

    • 使用 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_IDclientId 值
PLACEHOLDER_REDIRECT_URIredirectUri 值
PLACEHOLDER_DISCOVERY_ENDPOINTdiscoveryEndpoint 值
PLACEHOLDER_SCOPES以逗号分隔并加引号的作用域,用于 Set([...])(例如 "openid", "profile", "email")
PLACEHOLDER_SERVER_URLserverUrl(仅 Journey/DaVinci)
PLACEHOLDER_REALMrealm(仅 Journey/DaVinci)
PLACEHOLDER_COOKIE_NAMEcookieName(仅 Journey/DaVinci)
PLACEHOLDER_JOURNEY_NAMEjourneyName(仅 Journey,默认值为 "Login")
PLACEHOLDER_ACR_VALUES_LINEDaVinci/OIDC:完整的 oidcConfig.acrValues = "..." 行——如果未提供,则完全省略该行
PLACEHOLDER_KEYCHAIN_ACCOUNTKeychain 账户键——使用 "<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始终需要(在任何使用节点类型的文件中显式导入)
PingJourneyPluginCallback 协议任何声明 any Callback 参数的文件
PingJourneyimport PingJourneyPingOne AIC / AM Journey 流程,所有具体回调类型
PingDavinciimport PingDavinciPingOne DaVinci 流程
PingDavinciPluginimport PingDavinciPluginCollector 协议 — 任何使用 any Collector 或按收集器类型进行分支判断的文件
PingOidcimport PingOidcOIDC / OAuth 令牌处理、User / userinfo、OidcWebClient
PingBrowserimport PingBrowser集中式登录 — BrowserType、BrowserMode、BrowserLauncher、OpenURLMonitor
PingLoggerimport PingLogger统一日志记录
PingStorageimport PingStorageKeychain 令牌/会话存储
PingExternalIdPimport PingExternalIdP社交登录(Apple/Google/Facebook)
PingFidoimport PingFidoFIDO2 / WebAuthn
PingProtectimport PingProtectPingOne Protect 风险信号
PingBindingimport PingBinding设备绑定与签名
PingOathimport PingOathTOTP / HOTP MFA
PingPushimport 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:枚举树、映射节点→回调、检查脚本、确定层级、提取配置)。

评论

支持 Markdown