separate implementation from boilerplate/bridging code
This commit is contained in:
parent
f322b67096
commit
89bb22046c
|
@ -0,0 +1,42 @@
|
|||
import Foundation
|
||||
import Keycard
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
class SmartCard {
|
||||
func nfcIsSupported() -> Bool {
|
||||
return KeycardController.isAvailable
|
||||
}
|
||||
|
||||
func initCard(channel: CardChannel, pin: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) throws -> Void {
|
||||
//let cmdSet = KeycardCommandSet(cardChannel: channel)
|
||||
//cmdSet.select().checkOK()
|
||||
//SmartCardSecrets s = SmartCardSecrets.generate(userPin);
|
||||
//cmdSet.init(s.getPin(), s.getPuk(), s.getPairingPassword()).checkOK();
|
||||
}
|
||||
|
||||
func signPinless(channel: CardChannel, hash: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) throws -> Void {
|
||||
let cmdSet = CashCommandSet(cardChannel: channel)
|
||||
let info = try CashApplicationInfo(cmdSet.select().checkOK().data)
|
||||
let message = self.hexToBytes(hash)
|
||||
let res = try cmdSet.sign(data: message).checkOK()
|
||||
|
||||
resolve(res.data.toHexString())
|
||||
}
|
||||
|
||||
func hexToBytes(_ hex: String) -> [UInt8] {
|
||||
var last = hex.first
|
||||
return hex.dropFirst().compactMap {
|
||||
guard
|
||||
let lastHexDigitValue = last?.hexDigitValue,
|
||||
let hexDigitValue = $0.hexDigitValue
|
||||
else {
|
||||
last = $0
|
||||
return nil
|
||||
}
|
||||
defer {
|
||||
last = nil
|
||||
}
|
||||
return UInt8(lastHexDigitValue * 16 + hexDigitValue)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
RCT_EXTERN_METHOD(nfcIsSupported:(RCTPromiseResolveBlock)resolve rejecter: (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXTERN_METHOD(openNfcSettings:(RCTPromiseResolveBlock)resolve rejecter: (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXTERN_METHOD(init:(NSString *)pin resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
|
||||
RCT_EXTERN_METHOD(signPinless:(NSString *)hash resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
|
||||
|
|
|
@ -3,106 +3,73 @@ import Keycard
|
|||
|
||||
@objc(StatusKeycard)
|
||||
class StatusKeycard: NSObject {
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
private(set) lazy var keycardController: KeycardController? = nil
|
||||
private(set) lazy var smartCard: SmartCard? = nil
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
|
||||
@objc
|
||||
func select() {
|
||||
if #available(iOS 13.0, *) {
|
||||
keycardController = KeycardController(onConnect: { [unowned self] channel in
|
||||
do {
|
||||
let cmdSet = KeycardCommandSet(cardChannel: channel)
|
||||
let info = try ApplicationInfo(cmdSet.select().checkOK().data)
|
||||
print(info)
|
||||
self.keycardController?.stop(alertMessage: "Success")
|
||||
} catch {
|
||||
print("Error: \(error)")
|
||||
self.keycardController?.stop(errorMessage: "Read error. Please try again.")
|
||||
}
|
||||
self.keycardController = nil
|
||||
}, onFailure: { [unowned self] error in
|
||||
print("Disconnected: \(error)")
|
||||
self.keycardController = nil
|
||||
})
|
||||
keycardController?.start(alertMessage: "Hold your iPhone near a Status Keycard.")
|
||||
} else {
|
||||
print("Unavailable")
|
||||
}
|
||||
self.smartCard = SmartCard()
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func openNfcSettings(_ resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) -> Void {
|
||||
reject("E_KEYCARD", "Unsupported on iOS", nil)
|
||||
}
|
||||
|
||||
@objc
|
||||
func signPinless(_ hash: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
||||
if #available(iOS 13.0, *) {
|
||||
DispatchQueue.main.async {
|
||||
self.keycardController = KeycardController(onConnect: { [unowned self] channel in
|
||||
do {
|
||||
let cmdSet = CashCommandSet(cardChannel: channel)
|
||||
let info = try CashApplicationInfo(cmdSet.select().checkOK().data)
|
||||
print("SELECT")
|
||||
print(info)
|
||||
|
||||
let message = self.hexToBytes(hash)
|
||||
let res = try cmdSet.sign(data: message).checkOK()
|
||||
print("SELECT")
|
||||
print(res)
|
||||
|
||||
self.keycardController?.stop(alertMessage: "SELECT: Success")
|
||||
print("DONE!")
|
||||
resolve(res.data.toHexString())
|
||||
} catch {
|
||||
reject("E_KEYCARD", "error", nil)
|
||||
print("Error: \(error)")
|
||||
self.keycardController?.stop(errorMessage: "Read error. Please try again.")
|
||||
}
|
||||
self.keycardController = nil
|
||||
}, onFailure: { [unowned self] error in
|
||||
reject("E_KEYCARD", "disconnected", nil)
|
||||
print("Disconnected: \(error)")
|
||||
self.keycardController = nil
|
||||
})
|
||||
self.keycardController?.start(alertMessage: "Hold your iPhone near a Status Keycard.")
|
||||
}
|
||||
} else {
|
||||
print("Unavailable")
|
||||
reject("E_KEYCARD", "unavailable", nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@objc
|
||||
func nfcIsSupported(_ resolve: RCTPromiseResolveBlock, rejecter _: RCTPromiseRejectBlock) -> Void {
|
||||
func nfcIsSupported(_ resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) -> Void {
|
||||
if #available(iOS 13.0, *) {
|
||||
resolve(KeycardController.isAvailable)
|
||||
resolve(smartCard?.nfcIsSupported())
|
||||
} else {
|
||||
resolve(false)
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func nfcIsEnabled(_ resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) -> Void {
|
||||
// On iOS NFC is always enabled (if available)
|
||||
nfcIsSupported(resolve, rejecter: reject)
|
||||
}
|
||||
|
||||
@objc
|
||||
func openNfcSettings(_ resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) -> Void {
|
||||
// NFC cannot be enabled/disabled
|
||||
reject("E_KEYCARD", "Unsupported on iOS", nil)
|
||||
}
|
||||
|
||||
@objc
|
||||
func `init`(_ pin: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
||||
keycardInvokation(reject) { [unowned self] channel in if #available(iOS 13.0, *) { try self.smartCard?.initCard(channel: channel, pin: pin, resolve: resolve, reject: reject) } }
|
||||
}
|
||||
|
||||
@objc
|
||||
func signPinless(_ hash: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
||||
keycardInvokation(reject) { [unowned self] channel in if #available(iOS 13.0, *) { try self.smartCard?.signPinless(channel: channel, hash: hash, resolve: resolve, reject: reject)} }
|
||||
}
|
||||
|
||||
@objc
|
||||
static func requiresMainQueueSetup() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func hexToBytes(_ hex: String) -> [UInt8] {
|
||||
var last = hex.first
|
||||
return hex.dropFirst().compactMap {
|
||||
guard
|
||||
let lastHexDigitValue = last?.hexDigitValue,
|
||||
let hexDigitValue = $0.hexDigitValue else {
|
||||
last = $0
|
||||
return nil
|
||||
}
|
||||
defer {
|
||||
last = nil
|
||||
}
|
||||
return UInt8(lastHexDigitValue * 16 + hexDigitValue)
|
||||
|
||||
func keycardInvokation(_ reject: @escaping RCTPromiseRejectBlock, body: @escaping (CardChannel) throws -> Void) {
|
||||
if #available(iOS 13.0, *) {
|
||||
DispatchQueue.main.async {
|
||||
var keycardController: KeycardController? = nil;
|
||||
keycardController = KeycardController(onConnect: { channel in
|
||||
do {
|
||||
try body(channel)
|
||||
keycardController?.stop(alertMessage: "Success")
|
||||
} catch {
|
||||
reject("E_KEYCARD", "error", error)
|
||||
keycardController?.stop(errorMessage: "Read error. Please try again.")
|
||||
}
|
||||
}, onFailure: { error in
|
||||
reject("E_KEYCARD", "disconnected", error)
|
||||
})
|
||||
|
||||
keycardController?.start(alertMessage: "Hold your iPhone near a Status Keycard.")
|
||||
}
|
||||
} else {
|
||||
reject("E_KEYCARD", "unavailable", nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
/* Begin PBXBuildFile section */
|
||||
65B8C633256D352A00C6A3EE /* Keycard in Frameworks */ = {isa = PBXBuildFile; productRef = 65B8C632256D352A00C6A3EE /* Keycard */; };
|
||||
F4FF95D7245B92E800C19C63 /* StatusKeycard.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FF95D6245B92E800C19C63 /* StatusKeycard.swift */; };
|
||||
F74884362577A87A00D908D2 /* SmartCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = F74884352577A87A00D908D2 /* SmartCard.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
|
@ -28,6 +29,7 @@
|
|||
B3E7B5891CC2AC0600A0062D /* StatusKeycard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StatusKeycard.m; sourceTree = "<group>"; };
|
||||
F4FF95D5245B92E700C19C63 /* StatusKeycard-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "StatusKeycard-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
F4FF95D6245B92E800C19C63 /* StatusKeycard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusKeycard.swift; sourceTree = "<group>"; };
|
||||
F74884352577A87A00D908D2 /* SmartCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartCard.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -53,6 +55,7 @@
|
|||
58B511D21A9E6C8500147676 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F74884352577A87A00D908D2 /* SmartCard.swift */,
|
||||
F4FF95D6245B92E800C19C63 /* StatusKeycard.swift */,
|
||||
B3E7B5891CC2AC0600A0062D /* StatusKeycard.m */,
|
||||
F4FF95D5245B92E700C19C63 /* StatusKeycard-Bridging-Header.h */,
|
||||
|
@ -124,6 +127,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F4FF95D7245B92E800C19C63 /* StatusKeycard.swift in Sources */,
|
||||
F74884362577A87A00D908D2 /* SmartCard.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue