Cannot create an Envelope using online signing mode

Viewed 111

I'm trying to create an Envelop and sign it using online mode.

At first, I logged in to my account

@IBAction private func signDocument(_ sender: UIButton) {
        guard let hostURL = URL(string: Environment.current.docuSignHost) else { return }
        isLoading = true
        DSMManager.login(withEmail: Environment.current.docuSignUserID,
                         password: Environment.current.docuSignPass,
                         integratorKey: Environment.current.docuSignIntegratorKey,
                         host: hostURL) { [weak self] info, error in
            self?.isLoading = false
            if let error = error {
                self?.error = error
            } else {
                self?.showDocuSign(info: info)
            }
        }
    }
    
// MARK: - Helpers
    
private func showDocuSign(info: DSMAccountInfo?) {
    guard let info = info else { return }
    envelopManager.perform(with: info, presentingController: self)
}

After that, I created my test Envelop:

final class EnvelopeManager {
        
        private let envelopesManager = DSMEnvelopesManager()
        private let templateManager = DSMTemplatesManager()
        
        // MARK: - Lifecycle
        
        func perform(with config: DSMAccountInfo, presentingController: UIViewController) {
            guard let documentURL = R.file.tgkCapitalPortfolioBAgreementPdf(),
                  let documentData = try? Data(contentsOf: documentURL) else { return }
            
            let envelopDefinition = DSMEnvelopeDefinition()
            envelopDefinition.envelopeName = "Some name"
            envelopDefinition.emailSubject = "Please Sign Envelope on Document"
            envelopDefinition.emailBlurb = "Hello, Please sign my Envelope"
            
            let document = DSMDocument()
            document.name = R.file.tgkCapitalPortfolioBAgreementPdf.name
            document.documentId = "1"
            document.documentBase64 = documentData.base64EncodedString()
            
            envelopDefinition.documents = [document]
            
            let signHere = DSMSignHere()
            signHere.documentId = document.documentId
            signHere.pageNumber = 1
            signHere.recipientId = "1"
            signHere.anchorXOffset = 100
            signHere.anchorYOffset = 100
            signHere.tabId = "1"
            
            let tabs = DSMTabs()
            tabs.signHereTabs = [signHere]
            
            let signer = DSMSigner()
            signer.canSignOffline = false
            signer.email = config.email
            signer.name = config.userName
            signer.recipientId = "1"
            signer.tabs = tabs
            
            let signers: [DSMSigner] = [signer]
            
            let recipients = DSMRecipients()
            recipients.signers = signers
            
            envelopDefinition.recipients = recipients
            envelopDefinition.status = "sent"
            
            envelopesManager.composeEnvelope(with: envelopDefinition, signingMode: .online) { [weak self] envelopID, error in
                if let envelopID = envelopID {
                    print(envelopID)
                    self?.presentSigning(presenter: presentingController,
                                         envelopeID: envelopID)
                } else {
                    print(error.localizedDescription)
                }
            }
        }
        
        private func presentSigning(presenter: UIViewController, envelopeID: String) {
            envelopesManager.presentSigning(withPresenting: presenter,
                                            envelopeId: envelopeID,
                                            animated: true) { (viewController, error) in
                if let viewController = viewController {
                    print(viewController)
                }

                if let error = error {
                    print(error.localizedDescription)
                }
            }
        }
    }

But here

envelopesManager.composeEnvelope(with: envelopDefinition, signingMode: .online) { [weak self] envelopID, error in
                if let envelopID = envelopID {
                    print(envelopID)
                    self?.presentSigning(presenter: presentingController,
                                         envelopeID: envelopID)
                } else {
                    print(error.localizedDescription)
                }
            }

I have an error:

Envelope creation online is not supported at this moment. Please try offline mode

When I switched to offline mode I can't use the method

envelopesManager.presentSigning(withPresenting:, enveloped: , animated:, completeion)

Because it works only for envelops which were created in the online mode. And in my case completion block is not executed.

How to resolve this issue? How I can create an envelope in online mode and sign it? What I'm doing wrong? I just want to select my pdf which contains in the project bundle and in some position add the sign.

The sample project which was provided here doesn't match my requirements. Because there is using a template from server and creating envelop via presentComposeEnvelopeController method for choosing document and etc.

XCode 12.4, iOS 13/14, DocuSign 2.4.1 included in the project via CocoaPods.

Edit1

I have updated my perform method:

