无法使用在线签名模式创建信封

Cannot create an Envelope using online signing mode

我正在尝试创建一个信封并使用在线模式对其进行签名。

首先,我登录了我的帐户

@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)
}

之后,我创建了我的测试信封:

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)
                }
            }
        }
    }

但是这里

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)
                }
            }

我有一个错误:

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

当我切换到离线模式时,我无法使用该方法

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

因为它只适用于在线模式下创建的信封。在我的例子中,完成块没有被执行。

如何解决这个问题?我如何在在线模式下创建信封并签名?我做错了什么?我只想 select 我的 pdf,它包含在项目包中,并在某个位置添加符号。

提供的示例项目 here 不符合我的要求。因为使用来自服务器的模板并通过 presentComposeEnvelopeController 方法创建信封以选择文档等

XCode 12.4,iOS 13/14,DocuSign 2.4.1 通过 CocoaPods 包含在项目中。

编辑1

我更新了我的 perform 方法:

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)
                }
            }
        }

presentSigning方法也是:

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)
                }
            }
        }

但是现在我在presentSigning方法中得到了错误:Envelope is ready for sync and can not be resumed for signing.

并且没有显示任何带有我的 pdf 文档的屏幕。如何解决?我如何预览此文档,然后添加用户签名的功能?

解决方案

EnvelopManagerclass的工作代码:

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)
                }
            }
        }
    }

签署文档后,不要忘记在顶视图控制器上调用 sync 方法

@igdev:感谢您添加代码片段和详细说明您的方法。

如何在线创建信封并签名?

截至当前 SDK 版本,尚不支持使用 EnvelopeDefinition 创建在线信封。

以下方法仅支持已创建(通过 DocuSign API 或 Web)并可在 Native-SDK 使用的 DocuSign 帐户上访问的远程信封。

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

我只想 select 我的 pdf,它包含在项目包中,并在某些位置添加符号。

基于 Compose 流程的信封创建将使您能够在离线模式下使用包中的 PDF、位置选项卡和演示签名。这是一份指南 (Compose Envelope),其中列出了获得所需结果的步骤和示例代码。撰写信封指南中的以下片段显示了如何呈现此类信封以供签名:

  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) { ... }
                                    }];                                
                                }
    ];
  }

更新之前的示例代码(感谢添加)使用选项卡框架而不是锚点是正确的步骤,因为 SDK 在离线模式下不支持锚点。为了避免此类问题,最好使用 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 同时确保自定义信封数据通过验证过程。

/*!
 * @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;

envelope ready for sync 状态:

但现在我在 presentSigning 方法中遇到错误:信封已准备好同步,无法恢复签名。

Envelope is ready for sync and can not be resumed for signing.表示信封没有本地签名者等待签名的状态。发生这种情况可能有几个原因。

查看 guide 的添加收件人部分,这会将 Remote Signer 添加到本地信封。

// 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];

没有添加本地签名者来捕获签名。如果您需要添加本地签名者,他们可以代表以下情况:

  • SDK 验证帐户可以用作签名者 (DSMRecipientTypeSigner)。在示例中:@"JaneWood@docusign.com" 使用 routingOrder(或 SigningOrder)为 1 的 SDK 登录。
  • 经过 SDK 身份验证的帐户可以用作亲自签名者 (DSMRecipientTypeInPersonSigner) 的主机。以下示例将为 @"JaneWood@docusign.com" 添加一个亲自签名者作为主持人:
// 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];
  • 只有远程签名者(非本地 SDK 帐户)已添加到信封:在此类信封上调用 syncEnvelopes 将发送文档并通过电子邮件发送远程签名者以完成签名。

注意:所有本地签名者都已完成签名:一旦捕获了所有本地签名,您就会遇到这种情况,使用syncEnvelopes将发送到 DocuSign 服务器的信封。