-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from shakurocom/Keychain-v1.0.0
Keychain v1.0.0
- Loading branch information
Showing
29 changed files
with
2,299 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
swiftlint_version: 0.43.1 | ||
|
||
excluded: # paths to ignore during linting. Takes precedence over `included`. | ||
- Pods | ||
|
||
analyzer_rules: # Rules run by `swiftlint analyze` (experimental) | ||
- unused_declaration | ||
- unused_import | ||
|
||
deployment_target: | ||
iOS_deployment_target: 10.0 | ||
|
||
closure_body_length: | ||
warning: 30 | ||
error: 30 | ||
|
||
identifier_name: | ||
max_length: 150 | ||
|
||
file_length: | ||
warning: 1500 | ||
error: 1500 | ||
#ignore_comment_only_lines: true | ||
|
||
function_body_length: | ||
warning: 200 | ||
error: 200 | ||
|
||
function_parameter_count: | ||
warning: 8 | ||
error: 8 | ||
|
||
line_length: | ||
warning: 1000 | ||
error: 1000 | ||
|
||
type_body_length: | ||
warning: 1000 | ||
error: 1000 | ||
|
||
type_name: | ||
max_length: 150 | ||
|
||
cyclomatic_complexity: | ||
warning: 30 | ||
error: 30 | ||
|
||
opt_in_rules: | ||
- closure_body_length | ||
# - closure_end_indentation # disabled, because xcode's block indentation depends on `[weak self]` | ||
- closure_spacing | ||
- contains_over_filter_count | ||
- contains_over_filter_is_empty | ||
- discouraged_optional_boolean | ||
- duplicate_imports | ||
- empty_collection_literal | ||
- empty_count | ||
- empty_string | ||
- explicit_init | ||
- fatal_error_message | ||
# - file_name # disabled, because results are not consistent | ||
# - file_header | ||
- first_where | ||
- force_unwrapping | ||
- identical_operands | ||
# - indentation_width # disabled, because multiline arguments are not recognized properly | ||
- inert_defer | ||
- last_where | ||
- legacy_random | ||
- let_var_whitespace | ||
# - multiline_arguments | ||
- multiline_parameters | ||
- multiple_closures_with_trailing_closure | ||
- no_space_in_method_call | ||
# - object_literal | ||
- operator_usage_whitespace | ||
- optional_enum_case_matching | ||
- prefer_self_type_over_type_of_self | ||
- prefixed_toplevel_constant | ||
- private_action | ||
- private_over_fileprivate | ||
- prohibited_super_call | ||
- reduce_into | ||
- redundant_nil_coalescing | ||
- single_test_class | ||
- sorted_first_last | ||
# - sorted_imports | ||
- strict_fileprivate | ||
- strong_iboutlet | ||
- toggle_bool | ||
# - type_contents_order # https://realm.github.io/SwiftLint/type_contents_order.html | ||
# - trailing_closure | ||
- unowned_variable_capture | ||
- unused_setter_value | ||
- vertical_parameter_alignment_on_call | ||
- yoda_condition |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>IDEDidComputeMac32BitWarning</key> | ||
<true/> | ||
</dict> | ||
</plist> |
8 changes: 8 additions & 0 deletions
8
Keychain.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>PreviewsEnabled</key> | ||
<false/> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
// | ||
// Copyright (c) 2017 Shakuro (https://shakuro.com/) | ||
// Sergey Laschuk | ||
// | ||
|
||
@testable import Keychain_Framework | ||
import XCTest | ||
|
||
class KeychainWrapperTests: XCTestCase { | ||
|
||
private struct KeychainData: Codable { | ||
let value1: String | ||
let value2: String | ||
} | ||
|
||
private enum Constant { | ||
static let serviceName: String = "com.shakuro.keychainWrapperTest" | ||
static let accountName: String = "TestingValues" | ||
static let accountName2: String = "TestingValues2" | ||
static let itemName: String = "" | ||
static let invalidServiceName: String = serviceName + ".invalid" | ||
static let etalonData: KeychainData = KeychainData(value1: "111", value2: "222") | ||
static let etalonData2: KeychainData = KeychainData(value1: "1112", value2: "2223") | ||
} | ||
|
||
override func setUp() { | ||
super.setUp() | ||
// Put setup code here. This method is called before the invocation of each test method in the class. | ||
} | ||
|
||
override func tearDown() { | ||
// Put teardown code here. This method is called after the invocation of each test method in the class. | ||
super.tearDown() | ||
try? KeychainWrapper.removeKeychainItems(serviceName: Constant.serviceName) | ||
} | ||
|
||
func testReadRemoveCollection() { | ||
// 1) read - there should be no items in keychain | ||
do { | ||
let emptyItemsInfo: [KeychainWrapper.ItemInfo] = try KeychainWrapper.itemsInfo(serviceName: Constant.serviceName) | ||
XCTAssertTrue(emptyItemsInfo.isEmpty) | ||
let emptyItems: [KeychainWrapper.Item<KeychainData>] = try KeychainWrapper.keychainItems(serviceName: Constant.serviceName) | ||
XCTAssertTrue(emptyItems.isEmpty) | ||
} catch let error { | ||
XCTFail("\(error)") | ||
} | ||
|
||
// 2) save etalon data | ||
do { | ||
let keychainItem = KeychainWrapper.Item(serviceName: Constant.serviceName, | ||
account: Constant.accountName, | ||
itemName: Constant.itemName, | ||
accessGroup: nil, | ||
secValue: Constant.etalonData) | ||
try KeychainWrapper.saveKeychainItem(keychainItem) | ||
} catch let error { | ||
XCTFail("\(error)") | ||
} | ||
do { | ||
let keychainItem = KeychainWrapper.Item(serviceName: Constant.serviceName, | ||
account: Constant.accountName2, | ||
itemName: Constant.itemName, | ||
accessGroup: nil, | ||
secValue: Constant.etalonData2) | ||
try KeychainWrapper.saveKeychainItem(keychainItem) | ||
} catch let error { | ||
XCTFail("\(error)") | ||
} | ||
|
||
// 3) read data back | ||
do { | ||
let itemsInfo: [KeychainWrapper.ItemInfo] = try KeychainWrapper.itemsInfo(serviceName: Constant.serviceName) | ||
XCTAssertTrue(itemsInfo.count == 2) | ||
let items: [KeychainWrapper.Item<KeychainData>] = try KeychainWrapper.keychainItems(serviceName: Constant.serviceName) | ||
XCTAssertTrue(items.count == itemsInfo.count) | ||
let itemInfoMap: [String: KeychainWrapper.ItemInfo] = itemsInfo.reduce(into: [:], { (res, info) in | ||
res[info.account] = info | ||
}) | ||
XCTAssertTrue(itemInfoMap.count == itemsInfo.count) | ||
let itemMap: [String: KeychainWrapper.Item<KeychainData>] = items.reduce(into: [:], { (res, info) in | ||
res[info.account] = info | ||
}) | ||
XCTAssertTrue(itemMap.count == items.count) | ||
|
||
let accInfo1 = try assertNotNilAndUnwrap(itemInfoMap[Constant.accountName]) | ||
let acc1 = try assertNotNilAndUnwrap(itemMap[Constant.accountName]) | ||
XCTAssertEqual(acc1.secValue.value1, Constant.etalonData.value1) | ||
XCTAssertEqual(acc1.secValue.value2, Constant.etalonData.value2) | ||
try [accInfo1, acc1.info].forEach { (info) in | ||
XCTAssertEqual(info.serviceName, Constant.serviceName) | ||
XCTAssertEqual(info.account, Constant.accountName) | ||
let itemNameNotNil = try assertNotNilAndUnwrap(info.itemName) | ||
XCTAssertEqual(itemNameNotNil, Constant.itemName) | ||
} | ||
|
||
let accInfo2 = try assertNotNilAndUnwrap(itemInfoMap[Constant.accountName2]) | ||
let acc2 = try assertNotNilAndUnwrap(itemMap[Constant.accountName2]) | ||
XCTAssertEqual(acc2.secValue.value1, Constant.etalonData2.value1) | ||
XCTAssertEqual(acc2.secValue.value2, Constant.etalonData2.value2) | ||
try [accInfo2, acc2.info].forEach { (info) in | ||
XCTAssertEqual(info.serviceName, Constant.serviceName) | ||
XCTAssertEqual(info.account, Constant.accountName2) | ||
let itemNameNotNil = try assertNotNilAndUnwrap(info.itemName) | ||
XCTAssertEqual(itemNameNotNil, Constant.itemName) | ||
} | ||
} catch let error { | ||
XCTFail("\(error)") | ||
} | ||
|
||
// 6) delete items | ||
do { | ||
try KeychainWrapper.removeKeychainItems(serviceName: Constant.serviceName) | ||
} catch let error { | ||
XCTFail("\(error)") | ||
} | ||
|
||
// 7) read after deletion | ||
do { | ||
let emptyItemsInfo: [KeychainWrapper.ItemInfo] = try KeychainWrapper.itemsInfo(serviceName: Constant.serviceName) | ||
XCTAssertTrue(emptyItemsInfo.isEmpty) | ||
} catch let error { | ||
XCTFail("\(error)") | ||
} | ||
} | ||
|
||
func testReadWriteRemove() { | ||
// 1) read - there should be no items in keychain | ||
do { | ||
let nilItem: KeychainWrapper.Item<KeychainData>? = try KeychainWrapper.keychainItem(serviceName: Constant.serviceName, account: Constant.accountName) | ||
XCTAssertNil(nilItem, "unexpected item found in keychain") | ||
} catch let error { | ||
XCTFail("\(error)") | ||
} | ||
do { | ||
let nilItem: KeychainWrapper.Item<KeychainData>? = try KeychainWrapper.keychainItem(serviceName: Constant.invalidServiceName, account: Constant.accountName) | ||
XCTAssertNil(nilItem, "unexpected item found in keychain") | ||
} catch let error { | ||
XCTFail("\(error)") | ||
} | ||
|
||
// 2) save etalon data | ||
do { | ||
let keychainItem = KeychainWrapper.Item(serviceName: Constant.serviceName, account: Constant.accountName, itemName: Constant.itemName, accessGroup: nil, secValue: Constant.etalonData) | ||
try KeychainWrapper.saveKeychainItem(keychainItem) | ||
} catch let error { | ||
XCTFail("\(error)") | ||
} | ||
|
||
// 3) read data back | ||
do { | ||
let keychainItem: KeychainWrapper.Item<KeychainData>? = try KeychainWrapper.keychainItem(serviceName: Constant.serviceName, account: Constant.accountName) | ||
let keychainItemNotNil = try assertNotNilAndUnwrap(keychainItem) | ||
XCTAssertEqual(keychainItemNotNil.serviceName, Constant.serviceName) | ||
XCTAssertEqual(keychainItemNotNil.account, Constant.accountName) | ||
let itemNameNotNil = try assertNotNilAndUnwrap(keychainItemNotNil.itemName) | ||
XCTAssertEqual(itemNameNotNil, Constant.itemName) | ||
XCTAssertEqual(keychainItemNotNil.secValue.value1, Constant.etalonData.value1) | ||
XCTAssertEqual(keychainItemNotNil.secValue.value2, Constant.etalonData.value2) | ||
} catch let error { | ||
XCTFail("\(error)") | ||
} | ||
|
||
// 4) read data with invalid service name | ||
do { | ||
let keychainItem: KeychainWrapper.Item<KeychainData>? = try KeychainWrapper.keychainItem(serviceName: Constant.invalidServiceName, account: Constant.accountName) | ||
XCTAssertNil(keychainItem) | ||
} catch let error { | ||
XCTFail("\(error)") | ||
} | ||
|
||
// 5) read data of invalid type | ||
do { | ||
let keychainItem: KeychainWrapper.Item<String>? = try KeychainWrapper.keychainItem(serviceName: Constant.serviceName, account: Constant.accountName) | ||
XCTFail("there should be an error in previous line. Returned result: \(String(describing: keychainItem))") | ||
} catch let error as KeychainWrapper.Error { | ||
switch error { | ||
case .unexpectedKeychainItemData: | ||
// this is the expected result | ||
break | ||
|
||
default: | ||
XCTFail("\(error)") | ||
} | ||
} catch let error { | ||
XCTFail("\(error)") | ||
} | ||
|
||
// 6) delete item | ||
do { | ||
try KeychainWrapper.removeKeychainItem(serviceName: Constant.serviceName, account: Constant.accountName) | ||
} catch let error { | ||
XCTFail("\(error)") | ||
} | ||
|
||
// 7) read after deletion | ||
do { | ||
let nilItem: KeychainWrapper.Item<KeychainData>? = try KeychainWrapper.keychainItem(serviceName: Constant.serviceName, account: Constant.accountName) | ||
XCTAssertNil(nilItem) | ||
} catch let error { | ||
XCTFail("\(error)") | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// | ||
// | ||
// | ||
|
||
import XCTest | ||
|
||
struct UnexpectedNilError: Error {} | ||
|
||
public func assertNotNilAndUnwrap<T>(_ variable: T?, message: String = "Unexpected nil variable", file: StaticString = #file, line: UInt = #line) throws -> T { | ||
guard let variable = variable else { | ||
XCTFail(message, file: file, line: line) | ||
throw UnexpectedNilError() | ||
} | ||
return variable | ||
} |
Oops, something went wrong.