swift 如何使一个函数与NFC调用同步?

qojgxg4l  于 2023-04-19  发布在  Swift
关注(0)|答案(2)|浏览(157)

我正在开发一个与ISO15693芯片交互的iOS NFC应用程序。
我需要通过NFC发送多个连续的命令,这些命令将被逐个执行,我需要等待上一个命令的结果来执行下一个命令。
从今天开始,我使用:

DispatchQueue.global().async {
    DispatchQueue.global().sync {
        var fw_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_FW_ID)
        print("Read FirmwareID \(fw_id)")
        var sw_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_SW_ID)
        print("Read SotfwareID \(sw_id)")
        var ex_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_EX_ID)
        print("Read ExternalID \(ex_id)")
    }
}

命令被一个接一个地正确执行。
但在sendI2C命令中:

public static func sendI2CCommand(tag: NFCISO15693Tag, command: UInt8) -> String{
    var res:String = ""
    tag.sendRequest(requestFlags: 0x12, commandCode: 0xD4, data: Data([0x04, 0x02, 0x00, command]))
    { result in
        switch result {
            case .success(( let responseFlag as NFCISO15693ResponseFlag, let response as Data)):
                break
            case .failure(_):
                break
            default:
                tag.sendRequest(requestFlags: 0x12, commandCode: 0xD5, data: Data([0x04, 0x02, 0x00]))
                { result in
                    switch result {
                        case .success(( let responseFlag as NFCISO15693ResponseFlag, let response2 as Data)):
                            break
                    case .failure(_):
                            break
                        default:
                            tag.sendRequest(requestFlags: 0x12, commandCode: 0xD2, data: Data([0x04, 0x00, 0x06]))
                            { result in
                                switch result {
                                    case .success(( let responseFlag as NFCISO15693ResponseFlag, let response2 as Data)):
                                        var foo = response2
                                        foo.insert(responseFlag.rawValue, at: 0)
                                        let responseHex = foo.hexadecimalCompressed.substring(with: 2..<4)
                                        res = responseHex
                                        break
                                    case .failure(_):
                                        break
                                    default:
                                        break
                                }
                            }
                            break
                    }
                }
                break
        }
    }
    usleep(50000)
    return res
}

sendI2CCommand正在发送两个连续的NFC命令。但是即使调用在DispatchQueue同步中,函数的结果也不会等待执行结束,并且我需要在返回值之前添加一个非常难看的usleep(50000),否则结果为空,并且不会返回任何数据,因为APDU命令需要一些时间才能执行。
我还尝试在函数中使用信号量,但它阻止了我的APDU命令:

let semaphore = DispatchSemaphore(value: 0)

在函数开始时

semaphore.signal()

等我处理完了
然后呢

semaphore.wait()

但在处理过程中崩溃。
那么,如何避免使用usleep函数等待处理结束后才返回函数中的结果呢?

0ejtzxu1

0ejtzxu11#

你不能像这样去做你所描述的事情。你会阻塞你不允许阻塞的共享线程。这正是async/await和CheckedContinuation的设计目的。

// Add `async throws` here
public static func sendI2CCommand(tag: NFCISO15693Tag, command: UInt8) async throws -> String {
    // Wrap in a CheckedThrowingContinuation
    try await withCheckedThrowingContinuation { continuation in
        // Call your async method
        tag.sendRequest(requestFlags: 0x12, commandCode: 0xD4, data: Data([0x04, 0x02, 0x00, command]))
        { result in

           // ... When you would return a value:
                                continuation.resume(returning: responseHex)

           // ... When there is an error:
           continuation.resume(throwing: error)
        }
    }
}

您必须确保只调用.resume一次。
然后你的顶层代码变成:

Task {
    do {
        var fw_id = try await NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_FW_ID)
        print("Read FirmwareID \(fw_id)")
        var sw_id = try await NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_SW_ID)
        print("Read SotfwareID \(sw_id)")
        var ex_id = try await NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_EX_ID)
        print("Read ExternalID \(ex_id)")
    } catch {
        // handle error
    }
}
z4iuyo4d

z4iuyo4d2#

如果你不想让sendI2CCommand返回结果,那么你可以使用DispatchGroup,如下所示:

public static func sendI2CCommand(tag: NFCISO15693Tag, command: UInt8) -> String{
    var res:String = ""
    var group = DispatchGroup()
    group.enter()
    DispatchQueue.global().async {
        tag.sendRequest(requestFlags: 0x12, commandCode: 0xD4, data: Data([0x04, 0x02, 0x00, command]))
        { result in
            switch result {
            case .success(( let responseFlag as NFCISO15693ResponseFlag, let response as Data)):
                group.leave()
            case .failure(_):
                group.leave()
            default:
                tag.sendRequest(requestFlags: 0x12, commandCode: 0xD5, data: Data([0x04, 0x02, 0x00]))
                { result in
                    switch result {
                    case .success(( let responseFlag as NFCISO15693ResponseFlag, let response2 as Data)):
                        group.leave()
                    case .failure(_):
                        group.leave()
                    default:
                        tag.sendRequest(requestFlags: 0x12, commandCode: 0xD2, data: Data([0x04, 0x00, 0x06]))
                        { result in
                            switch result {
                            case .success(( let responseFlag as NFCISO15693ResponseFlag, let response2 as Data)):
                                var foo = response2
                                foo.insert(responseFlag.rawValue, at: 0)
                                let responseHex = foo.hexadecimalCompressed.substring(with: 2..<4)
                                res = responseHex
                            case .failure(_):
                                break
                            default:
                                break
                            }
                            group.leave()
                        }
                    }
                }
            }
        }
    }

    group.wait()

    return res
}

您也可以从调用代码中删除DispatchGroup.global().sync

DispatchQueue.global().async {
    var fw_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_FW_ID)
    print("Read FirmwareID \(fw_id)")
    var sw_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_SW_ID)
    print("Read SotfwareID \(sw_id)")
    var ex_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_EX_ID)
    print("Read ExternalID \(ex_id)")
}

另一种选择是修改sendI2CCommand以接受完成处理程序:

public static func sendI2CCommand(tag: NFCISO15693Tag, command: UInt8, completion: (String) -> Void) -> String{
    var res = ""
    tag.sendRequest(requestFlags: 0x12, commandCode: 0xD4, data: Data([0x04, 0x02, 0x00, command]))
    { result in
        switch result {
        case .success(( let responseFlag as NFCISO15693ResponseFlag, let response as Data)):
            completion(res)
        case .failure(_):
            completion(res)
        default:
            tag.sendRequest(requestFlags: 0x12, commandCode: 0xD5, data: Data([0x04, 0x02, 0x00]))
            { result in
                switch result {
                case .success(( let responseFlag as NFCISO15693ResponseFlag, let response2 as Data)):
                    completion(res)
                case .failure(_):
                    completion(res)
                default:
                    tag.sendRequest(requestFlags: 0x12, commandCode: 0xD2, data: Data([0x04, 0x00, 0x06]))
                    { result in
                        switch result {
                        case .success(( let responseFlag as NFCISO15693ResponseFlag, let response2 as Data)):
                            var foo = response2
                            foo.insert(responseFlag.rawValue, at: 0)
                            let responseHex = foo.hexadecimalCompressed.substring(with: 2..<4)
                            res = responseHex
                        case .failure(_):
                            break
                        default:
                            break
                        }
                        completion(res)
                    }
                }
            }
        }
    }
}

然后调用代码看起来像这样:

DispatchQueue.global().async {
    NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_FW_ID) { fw_id in
        print("Read FirmwareID \(fw_id)")
        var sw_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_SW_ID) { sw_id in
            print("Read SotfwareID \(sw_id)")
            var ex_id = NFCUtils.sendI2CCommand(tag: self.tag!, command: NFCConstants.CONFIG_EX_ID) { ex_id in
                print("Read ExternalID \(ex_id)")
            }
        }
    }
}

相关问题