Skip to content

feat(sdk): typed arrays in the Swift SDK and both mobile example apps (PV14) - #4926

Merged
QuantumExplorer merged 2 commits into
v4.2-devfrom
feat/swift-typed-arrays
Sep 23, 2026
Merged

QuantumExplorer merged 2 commits into
v4.2-devfrom
feat/swift-typed-arrays

Conversation

@QuantumExplorer

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

Protocol version 14 adds typed scalar arrays to document schemas (#4922, #4923, #4924): an array property declared by an items schema instead of byteArray: true. The mobile clients did not support them:

No Rust change is needed. The wallet's schema sanitizer (DocumentType::sanitize_document_properties) already walks into typed array elements: it turns base58 or hex strings into identifiers, hex or base64 strings into bytes, and narrows JSON integers to the element width. It never parses a number or a boolean out of a string, so the clients have to send each element as a JSON value of its own kind.

What was done?

The examples use this property:

"scores": {
  "type": "array",
  "items": { "type": "integer", "minimum": 0, "maximum": 100 },
  "minItems": 1,
  "maxItems": 8,
  "position": 0
}

Swift SDK

  • DataContractParser persists a typed array as an ordinary array property. The ParseError enum fix(dpp)!: typed array review fixes: hyphenated list paths, element constraints, untrusted lists, Swift refusal #4924 added is removed; it was never released.

    Before: parseDataContract throws unsupportedTypedArray(documentType: "charter", property: "scores")
    After:  the contract is stored; scores is a PersistentProperty with type "array", byteArray false, minItems 1, maxItems 8
    
  • New DocumentTypedArray (mirrors wasm-dpp2's DocumentTypedArrayProperty): path, element kind (integer, number, boolean, string, byteArray, identifier with their bounds and enum), minItems, maxItems, uniqueItems. PersistentDocumentType.typedArrays and typedArray(named:) derive it from the stored schemaJSON, like immutability does, so no SwiftData field or schema version is added. Nested typed arrays are reported by dotted path (team.leads).

  • DocumentTypedArray.values(fromInputs:) / jsonArray(fromInputs:) convert the form rows. They check the count against minItems/maxItems, then each row, then repeats when uniqueItems is set, and name the first failure (scores[1]: "x" is not a whole number.).

Swift example app

  • The document form edits a typed array one row per element: a picker when the items declare an enum, a toggle for booleans, number fields for integers and numbers, base58 text for identifiers, hex text for byte arrays.

    Input: rows "3" and "99"
    Before: no form; the contract could not be loaded (and before #4924 the comma field sent {"scores": ["3", "99"]}, which consensus refuses and still charges for)
    After:  {"scores": [3, 99]}
    
  • An invalid list is refused before broadcast, because a refused transition is still paid for.

    Input: rows "3" and "x"
    After: "Could not encode document fields: scores[1]: "x" is not a whole number."  nothing is sent
    
  • The state-transition builder (TransitionDetailView) now encodes its document fields through the same conversion. Before, it serialized them with try? JSONSerialization.data(...), which raises an Objective-C exception (not caught by try?) on the Data value an identifier or byte array field holds. On a conversion failure its submit is disabled and the reason is shown under the fields. propertiesJSON also refuses a non-finite number (nan typed into a number field) instead of crashing.

  • The type details and storage record views show a typed array's element kind and bounds.

Kotlin example app (the Kotlin SDK keeps no document-type model, so this is app-only, like #4820)

  • TypedArrays.kt: the same model read off the schema JSON, the per-element conversion, the whole-list check, and replace seeding.

  • The create and replace forms get the row editor; the type details screen shows the element kind and bounds.

    Input: rows "true" and "false" for "flags": {"type": "array", "items": {"type": "boolean"}, "maxItems": 4}
    Before: {"flags": ["true", "false"]}   refused by consensus
    After:  {"flags": [true, false]}
    
  • The replace form seeds one row per stored element. Byte array elements arrive as base64 and are shown as hex: the sanitizer tries hex before base64, so sending base64 made only of hex digits back would decode to different bytes.

    Stored: "digests": ["3q2+7w=="]
    Before: the list was left blank (arrays were not seeded) and preserved as is
    After:  one row "deadbeef", sent back as "deadbeef"
    
  • Emptying a seeded optional list removes it; a required list is sent even when empty, so minItems is judged on the form.

How Has This Been Tested?

  • Swift: xcodebuild test -scheme SwiftDashSDK on an iPhone 17 simulator, run against a freshly built release xcframework, 7 suites: 87 tests, 0 failures. That covers the new DataContractParserTypedArrayTests (12) and DocumentTypedArrayElementInputTests (34), plus the other DataContractParser*Tests and DocumentTypeImmutabilityTests, which share the parser.
  • Swift example app: clean simulator build with warnings as errors: succeeded.
  • Kotlin: ./gradlew :app:compileDebugKotlin :app:testDebugUnitTest: 31 suites, 219 tests, 0 failures, including the new TypedArraysTest (9).
  • Not run: a create or replace with a typed array on a live network, and the forms driven on a simulator or emulator.

Known limitation, not changed here: the Swift state-transition builder's replace calls dash_sdk_document_set_properties (rs-sdk-ffi), which does not run the schema sanitizer. That path still cannot send identifier or byte array values, top-level or inside a typed array; it now gets strings instead of crashing. The builder's create path and both apps' main create and replace forms do sanitize.

Breaking Changes

None. DataContractParser.ParseError was added by #4924 on v4.2-dev and never released.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed
  • If I added or changed GroveDB structure, I described it in the area's structure.rs, regenerated grovedb-structure.json, and checked the structure viewer link posted on this pull request

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

🤖 Generated with Claude Code

QuantumExplorer and others added 2 commits September 23, 2026 07:02
The contract parser no longer refuses a typed array (an array property
declared by an items schema); it persists it as an ordinary array
property. DocumentTypedArray reads the element kind, bounds and enum off
the stored schemaJSON, so no SwiftData field or schema version is added,
and converts form text into JSON values of the element's own kind.

The example app's document form edits a typed array one row per element
and refuses an invalid list before broadcast, since a refused transition
is still paid for. The state-transition builder now encodes its document
fields through the same conversion, which also stops the Objective-C
exception it raised on identifier and byte array values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lace forms (PV14)

The create and replace forms sent every element of a non-byte array as a
string split on commas, which consensus refuses for integer, number and
boolean elements and which breaks strings containing a comma. A typed
array now gets one row per element with an input suited to its kind, and
is sent as a JSON array of that kind; an invalid element, count or
repeat is refused on the form. The replace form seeds the rows from the
stored document, showing byte array elements as hex because the Rust
sanitizer tries hex before base64.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 32 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: dashpay/platform/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: c7ca98ae-908c-46a7-9f11-b79db1e11c04

📥 Commits

Reviewing files that changed from the base of the PR and between 107534d and 73d3543.

📒 Files selected for processing (15)
  • packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/ui/contracts/CreateDocumentScreen.kt
  • packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/ui/contracts/DocumentActionsScreen.kt
  • packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/ui/contracts/DocumentTypeDetailsScreen.kt
  • packages/kotlin-sdk/KotlinExampleApp/app/src/main/java/org/dashfoundation/example/ui/contracts/TypedArrays.kt
  • packages/kotlin-sdk/KotlinExampleApp/app/src/test/java/org/dashfoundation/example/ui/contracts/TypedArraysTest.kt
  • packages/swift-sdk/Sources/SwiftDashSDK/Core/Utils/DataContractParser.swift
  • packages/swift-sdk/Sources/SwiftDashSDK/Core/Utils/DocumentTypedArray.swift
  • packages/swift-sdk/Sources/SwiftDashSDK/Persistence/Models/PersistentDocumentType.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/DocumentFieldsView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/DocumentTypeDetailsView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/DocumentsView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/StorageRecordDetailViews.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/TransitionDetailView.swift
  • packages/swift-sdk/SwiftTests/SwiftDashSDKTests/DataContractParserTypedArrayTests.swift
  • packages/swift-sdk/SwiftTests/SwiftDashSDKTests/DocumentTypedArrayElementInputTests.swift

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@QuantumExplorer
QuantumExplorer merged commit ba7b075 into v4.2-dev Sep 23, 2026
9 checks passed
@QuantumExplorer
QuantumExplorer deleted the feat/swift-typed-arrays branch September 23, 2026 00:09
@thepastaclaw

Copy link
Copy Markdown
Collaborator

🕓 Review not started yet because the new head is waiting for the 30-minute push debounce.

  • Request normal review — click when the PR is ready for review.
  • Request priority review — click to move this review to the front of the queue.

Commit 73d3543. Normal review starts when eligible; priority review starts as soon as a slot is available.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants