Skip to content

fix(ios): validate signingUrl and emit the documented error codes - #7

Merged
IronTony merged 4 commits into
mainfrom
fix/ios-url-validation-and-error-codes
Aug 29, 2026
Merged

fix(ios): validate signingUrl and emit the documented error codes#7
IronTony merged 4 commits into
mainfrom
fix/ios-url-validation-and-error-codes

Conversation

@IronTony

Copy link
Copy Markdown
Owner

Brings the iOS module up to the contract the README has always described and the Android module now honours. Both changes are breaking, so they belong in 2.0.0 or they wait for 3.0.0.

Closes #5.

Validate signingUrl (#5)

DSMEnvelopesManager.presentCaptiveSigning(withPresenting:signingUrl:...) validates nothing and presents unconditionally. A blank or non-https URL rendered an empty signing controller whose completion never fired, so the JS promise never settled. CaptiveSigningUrlRecord defaults signingUrl to "", which means an omitted field reached the SDK without a malformed URL being involved at all.

The guard sits after the isInitialized check and before the stateQueue.sync that assigns pendingCompletion, so a rejected URL never claims the slot and a later valid call is not refused as "already in progress". Same exception type and message string as the Android guard, so the two platforms reject identically.

Emit the documented error codes

Expo derives a code from the exception class name when none is set, so NotInitializedException reached JS as ERR_NOT_INITIALIZED rather than the not_initialized the error table has listed since 1.0.0. Android got explicit codes in the previous release, which left the platforms disagreeing on every single code. All five exceptions now set theirs.

Separately, both .failure branches in DocuSignModule.swift hard-coded "signing_failed", which flattened presentation_failed and hid which stage had failed. They now forward the failure's own code. promise.reject(error) is not the alternative: it wraps anything that is not an Exception, so a raw SDK NSError would surface as ERR_UNEXPECTED.

Migration

Before (iOS) After (both platforms)
ERR_NOT_INITIALIZED not_initialized
ERR_NOT_LOGGED_IN not_logged_in
ERR_LOGIN_FAILED login_failed
ERR_SIGNING_FAILED signing_failed
ERR_PRESENTATION_FAILED (never reached, flattened to signing_failed) presentation_failed

Rejection messages on the failure path are now the underlying error text on its own, where they previously carried a DocuSign signing failed: prefix.

Verification

swiftc -parse clean on all three Swift files. npm run build, npm run lint and 18/18 Jest tests pass.

Worth stating plainly: CI compiles no Swift and runs no iOS tests, so nothing in this repo can catch a type error in these files. The native changes are verified by parsing and by reading against the Android implementation they mirror. A device run against a real envelope before tagging 2.0.0 would be the honest bar.

Not included

The .failure double-emit that Android had does not exist here. The manager's SDK-error path calls resolvePending(.success(outcome)) with status: "error", so the module's failure branch never runs for signing errors. Different from Android, internally consistent, left alone.

DSMEnvelopesManager.presentCaptiveSigning validates nothing and presents
unconditionally, so a blank or non-https URL rendered an empty signing
controller whose completion never fired, leaving the JS promise unsettled.
CaptiveSigningUrlRecord defaults signingUrl to "", so an omitted field
reached the SDK without a malformed URL being involved at all.

The guard sits after the isInitialized check and before the stateQueue.sync
that claims pendingCompletion, so a rejected URL never occupies the slot and
a later valid call is not refused as "already in progress". Same exception
type and message as the Android guard, so both platforms reject identically.

Closes #5
Expo derives a code from the exception class name when none is set, so
NotInitializedException reached JS as ERR_NOT_INITIALIZED rather than the
not_initialized the error table has always listed. The Android module got
explicit codes in the previous change, which left the two platforms
disagreeing on every code. All five exceptions now set their code.

Both failure branches in the module hard-coded "signing_failed", which
flattened presentation_failed and hid which stage failed. They now forward
the failure's own code. promise.reject(error) is not the alternative here:
it wraps anything that is not an Exception, so a raw SDK NSError would
surface as ERR_UNEXPECTED.
Add presentation_failed to the error table and state that both platforms
emit the codes verbatim, since matching on the ERR_-prefixed variants was
the only thing that worked on iOS before this release.
Keeps local coding-agent state out of the repo and the npm tarball.
@IronTony IronTony self-assigned this Aug 29, 2026
@IronTony IronTony added the enhancement New feature or request label Aug 29, 2026
@IronTony
IronTony merged commit 1a27342 into main Aug 29, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

iOS: presentCaptiveSigningWithUrl does not validate signingUrl

1 participant