func perform(with config: DSMAccountInfo, presentingController: UIViewController) {
            guard let documentURL = R.file.tgkCapitalPortfolioBAgreementPdf(),
                  let documentData = try? Data(contentsOf: documentURL) else { return }
            
            let envelopDefinition = DSMEnvelopeDefinition()
            envelopDefinition.envelopeName = "Some name"
            envelopDefinition.emailSubject = "Please Sign Envelope on Document"
            envelopDefinition.emailBlurb = "Hello, Please sign my Envelope"
            
            let document = DSMDocument()
            document.name = R.file.tgkCapitalPortfolioBAgreementPdf.name
            document.documentId = "1"
            document.documentBase64 = documentData.base64EncodedString()
            
            envelopDefinition.documents = [document]
            
            let signHere = DSMSignHere()
            signHere.documentId = document.documentId
            signHere.pageNumber = 1
            signHere.recipientId = "1"
            signHere.frame = .init(originX: 100,
                                   originY: 100,
                                   width: 100,
                                   height: 100,
                                   originYOffsetApplied: 50)
            signHere.tabId = "1"
            
            let tabs = DSMTabs()
            tabs.signHereTabs = [signHere]
            
            let signer = DSMSigner()
            signer.email = config.email
            signer.name = config.userName
            signer.recipientId = "1"
            signer.tabs = tabs
            
            let signers: [DSMSigner] = [signer]
            
            let recipients = DSMRecipients()
            recipients.signers = signers
            
            envelopDefinition.recipients = recipients
            envelopDefinition.status = "created"
            
            envelopesManager.composeEnvelope(with: envelopDefinition, signingMode: .offline) { [weak self] envelopID, error in
                if let envelopID = envelopID {
                    print(envelopID)
                    self?.presentSigning(presenter: presentingController,
                                         envelopeID: envelopID)
                } else {
                    print(error.localizedDescription)
                }
            }
        }

and presentSigning method too:

private func presentSigning(presenter: UIViewController, envelopeID: String) {
            envelopesManager.resumeSigningEnvelope(withPresenting: presenter,
                                                   envelopeId: envelopeID) { (viewController, error) in
                if let viewController = viewController {
                    print(viewController)
                }

                if let error = error {
                    print(error.localizedDescription)
                }
            }
        }

But now I have got the error in the presentSigning method: Envelope is ready for sync and can not be resumed for signing.

And any screen with my pdf document was not shown. How to resolve it? How I can preview this document and after that add the ability for a user to sign it?

Solution

The working code of the EnvelopManager class:

final class EnvelopeManager {
        
        private let envelopesManager = DSMEnvelopesManager()
        private let templateManager = DSMTemplatesManager()
        
        // MARK: - Lifecycle
        
        func sync() {
            envelopesManager.syncEnvelopes()
        }
        
        func perform(with config: DSMAccountInfo, presentingController: UIViewController) {
            guard let path = Bundle.main.path(forResource: R.file.tgkCapitalPortfolioBAgreementPdf.name, ofType: "pdf") else { return }
            
            let envelopDefinition = DSMEnvelopeDefinition()
            envelopDefinition.envelopeName = "Some name"
            envelopDefinition.emailSubject = "Please Sign Envelope on Document"
            envelopDefinition.emailBlurb = "Hello, Please sign my Envelope"
            
            let builder = DSMDocumentBuilder()
            builder.addDocumentId("1")
            builder.addName(R.file.tgkCapitalPortfolioBAgreementPdf.name)
            builder.addFilePath(Bundle.main.path(forResource: R.file.tgkCapitalPortfolioBAgreementPdf.name,
                                                 ofType: "pdf")!)
            let document = builder.build()
            
            envelopDefinition.documents = [document]
            
            let signHere = DSMSignHere()
            signHere.documentId = document.documentId
            signHere.pageNumber = 1
            signHere.recipientId = "1"
            signHere.frame = .init(originX: 100,
                                   originY: 100,
                                   width: 100,
                                   height: 100,
                                   originYOffsetApplied: 50)
            signHere.tabId = "1"
            
            let tabs = DSMTabs()
            tabs.signHereTabs = [signHere]
            
            let signer = DSMSigner()
            signer.email = config.email
            signer.name = config.userName
            signer.userId = config.userId
            signer.clientUserId = config.userId
            signer.routingOrder = 1
            signer.recipientId = "1"
            signer.tabs = tabs
            
            let signers: [DSMSigner] = [signer]
            
            let recipients = DSMRecipients()
            recipients.signers = signers
            
            envelopDefinition.recipients = recipients
            envelopDefinition.status = "created"
            
            envelopesManager.composeEnvelope(with: envelopDefinition, signingMode: .offline) { [weak self] envelopID, error in
                if let envelopID = envelopID {
                    print(envelopID)
                    self?.presentSigning(presenter: presentingController,
                                         envelopeID: envelopID)
                } else {
                    print(error.localizedDescription)
                }
            }
        }
        
