Skip to content

Commit 44da0ea

Browse files
Merge pull request #3 from GoodRequest/feature/authetication-syncronizability
feat: changed implementation of keychain wrapper to use Keychainaccess lib
2 parents bf82128 + 154e960 commit 44da0ea

File tree

15 files changed

+430
-724
lines changed

15 files changed

+430
-724
lines changed

GoodPersistence-Sample/GoodPersistence-Sample.xcodeproj/project.pbxproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@
284284
attributes = {
285285
BuildIndependentTargetsInParallel = 1;
286286
LastSwiftUpdateCheck = 1410;
287-
LastUpgradeCheck = 1410;
287+
LastUpgradeCheck = 1520;
288288
TargetAttributes = {
289289
EACEC4592995B363008242AA = {
290290
CreatedOnToolsVersion = 14.1;
@@ -401,6 +401,7 @@
401401
DEBUG_INFORMATION_FORMAT = dwarf;
402402
ENABLE_STRICT_OBJC_MSGSEND = YES;
403403
ENABLE_TESTABILITY = YES;
404+
ENABLE_USER_SCRIPT_SANDBOXING = YES;
404405
GCC_C_LANGUAGE_STANDARD = gnu11;
405406
GCC_DYNAMIC_NO_PIC = NO;
406407
GCC_NO_COMMON_BLOCKS = YES;
@@ -461,6 +462,7 @@
461462
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
462463
ENABLE_NS_ASSERTIONS = NO;
463464
ENABLE_STRICT_OBJC_MSGSEND = YES;
465+
ENABLE_USER_SCRIPT_SANDBOXING = YES;
464466
GCC_C_LANGUAGE_STANDARD = gnu11;
465467
GCC_NO_COMMON_BLOCKS = YES;
466468
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -486,6 +488,7 @@
486488
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
487489
CODE_SIGN_STYLE = Automatic;
488490
CURRENT_PROJECT_VERSION = 1;
491+
DEVELOPMENT_TEAM = FFZN8CA2AB;
489492
GENERATE_INFOPLIST_FILE = YES;
490493
INFOPLIST_FILE = "GoodPersistence-Sample/Application/Info.plist";
491494
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
@@ -514,6 +517,7 @@
514517
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
515518
CODE_SIGN_STYLE = Automatic;
516519
CURRENT_PROJECT_VERSION = 1;
520+
DEVELOPMENT_TEAM = FFZN8CA2AB;
517521
GENERATE_INFOPLIST_FILE = YES;
518522
INFOPLIST_FILE = "GoodPersistence-Sample/Application/Info.plist";
519523
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;

GoodPersistence-Sample/GoodPersistence-Sample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

GoodPersistence-Sample/GoodPersistence-Sample/Application/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
33
<plist version="1.0">
44
<dict>
5+
<key>NSFaceIDUsageDescription</key>
6+
<string>Using FaceID to access biometry protected data in keychain </string>
57
<key>CFBundleDocumentTypes</key>
68
<array>
79
<dict>

GoodPersistence-Sample/GoodPersistence-Sample/Helpers/Constants.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,12 @@ struct Constants {
3737
struct Home {
3838

3939
static let title = "Time"
40-
static let save = "Save the time to cache"
40+
static let saveToUserDefaults = "Save the time to UserDefaults"
41+
static let saveToKeychain = "Save the time to KeyChain"
4142
static let reset = "Reset cache"
4243
static let aboutApp = "About app"
43-
static let savedTime = "Saved time:\n"
44+
static let userDefaultsTime = "UserDefaults saved time:\n"
45+
static let keychainTime = "Keychain saved time:\n"
4446

4547
}
4648

GoodPersistence-Sample/GoodPersistence-Sample/Managers/CacheManager/CacheManager.swift

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,40 @@ import GoodPersistence
1010

1111
final class CacheManager: CacheManagerType {
1212

13-
@UserDefaultValue("savedTime", defaultValue: "")
14-
var savedTime: String
13+
@UserDefaultValue("savedTimeUserDefaults", defaultValue: "")
14+
var savedTimeUserDefaults: String
1515

16-
lazy var savedTimePublisher = _savedTime.publisher
16+
lazy var savedTimeUserDefaultsPublisher = _savedTimeUserDefaults.publisher
1717
.dropFirst()
1818
.removeDuplicates()
1919
.eraseToAnyPublisher()
2020

21+
@KeychainValue(
22+
"savedTimeKeychain",
23+
defaultValue: "",
24+
accessibility: .whenPasscodeSetThisDeviceOnly,
25+
authenticationPolicy: [.biometryAny]
26+
)
27+
var savedTimeKeychain: String
2128

22-
func save(value: String) {
23-
savedTime = value
29+
lazy var savedTimeKeychainPublisher = _savedTimeKeychain.valuePublisher
30+
.removeDuplicates()
31+
.eraseToAnyPublisher()
32+
33+
@KeychainValue("savedNumberKeychain", defaultValue: 0)
34+
var savedNumberKeychain: Int
35+
lazy var savedNumber = $savedNumberKeychain
36+
37+
func saveToUserDefaults(value: String) {
38+
savedTimeUserDefaults = value
39+
}
40+
41+
func saveToKeychain(value: String) {
42+
savedTimeKeychain = value
2443
}
2544

2645
func resetToDefault() {
27-
savedTime = ""
46+
savedTimeUserDefaults = ""
47+
savedTimeKeychain = ""
2848
}
2949
}

GoodPersistence-Sample/GoodPersistence-Sample/Managers/CacheManager/CacheManagerType.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,22 @@
66
//
77

88
import Combine
9+
import GoodPersistence
910

1011
protocol CacheManagerType: AnyObject {
1112

12-
var savedTime: String { get }
13+
var savedTimeUserDefaults: String { get }
1314

14-
var savedTimePublisher: AnyPublisher<String, Never> { get }
15+
var savedTimeUserDefaultsPublisher: AnyPublisher<String, Never> { get }
16+
17+
var savedTimeKeychain: String { get }
18+
19+
var savedTimeKeychainPublisher: AnyPublisher<String, KeychainError> { get }
1520

16-
func save(value: String)
21+
var savedNumberKeychain: Int { get set }
22+
23+
func saveToUserDefaults(value: String)
24+
func saveToKeychain(value: String)
1725
func resetToDefault()
1826

1927
}

GoodPersistence-Sample/GoodPersistence-Sample/Screens/Home/HomeViewController.swift

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ final class HomeViewController: BaseViewController<HomeViewModel> {
2020
return counterValueLabel
2121
}()
2222

23-
private let savedValueLabel: MultiLineLabel = {
23+
private let savedUserDefaultsValueLabel: MultiLineLabel = {
2424
let counterValueLabel = MultiLineLabel.create(
2525
font: .systemFont(ofSize: 32, weight: .heavy),
2626
alignment: .center
@@ -30,10 +30,29 @@ final class HomeViewController: BaseViewController<HomeViewModel> {
3030

3131
return counterValueLabel
3232
}()
33+
34+
private let savedKeychainValueLabel: MultiLineLabel = {
35+
let counterValueLabel = MultiLineLabel.create(
36+
font: .systemFont(ofSize: 32, weight: .heavy),
37+
alignment: .center
38+
)
39+
counterValueLabel.translatesAutoresizingMaskIntoConstraints = false
40+
counterValueLabel.isHidden = true
3341

34-
private let saveButton: ActionButton = {
42+
return counterValueLabel
43+
}()
44+
45+
private let saveButtonUserDefaults: ActionButton = {
3546
let button = ActionButton()
36-
button.setTitle(Constants.Texts.Home.save, for: .normal)
47+
button.setTitle(Constants.Texts.Home.saveToUserDefaults, for: .normal)
48+
button.updateActivityIndicatorColor(color: .black)
49+
50+
return button
51+
}()
52+
53+
private let saveButtonKeychain: ActionButton = {
54+
let button = ActionButton()
55+
button.setTitle(Constants.Texts.Home.saveToKeychain, for: .normal)
3756
button.updateActivityIndicatorColor(color: .black)
3857

3958
return button
@@ -52,6 +71,15 @@ final class HomeViewController: BaseViewController<HomeViewModel> {
5271

5372
return button
5473
}()
74+
75+
private let topStackView: UIStackView = {
76+
let topStackView = UIStackView()
77+
topStackView.translatesAutoresizingMaskIntoConstraints = false
78+
topStackView.axis = .vertical
79+
topStackView.spacing = 16
80+
81+
return topStackView
82+
}()
5583

5684
private let bottomStackView: UIStackView = {
5785
let bottomStackView = UIStackView()
@@ -89,10 +117,10 @@ extension HomeViewController {
89117
navigationController?.navigationBar.prefersLargeTitles = true
90118
title = Constants.Texts.Home.title
91119

92-
[saveButton, resetButton, aboutAppButton].forEach { bottomStackView.addArrangedSubview($0) }
93-
[bottomStackView, counterValueLabel].forEach {view.addSubview($0) }
120+
[savedUserDefaultsValueLabel, savedKeychainValueLabel].forEach { topStackView.addArrangedSubview($0) }
121+
[saveButtonUserDefaults, saveButtonKeychain, resetButton, aboutAppButton].forEach { bottomStackView.addArrangedSubview($0) }
122+
[topStackView, bottomStackView, counterValueLabel].forEach {view.addSubview($0) }
94123

95-
view.addSubview(savedValueLabel)
96124
setupConstraints()
97125
}
98126

@@ -101,9 +129,10 @@ extension HomeViewController {
101129
counterValueLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
102130
counterValueLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
103131

104-
savedValueLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
105-
savedValueLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
106-
132+
topStackView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -32),
133+
topStackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: -16),
134+
topStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
135+
107136
bottomStackView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -32),
108137
bottomStackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -16),
109138
bottomStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
@@ -124,18 +153,30 @@ extension HomeViewController {
124153
.assign(to: \.text, on: counterValueLabel, ownership: .weak)
125154
.store(in: &cancellables)
126155

127-
reactor.savedValuePublisher
156+
reactor.savedUserDefaultsValuePublisher
157+
.removeDuplicates()
158+
.sink { [weak self] in self?.handle(savedUserDefaultsValue: $0) }
159+
.store(in: &cancellables)
160+
161+
reactor.savedKeychainValuePublisher
128162
.removeDuplicates()
129-
.sink { [weak self] in self?.handle(savedValue: $0) }
163+
.sink { [weak self] in self?.handle(savedKeychainValue: $0) }
130164
.store(in: &cancellables)
131165
}
132166

133167
func bindActions(reactor: HomeViewModel) {
134-
saveButton.publisher(for: .touchUpInside)
168+
saveButtonUserDefaults.publisher(for: .touchUpInside)
135169
.sink { [unowned self] in
136170
guard let timerValue = self.counterValueLabel.text else { return }
137171

138-
reactor.saveToCache(value: timerValue) }
172+
reactor.saveToUserDefaults(value: timerValue) }
173+
.store(in: &cancellables)
174+
175+
saveButtonKeychain.publisher(for: .touchUpInside)
176+
.sink { [unowned self] in
177+
guard let timerValue = self.counterValueLabel.text else { return }
178+
179+
reactor.saveToKeychain(value: timerValue) }
139180
.store(in: &cancellables)
140181

141182
resetButton.publisher(for: .touchUpInside)
@@ -154,9 +195,14 @@ extension HomeViewController {
154195

155196
extension HomeViewController {
156197

157-
func handle(savedValue: String) {
158-
savedValueLabel.isHidden = savedValue.isEmpty
159-
savedValueLabel.text = [Constants.Texts.Home.savedTime, savedValue].joined()
198+
func handle(savedUserDefaultsValue: String) {
199+
savedUserDefaultsValueLabel.isHidden = savedUserDefaultsValue.isEmpty
200+
savedUserDefaultsValueLabel.text = [Constants.Texts.Home.userDefaultsTime, savedUserDefaultsValue].joined()
201+
}
202+
203+
func handle(savedKeychainValue: String) {
204+
savedKeychainValueLabel.isHidden = savedKeychainValue.isEmpty
205+
savedKeychainValueLabel.text = [Constants.Texts.Home.keychainTime, savedKeychainValue].joined()
160206
}
161207

162208
}

GoodPersistence-Sample/GoodPersistence-Sample/Screens/Home/HomeViewModel.swift

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ final class HomeViewModel {
2525
private let sourceSubject = PassthroughSubject<Date, Never>()
2626
private(set) lazy var sourcePublisher = sourceSubject.eraseToAnyPublisher()
2727

28-
private let savedValue = CurrentValueSubject<String, Never>("")
29-
private(set) lazy var savedValuePublisher = savedValue.eraseToAnyPublisher()
28+
private let savedUserDefaultsValue = CurrentValueSubject<String, Never>("")
29+
private(set) lazy var savedUserDefaultsValuePublisher = savedUserDefaultsValue.eraseToAnyPublisher()
30+
31+
private let savedKeychainValue = CurrentValueSubject<String, Never>("")
32+
private(set) lazy var savedKeychainValuePublisher = savedKeychainValue.eraseToAnyPublisher()
3033

3134
// MARK: - Initializer
3235

@@ -35,8 +38,9 @@ final class HomeViewModel {
3538
self.di = di
3639

3740
setupTimer()
38-
sinkToCachedValue()
39-
savedValue.send(di.cacheManager.savedTime)
41+
sinkToValue()
42+
savedUserDefaultsValue.send(di.cacheManager.savedTimeUserDefaults)
43+
savedKeychainValue.send(di.cacheManager.savedTimeKeychain)
4044
}
4145

4246
}
@@ -45,8 +49,12 @@ final class HomeViewModel {
4549

4650
extension HomeViewModel {
4751

48-
func saveToCache(value: String) {
49-
di.cacheManager.save(value: value)
52+
func saveToUserDefaults(value: String) {
53+
di.cacheManager.saveToUserDefaults(value: value)
54+
}
55+
56+
func saveToKeychain(value: String) {
57+
di.cacheManager.saveToKeychain(value: value)
5058
}
5159

5260
func resetCache() {
@@ -70,9 +78,19 @@ private extension HomeViewModel {
7078
.store(in: &cancellables)
7179
}
7280

73-
func sinkToCachedValue() {
74-
di.cacheManager.savedTimePublisher
75-
.sink { [weak self] value in self?.savedValue.send(value) }
81+
func sinkToValue() {
82+
di.cacheManager.savedTimeUserDefaultsPublisher
83+
.sink { [weak self] value in self?.savedUserDefaultsValue.send(value) }
84+
.store(in: &cancellables)
85+
86+
di.cacheManager.savedTimeKeychainPublisher
87+
.sink(receiveCompletion: { completion in
88+
if case let .failure(keychainError) = completion {
89+
print(keychainError)
90+
}
91+
}, receiveValue: { [weak self] value in
92+
self?.savedKeychainValue.send(value)
93+
})
7694
.store(in: &cancellables)
7795
}
7896

Package.resolved

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,17 @@ let package = Package(
1616
],
1717
dependencies: [
1818
// Dependencies declare other packages that this package depends on.
19-
.package(url: "https://github.com/CombineCommunity/CombineExt.git", from: "1.0.0")
19+
.package(url: "https://github.com/CombineCommunity/CombineExt.git", from: "1.0.0"),
20+
.package(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", from: "4.2.2")
2021
],
2122
targets: [
2223
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
2324
// Targets can depend on other targets in this package, and on products in packages this package depends on.
2425
.target(
2526
name: "GoodPersistence",
2627
dependencies: [
27-
.product(name: "CombineExt", package: "CombineExt")
28+
.product(name: "CombineExt", package: "CombineExt"),
29+
.product(name: "KeychainAccess", package: "KeychainAccess")
2830
],
2931
path: "./Sources/GoodPersistence"
3032
),

0 commit comments

Comments
 (0)