如何从用 Swift 编写的 Flutter 平台特定代码启动 ViewController?

Posted

技术标签:

【中文标题】如何从用 Swift 编写的 Flutter 平台特定代码启动 ViewController?【英文标题】:How to launch a ViewController from Flutter Platform Specific code written in Swift? 【发布时间】:2019-05-08 05:31:19 【问题描述】:

我正在为 Paytm 创建一个插件,并为 androidios 编写特定于平台的代码。

Paytm 是印度的支付网关服务,非常易于配置和集成到应用程序中。它需要一些配置,最后一步是启动支付交易,它在内部打开自己的 Android Activity 和 iOS 平台的 ViewController。

我测试了插件的 Android 部分。它工作正常。

但我不知道如何在 iOS 中触发 viewController。我正在使用 swift 进行 iOS 编码部分。

func beginPayment()  
serv = serv.createProductionEnvironment() 
let type :ServerType = .eServerTypeProduction 
let order = PGOrder(orderID: "", customerID: "", amount: "", eMail: "", mobile: "") 
order.params = [
    "MID": "rxazcv89315285244163",
    "ORDER_ID": "order1",
    "CUST_ID": "cust123",
    "MOBILE_NO": "7777777777",
    "EMAIL": "username@emailprovider.com",
    "CHANNEL_ID": "WAP",
    "WEBSITE": "WEBSTAGING", 
    "TXN_AMOUNT": "100.12",
    "INDUSTRY_TYPE_ID": "Retail",
    "CHECKSUMHASH": "oCDBVF+hvVb68JvzbKI40TOtcxlNjMdixi9FnRSh80Ub7XfjvgNr9NrfrOCPLmt65UhStCkrDnlYkclz1qE0uBMOrmuKLGlybuErulbLYSQ=", 
    "CALLBACK_URL": "https://securegw-stage.paytm.in/theia/paytmCallback?ORDER_ID=order1"
]
 self.txnController = self.txnController.initTransaction(for: order) as?PGTransactionViewController 
self.txnController.title = "Paytm Payments" self.txnController.setLoggingEnabled(true) 
if(type != ServerType.eServerTypeNone)  
    self.txnController.serverType = type; 
 else  return  
self.txnController.merchant = PGMerchantConfiguration.defaultConfiguration() 
self.txnController.delegate = self 
self.navigationController?.pushViewController(self.txnController, animated: true) 

这是在 PayTM iOS SDK 文档页面中编写的关于如何触发 Paytm 交易控制器的演示。

这是我为实现上述过程而编写的代码。

import Flutter
import PaymentSDK
import UIKit


/*
 A delegate interface that exposes all of the PayTM Payment Gateway functionality for other plugins to use.
 The below [Delegate] implementation should be used by any clients unless they need to
 override some of these functions, such as for testing.
 */
protocol IDelegate 

    // Initializes this delegate so that it can perform transaction operation
    func initializePaytmService(result: @escaping FlutterResult, buildVariant: String?)

    // Returns the PayTM transaction status without displaying any user interface.
    func startPaymentTransaction(result: @escaping FlutterResult, checkSumRequestObject: Dictionary<String, String>?)


/*
 Delegate class will have the code for making PayTM Transactions.
 */
class FlutterPaytmPluginDelegate : IDelegate,  PGTransactionDelegate 

    private let flutterRegistrar: FlutterPluginRegistrar
    private var viewController: UIViewController
    private var serverType: ServerType?
    private var pendingOperation: PendingOperation?
    private var paytmTransactionController: PGTransactionViewController?

    let release = "BuildVariant.release"
    let debug = "BuildVariant.debug"

    //Method Constants
    let methodInitPaytmService = "initialize_paytm_service"
    let methodStartPaymentTransaction = "start_payment_transaction"

    //PayTM Success Response Constants
    let paytmStatus = "STATUS"
    let paytmChecksumHash = "CHECKSUMHASH"
    let paytmBankName = "BANKNAME"
    let paytmOrderId = "ORDERID"
    let paytmTransactionAmount = "TXNAMOUNT"
    let paytmTransactionDate = "TXNDATE"
    let paytmMerchantId = "MID"
    let paytmTransactionId = "TXNID"
    let paytmResponseCode = "RESPCODE"
    let paytmPaymentMode = "PAYMENTMODE"
    let paytmBankTransactionId = "BANKTXNID"
    let paytmCurrency = "CURRENCY"
    let paytmGatewayName = "GATEWAYNAME"
    let paytmResponseMessage = "RESPMSG"

    //Error Constants
    let errorReasonBuildVariantNotPassed = "build_variant_not_passed"
    let errorReasonChecksumObjectNotPassed = "checksum_request_object_not_passed"

    // These error codes must match with ones declared on iOS and Dart sides.
    let errorReasonPaytmTransactionResponseNull = "paytm_transaction_response_null"
    let errorReasonPaytmTransactionCancelled = "paytm_transaction_cancelled"
    let errorReasonPaytmMissingParameters = "paytm_missing_parameters"

    init(registrar: FlutterPluginRegistrar, viewController: UIViewController) 
        self.flutterRegistrar = registrar
        self.viewController = viewController
    

    /*
     Initializes this delegate so that it is ready to perform other operations. The Dart code
     guarantees that this will be called and completed before any other methods are invoked.
     */
    func initializePaytmService(result: @escaping FlutterResult, buildVariant: String?) 
        if buildVariant?.isEmpty ?? true 
            result(FlutterError(code: errorReasonBuildVariantNotPassed, message: "Need a build variant", details: nil))
         else 
            serverType = buildVariant == release ? .eServerTypeProduction : .eServerTypeStaging
            result(nil)
        
    

    func startPaymentTransaction(result: @escaping FlutterResult, checkSumRequestObject: Dictionary<String, String>?) 

        if checkSumRequestObject?.isEmpty ?? true 
            result(FlutterError(code: errorReasonChecksumObjectNotPassed, message: "Need a build variant", details: nil))
         else 
            checkAndSetPendingOperation(method: methodStartPaymentTransaction, result: result)
            let order = PGOrder(orderID: "", customerID: "", amount: "", eMail: "", mobile: "")
            order.params = checkSumRequestObject!

            self.paytmTransactionController = paytmTransactionController?.initTransaction(for: order) as? PGTransactionViewController ?? PGTransactionViewController()
            self.paytmTransactionController?.title = "Paytm Payments"
            if(serverType != .eServerTypeNone) 
                self.paytmTransactionController?.serverType = serverType;
             else 
                return
            
            self.paytmTransactionController?.setLoggingEnabled(serverType == .eServerTypeStaging)
            self.paytmTransactionController?.merchant = PGMerchantConfiguration.defaultConfiguration()
            self.paytmTransactionController?.delegate = self
            UIApplication.shared.delegate?.window??.rootViewController?.present(paytmTransactionController, animated: true, completion: nil)

        
    

    private func checkAndSetPendingOperation(method: String, result: @escaping FlutterResult) 
        if (pendingOperation != nil) 
            return;
            //                throw IllegalStateException("Concurrent operations detected: " + pendingOperation!!.method + ", " + method)
        
        pendingOperation = PendingOperation(method: method, result: result)
    

    private func finishWithSuccess(data: Dictionary<String, String>?) 
        pendingOperation!.result(data)
        pendingOperation = nil
        paytmTransactionController = nil
    

    private func finishWithError(errorCode: String, errorMessage: String) 
        pendingOperation!.result(FlutterError(code: errorCode, message: errorMessage, details: nil))
        pendingOperation = nil
    

    /*
     PayTM Transaction Delegates
     */
    func didFinishedResponse(_ controller: PGTransactionViewController, response responseString: String) 
        var paytmSuccessResponse = Dictionary<String, String>()
        if let data = responseString.data(using: String.Encoding.utf8) 
            do 
                if let jsonresponse = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:String] , jsonresponse.count > 0
                    paytmSuccessResponse[paytmStatus] = jsonresponse[paytmStatus] ?? ""
                    paytmSuccessResponse[paytmChecksumHash] = jsonresponse[paytmChecksumHash] ?? ""
                    paytmSuccessResponse[paytmBankName] = jsonresponse[paytmBankName] ?? ""
                    paytmSuccessResponse[paytmOrderId] = jsonresponse[paytmOrderId] ?? ""
                    paytmSuccessResponse[paytmTransactionAmount] = jsonresponse[paytmTransactionAmount] ?? ""
                    paytmSuccessResponse[paytmTransactionDate] = jsonresponse[paytmTransactionDate] ?? ""
                    paytmSuccessResponse[paytmTransactionId] = jsonresponse[paytmTransactionId] ?? ""
                    paytmSuccessResponse[paytmMerchantId] = jsonresponse[paytmMerchantId] ?? ""
                    paytmSuccessResponse[paytmResponseCode] = jsonresponse[paytmResponseCode] ?? ""
                    paytmSuccessResponse[paytmPaymentMode] = jsonresponse[paytmPaymentMode] ?? ""
                    paytmSuccessResponse[paytmBankTransactionId] = jsonresponse[paytmBankTransactionId] ?? ""
                    paytmSuccessResponse[paytmCurrency] = jsonresponse[paytmCurrency] ?? ""
                    paytmSuccessResponse[paytmGatewayName] = jsonresponse[paytmGatewayName] ?? ""
                    paytmSuccessResponse[paytmResponseMessage] = jsonresponse[paytmResponseMessage] ?? ""

                    finishWithSuccess(data: paytmSuccessResponse)
                
             catch 
                finishWithError(errorCode: errorReasonPaytmTransactionResponseNull, errorMessage: "Paytm transaction response in null")
            
        
    

    func didCancelTrasaction(_ controller: PGTransactionViewController) 
        finishWithError(errorCode: errorReasonPaytmTransactionCancelled, errorMessage: "Transaction cancelled.")
    

    func errorMisssingParameter(_ controller: PGTransactionViewController, error: NSError?) 
        finishWithError(errorCode: errorReasonPaytmMissingParameters, errorMessage: "There are some missing parameters.")
    

    private class PendingOperation 
        let method: String
        let result: FlutterResult

        init(method: String, result: @escaping FlutterResult) 
            self.method = method
            self.result = result
        
    



public class SwiftFlutterPaytmPlugin: NSObject, FlutterPlugin 
    //Channel Name Constant
    static let channelName = "flutterpaytmplugin.flutter.com/flutter_paytm_plugin"

    //Argument Constants
    let buildVariant = "build_variant"
    let checksumRequestObject = "checksum_request_object"

    //Method Constants
    let methodInitPaytmService = "initialize_paytm_service"
    let methodStartPaymentTransaction = "start_payment_transaction"

    var delegate : IDelegate

    init(pluginRegistrar: FlutterPluginRegistrar, uiViewController: UIViewController) 
        delegate = FlutterPaytmPluginDelegate(registrar: pluginRegistrar, viewController: uiViewController)
    

    public static func register(with registrar: FlutterPluginRegistrar) 
        let channel = FlutterMethodChannel(name: channelName, binaryMessenger: registrar.messenger())

        let viewController: UIViewController =
            (UIApplication.shared.delegate?.window??.rootViewController)!;

        let instance = SwiftFlutterPaytmPlugin(pluginRegistrar: registrar, uiViewController: viewController)
        registrar.addMethodCallDelegate(instance, channel: channel)
    

    public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) 
        let arguments = call.arguments as? Dictionary<String, Any>
        switch call.method 
        case methodInitPaytmService:
            delegate.initializePaytmService(result: result, buildVariant: (arguments?[buildVariant] as? String))
        case methodStartPaymentTransaction:
            delegate.startPaymentTransaction(result: result, checkSumRequestObject: (arguments?[checksumRequestObject] as? Dictionary<String, String>))
        default:
            result(FlutterMethodNotImplemented)
        
    

我正在关注这个已关闭问题https://github.com/flutter/flutter/issues/9961 的一些建议。

但找不到成功。

这是我得到的日志跟踪:

2018-12-07 17:27:39.510397+0530 Runner[56634:1432738] You've implemented -[<UIApplicationDelegate> application:performFetchWithCompletionHandler:], but you still need to add "fetch" to the list of your supported UIBackgroundModes in your Info.plist.
2018-12-07 17:27:39.510615+0530 Runner[56634:1432738] You've implemented -[<UIApplicationDelegate> application:didReceiveRemoteNotification:fetchCompletionHandler:], but you still need to add "remote-notification" to the list of your supported UIBackgroundModes in your Info.plist.
2018-12-07 17:27:39.610121+0530 Runner[56634:1432850] flutter: Observatory listening on http://127.0.0.1:56923/
PGTransactionViewController:loadView
PGTransactionViewController::viewDidLoad
PGTransactionViewController::viewWillAppear
(lldb) 

有一个例外: Exception showcased in the image

请帮帮我。

【问题讨论】:

可以分享一下安卓解决方案吗? 【参考方案1】:

我的一个朋友@virander 帮助我解决了这个问题。他编写了从 swift 插件文件中打开视图控制器的代码。

 if let navigationController = UIApplication.shared.keyWindow?.rootViewController as? UINavigationController 
            navigationController.pushViewController(self.paytmTransactionController!, animated: true)
        

        let storyboard : UIStoryboard? = UIStoryboard.init(name: "Main", bundle: nil);
        let window: UIWindow = ((UIApplication.shared.delegate?.window)!)!

        let objVC: UIViewController? = storyboard!.instantiateViewController(withIdentifier: "FlutterViewController")
        let aObjNavi = UINavigationController(rootViewController: objVC!)
        window.rootViewController = aObjNavi
        aObjNavi.pushViewController(self.paytmTransactionController!, animated: true)

使用此代码,我可以从 Swift 插件类触发 PayTM 视图控制器。

【讨论】:

以上是关于如何从用 Swift 编写的 Flutter 平台特定代码启动 ViewController?的主要内容,如果未能解决你的问题,请参考以下文章

我如何从用swift编写的iOS中完全导出csv文件?

如何在 Flutter 中为 Web 编写特定于平台的代码?

如何在 Flutter 插件的 Swift 编写的 iOS 部分中使用 Objective-C 框架

如何从用 C# 编写的抽象基类继承

如何从用 asp.net 中的 scriptmanager.registerclientscript 编写的 javascript 确认框返回值?

如何从用java编写的查询中将虚线字符串传递给oracle