Skip to content

Commit

Permalink
Add tests, documentation and fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
tureck1y committed Nov 18, 2019
1 parent 9effb72 commit 2b777fe
Show file tree
Hide file tree
Showing 36 changed files with 895 additions and 194 deletions.
2 changes: 1 addition & 1 deletion TangemSdk.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

Pod::Spec.new do |s|
s.name = 'TangemSdk'
s.version = '0.3.0-alpha'
s.version = '0.4.0-alpha'
s.summary = 'Use TangemSdk for Tangem cards integration'

# This description is used to generate tags and improve search results.
Expand Down
24 changes: 24 additions & 0 deletions TangemSdk/TangemSdk.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
5D273A0B234F270200ACDA11 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D2739F4234F270200ACDA11 /* Error.swift */; };
5D273A0C234F270200ACDA11 /* PrivateKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D2739F5234F270200ACDA11 /* PrivateKey.swift */; };
5D273A0D234F270200ACDA11 /* Seed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D2739F6234F270200ACDA11 /* Seed.swift */; };
5D437EEB237BE980009C82A8 /* TlvTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D437EEA237BE980009C82A8 /* TlvTests.swift */; };
5D58B4BF233E4C6B00F33764 /* InputStream+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D58B4BE233E4C6B00F33764 /* InputStream+.swift */; };
5D58B4C1233E54A400F33764 /* Int+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D58B4C0233E54A400F33764 /* Int+.swift */; };
5D6795B2237AEFB60075A330 /* ApduTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D6795B1237AEFB60075A330 /* ApduTests.swift */; };
5D6A92D82344E2D700158457 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5D6A92DA2344E2D700158457 /* Localizable.strings */; };
5D6A92DC2344E3A500158457 /* Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D6A92DB2344E3A500158457 /* Localization.swift */; };
5D6A92DE2344F1DC00158457 /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D6A92DD2344F1DC00158457 /* Task.swift */; };
Expand All @@ -43,15 +45,19 @@
5D6A92EA2345FF2200158457 /* SingleCommandTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D6A92E92345FF2200158457 /* SingleCommandTask.swift */; };
5D6A92EC2346069700158457 /* CardManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D6A92EB2346069700158457 /* CardManager.swift */; };
5D6A92EE23463D1200158457 /* ScanTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D6A92ED23463D1200158457 /* ScanTask.swift */; };
5D713B2D236C3F6400E4F6FC /* StringUtilsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D713B2C236C3F6400E4F6FC /* StringUtilsTest.swift */; };
5D713B2F236C53E300E4F6FC /* ByteUtilsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D713B2E236C53E300E4F6FC /* ByteUtilsTest.swift */; };
5D7D5FB223449D4000058D69 /* CardEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D7D5FB123449D4000058D69 /* CardEnvironment.swift */; };
5D7D5FB423449F2300058D69 /* CommandSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D7D5FB323449F2300058D69 /* CommandSerializer.swift */; };
5D7F6644235F30B5008149B1 /* NDEFReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D7F6643235F30B5008149B1 /* NDEFReader.swift */; };
5D974B7E237D672F00DCDF21 /* Date+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D974B7D237D672F00DCDF21 /* Date+.swift */; };
5DA5B611233E11950058C720 /* CommandApdu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA5B610233E11950058C720 /* CommandApdu.swift */; };
5DA5B613233E11A50058C720 /* ResponseApdu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA5B612233E11A50058C720 /* ResponseApdu.swift */; };
5DA5B616233E12170058C720 /* Instruction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA5B615233E12170058C720 /* Instruction.swift */; };
5DA5B618233E124A0058C720 /* StatusWord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA5B617233E124A0058C720 /* StatusWord.swift */; };
5DA5B61A233E12780058C720 /* TlvTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA5B619233E12780058C720 /* TlvTag.swift */; };
5DA5B61C233E12B30058C720 /* Tlv.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA5B61B233E12B30058C720 /* Tlv.swift */; };
5DA7942A236C64D100B33DB5 /* IntUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA79429236C64D100B33DB5 /* IntUtilsTests.swift */; };
5DA80CA4231D247A00A50A10 /* TangemSdk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DA80C9A231D247A00A50A10 /* TangemSdk.framework */; };
5DA80CA9231D247A00A50A10 /* CryptoUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA80CA8231D247A00A50A10 /* CryptoUtilsTests.swift */; };
5DA80CAB231D247A00A50A10 /* TangemSdk.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DA80C9D231D247A00A50A10 /* TangemSdk.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -99,9 +105,11 @@
5D2739F4234F270200ACDA11 /* Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = "<group>"; };
5D2739F5234F270200ACDA11 /* PrivateKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrivateKey.swift; sourceTree = "<group>"; };
5D2739F6234F270200ACDA11 /* Seed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Seed.swift; sourceTree = "<group>"; };
5D437EEA237BE980009C82A8 /* TlvTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TlvTests.swift; sourceTree = "<group>"; };
5D53A4092369D7D10021DAF6 /* CoreNFC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreNFC.framework; path = System/Library/Frameworks/CoreNFC.framework; sourceTree = SDKROOT; };
5D58B4BE233E4C6B00F33764 /* InputStream+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "InputStream+.swift"; sourceTree = "<group>"; };
5D58B4C0233E54A400F33764 /* Int+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+.swift"; sourceTree = "<group>"; };
5D6795B1237AEFB60075A330 /* ApduTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApduTests.swift; sourceTree = "<group>"; };
5D6A92D92344E2D700158457 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
5D6A92DB2344E3A500158457 /* Localization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Localization.swift; sourceTree = "<group>"; };
5D6A92DD2344F1DC00158457 /* Task.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Task.swift; sourceTree = "<group>"; };
Expand All @@ -112,15 +120,19 @@
5D6A92E92345FF2200158457 /* SingleCommandTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleCommandTask.swift; sourceTree = "<group>"; };
5D6A92EB2346069700158457 /* CardManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardManager.swift; sourceTree = "<group>"; };
5D6A92ED23463D1200158457 /* ScanTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanTask.swift; sourceTree = "<group>"; };
5D713B2C236C3F6400E4F6FC /* StringUtilsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringUtilsTest.swift; sourceTree = "<group>"; };
5D713B2E236C53E300E4F6FC /* ByteUtilsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ByteUtilsTest.swift; sourceTree = "<group>"; };
5D7D5FB123449D4000058D69 /* CardEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardEnvironment.swift; sourceTree = "<group>"; };
5D7D5FB323449F2300058D69 /* CommandSerializer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandSerializer.swift; sourceTree = "<group>"; };
5D7F6643235F30B5008149B1 /* NDEFReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NDEFReader.swift; sourceTree = "<group>"; };
5D974B7D237D672F00DCDF21 /* Date+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+.swift"; sourceTree = "<group>"; };
5DA5B610233E11950058C720 /* CommandApdu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandApdu.swift; sourceTree = "<group>"; };
5DA5B612233E11A50058C720 /* ResponseApdu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseApdu.swift; sourceTree = "<group>"; };
5DA5B615233E12170058C720 /* Instruction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instruction.swift; sourceTree = "<group>"; };
5DA5B617233E124A0058C720 /* StatusWord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusWord.swift; sourceTree = "<group>"; };
5DA5B619233E12780058C720 /* TlvTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TlvTag.swift; sourceTree = "<group>"; };
5DA5B61B233E12B30058C720 /* Tlv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tlv.swift; sourceTree = "<group>"; };
5DA79429236C64D100B33DB5 /* IntUtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntUtilsTests.swift; sourceTree = "<group>"; };
5DA80C9A231D247A00A50A10 /* TangemSdk.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TangemSdk.framework; sourceTree = BUILT_PRODUCTS_DIR; };
5DA80C9D231D247A00A50A10 /* TangemSdk.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TangemSdk.h; sourceTree = "<group>"; };
5DA80CA3231D247A00A50A10 /* TangemSdkTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TangemSdkTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -302,6 +314,11 @@
5DA80CA8231D247A00A50A10 /* CryptoUtilsTests.swift */,
5DA80CAA231D247A00A50A10 /* Info.plist */,
5DAD449D236B2435006C38F8 /* DataExtensionTests.swift */,
5D713B2C236C3F6400E4F6FC /* StringUtilsTest.swift */,
5D713B2E236C53E300E4F6FC /* ByteUtilsTest.swift */,
5DA79429236C64D100B33DB5 /* IntUtilsTests.swift */,
5D6795B1237AEFB60075A330 /* ApduTests.swift */,
5D437EEA237BE980009C82A8 /* TlvTests.swift */,
);
path = TangemSdkTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -346,6 +363,7 @@
5D58B4C0233E54A400F33764 /* Int+.swift */,
5DB4406C234B740B00AC39F1 /* Data+.swift */,
5DB4406E234B750200AC39F1 /* String+.swift */,
5D974B7D237D672F00DCDF21 /* Date+.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -486,6 +504,7 @@
5D273A01234F270200ACDA11 /* add_scalar.c in Sources */,
5D6A92E52345F2B200158457 /* SignCommand.swift in Sources */,
5D273A06234F270200ACDA11 /* sha512.c in Sources */,
5D974B7E237D672F00DCDF21 /* Date+.swift in Sources */,
5D273A0A234F270200ACDA11 /* PublicKey.swift in Sources */,
5D2739FD234F270200ACDA11 /* sc.c in Sources */,
5D273A0B234F270200ACDA11 /* Error.swift in Sources */,
Expand Down Expand Up @@ -524,8 +543,13 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5DA7942A236C64D100B33DB5 /* IntUtilsTests.swift in Sources */,
5DA80CA9231D247A00A50A10 /* CryptoUtilsTests.swift in Sources */,
5D713B2D236C3F6400E4F6FC /* StringUtilsTest.swift in Sources */,
5DAD449E236B2435006C38F8 /* DataExtensionTests.swift in Sources */,
5D6795B2237AEFB60075A330 /* ApduTests.swift in Sources */,
5D713B2F236C53E300E4F6FC /* ByteUtilsTest.swift in Sources */,
5D437EEB237BE980009C82A8 /* TlvTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1110"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5DA80C99231D247A00A50A10"
BuildableName = "TangemSdk.framework"
BlueprintName = "TangemSdk"
ReferencedContainer = "container:TangemSdk.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5DA80CA2231D247A00A50A10"
BuildableName = "TangemSdkTests.xctest"
BlueprintName = "TangemSdkTests"
ReferencedContainer = "container:TangemSdk.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5DA80C99231D247A00A50A10"
BuildableName = "TangemSdk.framework"
BlueprintName = "TangemSdk"
ReferencedContainer = "container:TangemSdk.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
42 changes: 37 additions & 5 deletions TangemSdk/TangemSdk/CardManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import Foundation
import CoreNFC

/// The main interface of Tangem SDK that allows your app to communicate with Tangem cards.
public final class CardManager {
public static var isNFCAvailable: Bool {
#if canImport(CoreNFC)
Expand All @@ -21,7 +21,10 @@ public final class CardManager {

public var isBusy: Bool = false

/// `cardReader` is an interface that is responsible for NFC connection and transfer of data to and from the Tangem Card.
private let cardReader: CardReader

/// An interface that allows interaction with users and shows relevant UI.
private let cardManagerDelegate: CardManagerDelegate
private var cardEnvironmentRepository: [String:CardEnvironment] = [:]
private var currentTask: AnyTask?
Expand All @@ -31,16 +34,43 @@ public final class CardManager {
self.cardManagerDelegate = cardManagerDelegate
}

/**
* To start using any card, you first need to read it using the `scanCard()` method.
* This method launches an NFC session, and once it’s connected with the card,
* it obtains the card data. Optionally, if the card contains a wallet (private and public key pair),
* it proves that the wallet owns a private key that corresponds to a public one.
*
* - Parameter callback:This method will send the following events in a callback:
* `onRead(Card)` after completing `ReadCommand`
* `onVerify(Bool)` after completing `CheckWalletCommand`
* `completion(TaskError?)` with an error field null after successful completion of a task or
* with an error if some error occurs.
*/
public func scanCard(callback: @escaping (TaskEvent<ScanEvent>) -> Void) {
let task = ScanTask()
runTask(task, callback: callback)
}

