Skip to main content
Back to iOS

ChangeCredential

The ChangeCredential view renders a row that lets a signed-in user set a new value for one of their own credentials. Tapping it opens a sheet that collects only a new value and its confirmation, checks it against the applicable rules, and posts the change via ThunderIDClient.updateUserCredentials.

It reads the rules it needs from the user type schema (GET /users/me/meta), fetched once by ThunderIDState and shared with any other mounted view that needs it (for example UserProfile). Multiple views cost one request rather than one each. The default label and messages are built from the credential attribute's own displayName in that schema, so they always match whatever an admin named it there.

By default it manages the password credential. To manage a different one, for example a PIN declared on the user type schema, set attribute; render one instance per credential to let a user manage more than one.

ChangeCredential requires .thunderIDProvider(config:) in its ancestor view hierarchy.

note

ChangeCredential collects only a new value and its confirmation, not the account's existing value. The self-service credential write path does not verify the current value today, so asking for one would only teach the user a false sense of security.

Usage

import SwiftUI
import ThunderIDSwiftUI

struct SecuritySection: View {
var body: some View {
VStack(alignment: .leading) {
Text("Security")
ChangeCredential()
}
}
}

Managing a Different Credential

Render one instance per credential the user type schema declares, keyed by its attribute name:

VStack(alignment: .leading) {
ChangeCredential()
ChangeCredential(attribute: "pin")
}

Parameters

ParameterTypeRequiredDescription
attributeStringThe credential attribute this instance manages, any attribute the user type schema declares credential: true for. Defaults to "password".
onSuccess(() -> Void)?Called after the credential has been changed successfully.

The display name always comes from the schema's displayName for attribute (falling back to the title-cased attribute name when the schema declares none). Rename it from the ThunderID console rather than overriding it in code: ChangeCredential has no parameter for that, so the schema stays the single source of truth.

Behavior

  • Renders as a row, matching UserProfile's field rows, that opens a compact sheet on tap.
  • If the schema does not declare attribute as a credential, the sheet opens to an unavailable message telling the user changes are not possible and to contact their administrator, instead of the form.
  • If the schema declares a regex for the attribute, the form enforces it and shows the schema's description as the requirement hint, falling back to a generic message when the schema has none.
  • Each field's own label (e.g. "New Password") is its placeholder, and disappears once the user starts typing. A trailing button toggles the value between masked and plain text.
  • Dismissing the sheet clears the entered values; a successful change also clears them and dismisses the sheet automatically.

Customization with BaseChangeCredential

BaseChangeCredential is the unstyled builder variant. It manages the schema lookup, validation, and the network call, and passes the current state to the content closure.

The content closure receives a plain ChangeCredentialState reference, not a property-wrapped one, so a TextField/SecureField two-way binding to it needs a child view that re-wraps it as @ObservedObject, the same shape ChangeCredential's own built-in row and sheet use internally:

import SwiftUI
import ThunderID
import ThunderIDSwiftUI

struct CustomChangePin: View {
var body: some View {
BaseChangeCredential(
attribute: "pin",
onSuccess: { print("PIN updated") }
) { state in
CustomChangePinForm(state: state)
}
}
}

private struct CustomChangePinForm: View {
@ObservedObject var state: ChangeCredentialState

var body: some View {
VStack(alignment: .leading, spacing: 12) {
if let error = state.error {
Text(error).foregroundStyle(.red)
}

SecureField("New \(state.credentialDisplayName)", text: $state.newValue)
.textFieldStyle(.roundedBorder)
SecureField("Confirm New \(state.credentialDisplayName)", text: $state.confirmValue)
.textFieldStyle(.roundedBorder)

Button(state.loading ? "Saving…" : "Save") {
state.submit()
}
.disabled(!state.evaluation.isValid || state.loading)
}
}
}

BaseChangeCredential Parameters

ParameterTypeRequiredDescription
attributeStringThe credential attribute to manage. Defaults to "password".
credentialDisplayNameString?Overrides the display name resolved from the schema. ChangeCredential does not expose this; it is an escape hatch for callers building their own UI without schema context.
policyRegexString?Overrides the schema-derived validation regex.
onSuccess(() -> Void)?Called after a successful credential change.
onError(() -> Void)?Called when a change fails.
content(ChangeCredentialState) -> ContentRenders the form from the current state.

ChangeCredentialState Properties

PropertyTypeDescription
credentialDisplayNameStringThe resolved display name for the credential.
newValueStringThe new value entered so far. Bindable.
confirmValueStringThe re-entered confirmation value. Bindable.
errorString?Form-level error from the last failed submission.
loadingBooltrue while a submission is in flight.
successBooltrue after the last submission succeeded.
unavailableBooltrue when the schema does not declare attribute as a credential.
evaluationCredentialFormEvaluationDerived validation state: isValid, confirmMatches, meetsPolicy, patternChecked, patternPassed.
fieldError(_:)(CredentialField) -> String?The error for CredentialField.new or CredentialField.form, if any.
submit()() -> VoidSubmits the current values. No-ops while invalid, loading, or unavailable.

Explore with AI

ThunderID LogoThunderID Logo

Product

DocsAPIsSDKs
© Copyright Linux Foundation Europe.For web site terms of use, trademark policy and other project policies please see https://linuxfoundation.eu/en/policies.