        private func presentSigning(presenter: UIViewController, envelopeID: String) {
            envelopesManager.resumeSigningEnvelope(withPresenting: presenter,
                                                   envelopeId: envelopeID) { (viewController, error) in
                if let viewController = viewController {
                    print(viewController)
                }

                if let error = error {
                    print(error.localizedDescription)
                }
            }
        }
    }

After you will have signed the document do not forget to call the sync method on the top view controller

2 Answers

@igdev: Thanks for adding code snippets and details explaining your approach.

How I can create an envelope in online mode and sign it?

As of current SDK version, creating online envelopes with EnvelopeDefinition is not yet supported.

Following method is only supported for remote-envelopes that have been already created (via DocuSign API or Web) and accessible on the DocuSign account under use by Native-SDK.

envelopesManager.presentSigning(withPresenting:, enveloped: , animated:, completion)

I just want to select my pdf which contains in the project bundle and in some position add the sign.

Compose flow based envelope creation would enable you to use the PDF from the bundle, position tabs and present signing in the offline mode. Here is a guide (Compose Envelope) that lists steps with sample code to get the desired result. Following snippet from compose envelope guide shows how such envelope can be presented for signing:

  if (envelope) {
    // Compose the envelope to automatically cache it
    [self.envelopesManager composeEnvelopeWithEnvelopeDefinition: envelope
                              signingMode: DSMSigningModeOffline
                              completion: ^(NSString * _Nullable envelopeId, NSError * _Nonnull error) {
                                    // error checks in case envelope compose failed. Also use notifications for caching related events.
                                    if (error) { ... }

                                    // Resume the envelope to start the signing process
                                    [self.envelopesManager resumeSigningEnvelopeWithPresentingController: self
                                            envelopeId: envelopeId
                                            completion: ^(UIViewController * _Nullable presentedController, NSError * _Nullable error) {
                                        // error checks in case UI presentation failed. Use notifications for other events.
                                        if (error) { ... }
                                    }];                                
                                }
    ];
  }

The sample code prior to update (thanks for adding it) to use the tab-frame instead of anchors is correct step as anchors aren't supported in offline mode by the SDK. In order to avoid such issues, it's best to use the EnvelopeBuilder (which in turn uses Recipient and TabBuilder) to create tabs for individual recipients and assign correct frames (e.g. signHere.frame = CGRect(x: 100, y: 100, width: 50, height: 40)). Using EnvelopeBuilder also ensures that custom envelope data goes through validation process.

/*!
 * @discussion add frame of the tab (e.g. CGFrameMake(100, 100, 50, 40) for Sign Tab) and return builder.
 * @param frame a frame to draw tab within
 */
- (DSMTabBuilder *)addFrame:(CGRect)frame;

Check answer below for envelope ready for sync state:

But now I have got the error in the presentSigning method: Envelope is ready for sync and can not be resumed for signing.

Envelope is ready for sync and can not be resumed for signing. denotes the state that envelope has no local signers awaiting signing. This could happen because of a few reasons.

Taking a look at the adding recipient section of guide, this adds a Remote Signer to local envelope.

// Create an envelope recipient with name and email and assign an id and type with routing order
DSMEnvelopeRecipient *recipient = [[[[[[DSMRecipientBuilder builderForType: DSMRecipientTypeSigner]
                                          addRecipientId: @"1"]
                                          addSignerName: @"Jane Wood"]
                                          addSignerEmail: @"JaneWood@docusign.com"]
                                          addRoutingOrder: 1] 
                                        build];

No local signers have been added to capture signatures. If you need to add a local signer they could represent following cases:

  • SDK authenticated account could be used as a signer (DSMRecipientTypeSigner). In the example: @"JaneWood@docusign.com" is logged in with SDK with routingOrder (or SigningOrder) of 1.
  • SDK authenticated account could be used as a host to an in-person-signer (DSMRecipientTypeInPersonSigner). The following example would add an in-person-signer for @"JaneWood@docusign.com" as host:
// Create an envelope recipient with name and email and assign an id and type with routing order
DSMEnvelopeRecipient *recipient = [[[[[[[DSMRecipientBuilder builderForType: DSMRecipientTypeInPersonSigner]
                                          addRecipientId: @"1"]
                                          addHostName: @"Jane Wood"]
                                          addHostEmail: @"JaneWood@docusign.com"]
                                          addSignerName: @"IPS - John Local Doe"]
                                          addRoutingOrder: 1] 
                                        build];
  • Only remote signers (non-local SDK account) have been added to envelope: Invoking syncEnvelopes on such envelopes will send documents and email the remote-signers to complete signing.

Note: All of the local signers have already completed signing: You'd reach this case once all local signatures have been captured, and using syncEnvelopes would send envelope to DocuSign servers.

Related