Search code examples
swiftprintingepos

Adding a completion handler to a framework's delegate function for response handling (Epson Printer)


So I have logic handling a queue of print jobs (Which is how all of our other printer integrations are handled) that calls specific functions for each printer (in this case, the sendCommand function) that does the print job, then reports back success / fail so we can properly handle retrying or removing the print job document.

func sendCommand(_ commands: Data, portName: String, portSettings: String, timeoutMilis: uint_least32_t) -> Bool {
    doPrinterSetup()
    let succPrint = cutAndCompletePrintJob(commands)
    if (connectedPrinter != nil) {
        disconnectPrinter()
    }
    while (!hasCompleted) { //Awful solution?
        sleep(1)
    }
    return printSuccess
}

Inside the printer's specific logic, I establish a connection, build the print data, ect... then send it. The response here I get is that the command has sent successfully.

func printData(_ data: Data) -> Bool {
    if connectedPrinter == nil {
        return false
    }
    connectedPrinter!.addCommand(data) //Add print information
    connectedPrinter!.addCut(EPOS2_CUT_FEED.rawValue) //Add cut
    connectedPrinter!.beginTransaction()
    connectedPrinter!.sendData(Int(EPOS2_PARAM_DEFAULT))
    return true
}

The EPOS framework has a separate delegate function, onPtrReceive that is called once the print job has actually finished printing, seen below, which actually reports the success or failure of printing the job:

//EPOS Delegate func
func onPtrReceive(_ printerObj: Epos2Printer!, code: Int32, status: Epos2PrinterStatusInfo!, printJobId: String!) { 
    hasCompleted = true
    
    print("🛑🛑🛑🛑Status in onPtrReceive \(makeErrorMessage(status))")
    
    dispPrinterWarnings(status)
    
    DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async(execute: {
        self.connectedPrinter!.endTransaction()
        self.connectedPrinter!.disconnect()
        self.connectedPrinter!.clearCommandBuffer()
        self.connectedPrinter!.setReceiveEventDelegate(nil)
        })
}

Now, the problem is that I have to wait for the response to sendCommand before responding to my parent class handling the print jobs, but I am at a loss on how to actually pass a completion handler to the framework delegate function and do it the proper way. Currently I have the global variable hasCompleted which I have a while loop stopping it from responding until onPtrReceive sets it to true, however I am under the impression this is an awful solution. Any suggestions on how to handle this the proper way or is this an acceptable strategy?


Solution

  • You could use closure for this problem.

    add a closure parameter to the sendCommand method and remove the Bool return value, hold this parameter as you class property.

    // Bool stand for the status of this printing job, true means success.
    var completion: ((Bool) -> Void)?
    
    func sendCommand(_ commands: Data, portName: String, portSettings: String, timeoutMilis: uint_least32_t, completion: @escaping (Bool -> Void)?) {
        self.completion = completion
        // ....
    }
    

    call this closure in the delegate method.

    func onPtrReceive(_ printerObj: Epos2Printer!, code: Int32, status: Epos2PrinterStatusInfo!, printJobId: String!) {
          
        let success = isPrintJobSuccess(by: code) // determine how a job is succeed
        self.completion?(success)
    }
    

    let's assume your class is called Printer, You could do this on you call site:

    let printer = Printer()
    printer.sendData(cmdData, portName: "port", portSettings: "settings", timeoutMilis: 1000, completion: { success in 
        if success {
            doSomeSuccessJob()
        }
    })
    // or you could use trailing closure
    printer.sendData(cmdData, portName: "port", portSettings: "settings", timeoutMilis: 1000) { success in 
        if success {
            doSomeSuccessJob()
        }
    }
    

    refs: https://docs.swift.org/swift-book/LanguageGuide/Closures.html