/**
* This method allows you to sign one or multiple hashes.
* Simultaneous signing of array of hashes in a single `SignCommand` is required to support
* Bitcoin-type multi-input blockchains (UTXO).
* The `SignCommand` will return a corresponding array of signatures.
*
* - Parameter callback: This method will send the following events in a callback:
* `SignResponse` after completing `SignCommand`
* `completion(TaskError?)` with an error field null after successful completion of a task or with an error if some error occurs.
* Please note that Tangem cards usually protect the signing with a security delay
* that may last up to 90 seconds, depending on a card.
* It is for `CardManagerDelegate` to notify users of security delay.
* - Parameter hashes: Array of transaction hashes. It can be from one or up to ten hashes of the same length.
* - Parameter cardId: CID, Unique Tangem card ID number
*/
@available(iOS 13.0, *)
public func sign(hashes: [Data], cardId: String, callback: @escaping (TaskEvent<SignResponse>) -> Void) {
var signHashesCommand: SignHashesCommand
var signCommand: SignCommand
do {
signHashesCommand = try SignHashesCommand(hashes: hashes, cardId: cardId)
signCommand = try SignCommand(hashes: hashes, cardId: cardId)
} catch {
if let taskError = error as? TaskError {
callback(.completion(taskError))
Expand All @@ -50,10 +80,11 @@ public final class CardManager {
return
}

let task = SingleCommandTask(signHashesCommand)
let task = SingleCommandTask(signCommand)
runTask(task, cardId: cardId, callback: callback)
}

/// Allows to run a custom task created outside of this SDK.
public func runTask<T>(_ task: Task<T>, cardId: String? = nil, callback: @escaping (TaskEvent<T>) -> Void) {
guard CardManager.isNFCAvailable else {
callback(.completion(TaskError.unsupported))
Expand All @@ -67,7 +98,7 @@ public final class CardManager {

currentTask = task
isBusy = true
task.cardReader = cardReader
task.reader = cardReader
task.delegate = cardManagerDelegate
let environment = fetchCardEnvironment(for: cardId)

Expand All @@ -87,6 +118,7 @@ public final class CardManager {
}
}

/// Allows to run a custom command created outside of this SDK.
@available(iOS 13.0, *)
public func runCommand<T: CommandSerializer>(_ command: T, cardId: String? = nil, callback: @escaping (TaskEvent<T.CommandResponse>) -> Void) {
let task = SingleCommandTask<T>(command)
Expand Down
8 changes: 7 additions & 1 deletion TangemSdk/TangemSdk/CardManagerDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@
import Foundation
import UIKit

/// Allows interaction with users and shows visual elements.
/// Its default implementation, `DefaultCardManagerDelegate`, is in our module.
public protocol CardManagerDelegate: class {
func showAlertMessage(_ text: String)
func showSecurityDelay(remainingMilliseconds: Int)

/// It is called when security delay is triggered by the card. A user is expected to hold the card until the security delay is over.
func showSecurityDelay(remainingMilliseconds: Int)

/// It is called when a user is expected to enter pin code.
func requestPin(completion: @escaping () -> Result<String, Error>)
}

Expand Down
Loading

0 comments on commit 2b777fe

Please sign in to comment.