Browse Source

Authenticator 1.0

David Walter 2 years ago
parent
commit
6902217d19
Signed by: David Walter <hello@davidwalter.at> GPG key ID: 9D6D093366A35900

+ 45
- 65
Authenticator.xcodeproj/project.pbxproj View File

@@ -7,32 +7,17 @@
7 7
 	objects = {
8 8
 
9 9
 /* Begin PBXBuildFile section */
10
-		6318DCB0200D1A3900F40A63 /* LTMorphingLabel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63D0F0EB200559E7001EC8EC /* LTMorphingLabel.framework */; };
11
-		6318DCB1200D1A3900F40A63 /* LTMorphingLabel.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 63D0F0EB200559E7001EC8EC /* LTMorphingLabel.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
12
-		6318DCB6200D1A3900F40A63 /* Eureka.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6367EA971FF91E3600753CDC /* Eureka.framework */; };
13
-		6318DCB7200D1A3900F40A63 /* Eureka.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6367EA971FF91E3600753CDC /* Eureka.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
14
-		6318DCB8200D1A3900F40A63 /* OTPGenerator.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6367EA861FF915BC00753CDC /* OTPGenerator.framework */; };
15
-		6318DCB9200D1A3900F40A63 /* OTPGenerator.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6367EA861FF915BC00753CDC /* OTPGenerator.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
16
-		6318DCBA200D1A3900F40A63 /* Realm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6367EA871FF915BC00753CDC /* Realm.framework */; };
17
-		6318DCBB200D1A3900F40A63 /* Realm.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6367EA871FF915BC00753CDC /* Realm.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
18
-		6318DCBC200D1A3900F40A63 /* RealmSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6367EA8A1FF915BC00753CDC /* RealmSwift.framework */; };
19
-		6318DCBD200D1A3900F40A63 /* RealmSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6367EA8A1FF915BC00753CDC /* RealmSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
20
-		6318DCBF200D1A3E00F40A63 /* Vision.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6354F7F51FF9155200FFCE99 /* Vision.framework */; };
21 10
 		631B55DC200E64D900D6C15F /* PasscodeSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631B55DB200E64D900D6C15F /* PasscodeSettingsViewController.swift */; };
22
-		632656712021295E00695B49 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 633D866A201266DB00FC305C /* AVFoundation.framework */; };
23
-		633D866F2012B27900FC305C /* KeychainSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 633D866E2012B27300FC305C /* KeychainSwift.framework */; };
24
-		633D86702012B27900FC305C /* KeychainSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 633D866E2012B27300FC305C /* KeychainSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
25 11
 		634795D72013503C001D334F /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 634795D62013503C001D334F /* Settings.bundle */; };
26 12
 		634F54A3201D572600B85422 /* dark.json in Resources */ = {isa = PBXBuildFile; fileRef = 634F54A1201D572600B85422 /* dark.json */; };
27 13
 		634F54A4201D572600B85422 /* light.json in Resources */ = {isa = PBXBuildFile; fileRef = 634F54A2201D572600B85422 /* light.json */; };
14
+		63512EFE208FCE0B001AA6EA /* Vision.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6354F7F51FF9155200FFCE99 /* Vision.framework */; };
15
+		63512EFF208FCE10001AA6EA /* AVKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6326566F2021293400695B49 /* AVKit.framework */; };
16
+		63512F00208FCE14001AA6EA /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 633D866A201266DB00FC305C /* AVFoundation.framework */; };
28 17
 		6354F7F31FF910D600FFCE99 /* ScannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6354F7F21FF910D600FFCE99 /* ScannerViewController.swift */; };
29 18
 		6367EA961FF91C4200753CDC /* TokenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6367EA951FF91C4200753CDC /* TokenViewController.swift */; };
30 19
 		6367EAB81FF941EC00753CDC /* TokenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6367EAB71FF941EC00753CDC /* TokenViewModel.swift */; };
31 20
 		63957D0F200417A700305625 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63957D0E200417A700305625 /* SettingsViewController.swift */; };
32
-		639696B620122F2400887ACC /* Hue.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 639696B220122F0500887ACC /* Hue.framework */; };
33
-		639696B720122F2400887ACC /* Hue.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 639696B220122F0500887ACC /* Hue.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
34
-		639696B820122F2400887ACC /* UserDefaults.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 639696B320122F0500887ACC /* UserDefaults.framework */; };
35
-		639696B920122F2400887ACC /* UserDefaults.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 639696B320122F0500887ACC /* UserDefaults.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
36 21
 		639696BB201230AB00887ACC /* AppConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 639696BA201230AB00887ACC /* AppConfig.swift */; };
37 22
 		63AA62DB1FF8221F00A76E77 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63AA62DA1FF8221F00A76E77 /* AppDelegate.swift */; };
38 23
 		63AA62E01FF8221F00A76E77 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 63AA62DE1FF8221F00A76E77 /* Main.storyboard */; };
@@ -40,14 +25,8 @@
40 25
 		63AA62E51FF8221F00A76E77 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 63AA62E31FF8221F00A76E77 /* LaunchScreen.storyboard */; };
41 26
 		63AA630A1FF8275800A76E77 /* MainTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63AA63091FF8275800A76E77 /* MainTableViewController.swift */; };
42 27
 		63AA63311FF82A2E00A76E77 /* Token.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63AA63301FF82A2E00A76E77 /* Token.swift */; };
43
-		63B119832024E2F70059A369 /* Passcode.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63B119822024E2F70059A369 /* Passcode.framework */; };
44
-		63B119842024E3040059A369 /* Passcode.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63B119822024E2F70059A369 /* Passcode.framework */; };
45
-		63B119852024E3040059A369 /* Passcode.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 63B119822024E2F70059A369 /* Passcode.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
46 28
 		63C74AF8202A2BCA00713DAA /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 63C74AF5202A2ACF00713DAA /* Localizable.strings */; };
47 29
 		63C896C81FFBCD0B00081DD3 /* TokenTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63C896C71FFBCD0B00081DD3 /* TokenTableViewCell.swift */; };
48
-		63C9C69C2027883100012934 /* Themeable.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63C9C69B2027883100012934 /* Themeable.framework */; };
49
-		63C9C69D2027883600012934 /* Themeable.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 63C9C69B2027883100012934 /* Themeable.framework */; };
50
-		63C9C69E2027883600012934 /* Themeable.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 63C9C69B2027883100012934 /* Themeable.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
51 30
 		63C9C6A62027895600012934 /* ColorSwatchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63C9C6A02027895500012934 /* ColorSwatchView.swift */; };
52 31
 		63C9C6A72027895600012934 /* ColorSwatchCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63C9C6A12027895500012934 /* ColorSwatchCell.swift */; };
53 32
 		63C9C6A82027895600012934 /* ColorPalettes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63C9C6A22027895500012934 /* ColorPalettes.swift */; };
@@ -61,29 +40,6 @@
61 40
 		63EDBD30201BDB0400FE52DB /* EmptyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 63EDBD2E201BDB0400FE52DB /* EmptyView.xib */; };
62 41
 /* End PBXBuildFile section */
63 42
 
64
-/* Begin PBXCopyFilesBuildPhase section */
65
-		6318DCBE200D1A3900F40A63 /* Embed Frameworks */ = {
66
-			isa = PBXCopyFilesBuildPhase;
67
-			buildActionMask = 2147483647;
68
-			dstPath = "";
69
-			dstSubfolderSpec = 10;
70
-			files = (
71
-				63C9C69E2027883600012934 /* Themeable.framework in Embed Frameworks */,
72
-				6318DCBB200D1A3900F40A63 /* Realm.framework in Embed Frameworks */,
73
-				639696B720122F2400887ACC /* Hue.framework in Embed Frameworks */,
74
-				63B119852024E3040059A369 /* Passcode.framework in Embed Frameworks */,
75
-				6318DCB1200D1A3900F40A63 /* LTMorphingLabel.framework in Embed Frameworks */,
76
-				6318DCBD200D1A3900F40A63 /* RealmSwift.framework in Embed Frameworks */,
77
-				633D86702012B27900FC305C /* KeychainSwift.framework in Embed Frameworks */,
78
-				6318DCB7200D1A3900F40A63 /* Eureka.framework in Embed Frameworks */,
79
-				6318DCB9200D1A3900F40A63 /* OTPGenerator.framework in Embed Frameworks */,
80
-				639696B920122F2400887ACC /* UserDefaults.framework in Embed Frameworks */,
81
-			);
82
-			name = "Embed Frameworks";
83
-			runOnlyForDeploymentPostprocessing = 0;
84
-		};
85
-/* End PBXCopyFilesBuildPhase section */
86
-
87 43
 /* Begin PBXFileReference section */
88 44
 		631B55DB200E64D900D6C15F /* PasscodeSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasscodeSettingsViewController.swift; sourceTree = "<group>"; };
89 45
 		6326566F2021293400695B49 /* AVKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVKit.framework; path = System/Library/Frameworks/AVKit.framework; sourceTree = SDKROOT; };
@@ -130,6 +86,7 @@
130 86
 		63D699702031C3C100FA1F7B /* Localizable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Localizable.swift; sourceTree = "<group>"; };
131 87
 		63EDBD2D201BDB0400FE52DB /* EmptyView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyView.swift; sourceTree = "<group>"; };
132 88
 		63EDBD2E201BDB0400FE52DB /* EmptyView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EmptyView.xib; sourceTree = "<group>"; };
89
+		63F5732A20C330AA00970942 /* Authenticator.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Authenticator.entitlements; sourceTree = "<group>"; };
133 90
 /* End PBXFileReference section */
134 91
 
135 92
 /* Begin PBXFrameworksBuildPhase section */
@@ -137,20 +94,9 @@
137 94
 			isa = PBXFrameworksBuildPhase;
138 95
 			buildActionMask = 2147483647;
139 96
 			files = (
140
-				633D866F2012B27900FC305C /* KeychainSwift.framework in Frameworks */,
141
-				63B119832024E2F70059A369 /* Passcode.framework in Frameworks */,
142
-				6318DCBA200D1A3900F40A63 /* Realm.framework in Frameworks */,
143
-				639696B820122F2400887ACC /* UserDefaults.framework in Frameworks */,
144
-				63C9C69D2027883600012934 /* Themeable.framework in Frameworks */,
145
-				6318DCB0200D1A3900F40A63 /* LTMorphingLabel.framework in Frameworks */,
146
-				6318DCBC200D1A3900F40A63 /* RealmSwift.framework in Frameworks */,
147
-				639696B620122F2400887ACC /* Hue.framework in Frameworks */,
148
-				6318DCB6200D1A3900F40A63 /* Eureka.framework in Frameworks */,
149
-				63B119842024E3040059A369 /* Passcode.framework in Frameworks */,
150
-				6318DCB8200D1A3900F40A63 /* OTPGenerator.framework in Frameworks */,
151
-				632656712021295E00695B49 /* AVFoundation.framework in Frameworks */,
152
-				63C9C69C2027883100012934 /* Themeable.framework in Frameworks */,
153
-				6318DCBF200D1A3E00F40A63 /* Vision.framework in Frameworks */,
97
+				63512EFF208FCE10001AA6EA /* AVKit.framework in Frameworks */,
98
+				63512EFE208FCE0B001AA6EA /* Vision.framework in Frameworks */,
99
+				63512F00208FCE14001AA6EA /* AVFoundation.framework in Frameworks */,
154 100
 			);
155 101
 			runOnlyForDeploymentPostprocessing = 0;
156 102
 		};
@@ -206,6 +152,7 @@
206 152
 		63AA62D91FF8221F00A76E77 /* Authenticator */ = {
207 153
 			isa = PBXGroup;
208 154
 			children = (
155
+				63F5732A20C330AA00970942 /* Authenticator.entitlements */,
209 156
 				639696BA201230AB00887ACC /* AppConfig.swift */,
210 157
 				63AA62DA1FF8221F00A76E77 /* AppDelegate.swift */,
211 158
 				63D0F0E520054CCB001EC8EC /* Models */,
@@ -286,9 +233,9 @@
286 233
 			buildConfigurationList = 63AA62E91FF8221F00A76E77 /* Build configuration list for PBXNativeTarget "Authenticator" */;
287 234
 			buildPhases = (
288 235
 				63AA62D31FF8221F00A76E77 /* Sources */,
236
+				6362A54220C4175000618111 /* Carthage */,
289 237
 				63AA62D41FF8221F00A76E77 /* Frameworks */,
290 238
 				63AA62D51FF8221F00A76E77 /* Resources */,
291
-				6318DCBE200D1A3900F40A63 /* Embed Frameworks */,
292 239
 				6323C4592021BD91004BE10A /* SwiftLint */,
293 240
 			);
294 241
 			buildRules = (
@@ -307,12 +254,23 @@
307 254
 			isa = PBXProject;
308 255
 			attributes = {
309 256
 				LastSwiftUpdateCheck = 0920;
310
-				LastUpgradeCheck = 0920;
257
+				LastUpgradeCheck = 0930;
311 258
 				ORGANIZATIONNAME = "David Walter";
312 259
 				TargetAttributes = {
313 260
 					63AA62D61FF8221F00A76E77 = {
314 261
 						CreatedOnToolsVersion = 9.2;
315 262
 						ProvisioningStyle = Automatic;
263
+						SystemCapabilities = {
264
+							com.apple.ApplicationGroups.iOS = {
265
+								enabled = 1;
266
+							};
267
+							com.apple.SafariKeychain = {
268
+								enabled = 1;
269
+							};
270
+							com.apple.iCloud = {
271
+								enabled = 1;
272
+							};
273
+						};
316 274
 					};
317 275
 				};
318 276
 			};
@@ -369,6 +327,20 @@
369 327
 			shellPath = /bin/sh;
370 328
 			shellScript = "if which swiftlint >/dev/null; then\n    swiftlint\nelse\n    echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi";
371 329
 		};
330
+		6362A54220C4175000618111 /* Carthage */ = {
331
+			isa = PBXShellScriptBuildPhase;
332
+			buildActionMask = 2147483647;
333
+			files = (
334
+			);
335
+			inputPaths = (
336
+			);
337
+			name = Carthage;
338
+			outputPaths = (
339
+			);
340
+			runOnlyForDeploymentPostprocessing = 0;
341
+			shellPath = /bin/sh;
342
+			shellScript = "carthage-copy-frameworks";
343
+		};
372 344
 /* End PBXShellScriptBuildPhase section */
373 345
 
374 346
 /* Begin PBXSourcesBuildPhase section */
@@ -453,6 +425,7 @@
453 425
 				CLANG_WARN_BOOL_CONVERSION = YES;
454 426
 				CLANG_WARN_COMMA = YES;
455 427
 				CLANG_WARN_CONSTANT_CONVERSION = YES;
428
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
456 429
 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
457 430
 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
458 431
 				CLANG_WARN_EMPTY_BODY = YES;
@@ -460,6 +433,7 @@
460 433
 				CLANG_WARN_INFINITE_RECURSION = YES;
461 434
 				CLANG_WARN_INT_CONVERSION = YES;
462 435
 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
436
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
463 437
 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
464 438
 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
465 439
 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
@@ -511,6 +485,7 @@
511 485
 				CLANG_WARN_BOOL_CONVERSION = YES;
512 486
 				CLANG_WARN_COMMA = YES;
513 487
 				CLANG_WARN_CONSTANT_CONVERSION = YES;
488
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
514 489
 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
515 490
 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
516 491
 				CLANG_WARN_EMPTY_BODY = YES;
@@ -518,6 +493,7 @@
518 493
 				CLANG_WARN_INFINITE_RECURSION = YES;
519 494
 				CLANG_WARN_INT_CONVERSION = YES;
520 495
 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
496
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
521 497
 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
522 498
 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
523 499
 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
@@ -550,16 +526,18 @@
550 526
 		63AA62EA1FF8221F00A76E77 /* Debug */ = {
551 527
 			isa = XCBuildConfiguration;
552 528
 			buildSettings = {
553
-				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
554 529
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
530
+				CODE_SIGN_ENTITLEMENTS = Authenticator/Authenticator.entitlements;
555 531
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
556 532
 				CODE_SIGN_STYLE = Automatic;
557 533
 				DEVELOPMENT_TEAM = 7MLF4U25UX;
558 534
 				FRAMEWORK_SEARCH_PATHS = (
559 535
 					"$(inherited)",
536
+					"$(PROJECT_DIR)/Carthage/Build/iOS/**",
560 537
 					"$(PROJECT_DIR)/Carthage/Build/iOS",
561 538
 				);
562 539
 				INFOPLIST_FILE = Authenticator/Info.plist;
540
+				IPHONEOS_DEPLOYMENT_TARGET = 11.2;
563 541
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
564 542
 				PRODUCT_BUNDLE_IDENTIFIER = at.davidwalter.Authenticator;
565 543
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -572,16 +550,18 @@
572 550
 		63AA62EB1FF8221F00A76E77 /* Release */ = {
573 551
 			isa = XCBuildConfiguration;
574 552
 			buildSettings = {
575
-				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
576 553
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
554
+				CODE_SIGN_ENTITLEMENTS = Authenticator/Authenticator.entitlements;
577 555
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
578 556
 				CODE_SIGN_STYLE = Automatic;
579 557
 				DEVELOPMENT_TEAM = 7MLF4U25UX;
580 558
 				FRAMEWORK_SEARCH_PATHS = (
581 559
 					"$(inherited)",
560
+					"$(PROJECT_DIR)/Carthage/Build/iOS/**",
582 561
 					"$(PROJECT_DIR)/Carthage/Build/iOS",
583 562
 				);
584 563
 				INFOPLIST_FILE = Authenticator/Info.plist;
564
+				IPHONEOS_DEPLOYMENT_TARGET = 11.2;
585 565
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
586 566
 				PRODUCT_BUNDLE_IDENTIFIER = at.davidwalter.Authenticator;
587 567
 				PRODUCT_NAME = "$(TARGET_NAME)";

+ 2
- 2
Authenticator/AppConfig.swift View File

@@ -26,7 +26,7 @@ struct AppConfig {
26 26
         }, biometricsGetter: {
27 27
             return UserDefaults.standard[.biometrics]
28 28
         })
29
-        config.autoBiometrics = UserDefaults.standard[.biometrics]
29
+        config.autoBiometrics = true
30 30
         
31 31
         return Passcode(window: AppDelegate.window, config: config)
32 32
     }()
@@ -52,7 +52,7 @@ struct AppConfig {
52 52
             Theme.shared.colors = UserDefaults.standard[.dark] ? .dark : .light
53 53
         }
54 54
         
55
-        Theme.shared.eureka = true
55
+        Theme.shared.features.append(.eureka)
56 56
         
57 57
         Theme.shared.customColor = {
58 58
             let textOnMainTint = Theme.shared.colors.tint.isDark ? Theme.shared.colors.textLight : Theme.shared.colors.textDark

+ 10
- 0
Authenticator/Authenticator.entitlements View File

@@ -0,0 +1,10 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+<plist version="1.0">
4
+<dict>
5
+	<key>com.apple.developer.associated-domains</key>
6
+	<array>
7
+		<string>at.davidwalter.authenticator</string>
8
+	</array>
9
+</dict>
10
+</plist>

+ 14
- 14
Authenticator/Models/OTPAuthParser.swift View File

@@ -18,7 +18,7 @@ extension URL {
18 18
 
19 19
 class OTPAuthParser {
20 20
     
21
-    static func parse(string: String?) -> [String: Any?]? {
21
+    static func parse(string: String?) -> [TokenKeys: Any?]? {
22 22
         guard let string = string, string.hasPrefix("otpauth://"), let url = URL(string: string), url.scheme == "otpauth" else {
23 23
             return nil
24 24
         }
@@ -26,7 +26,7 @@ class OTPAuthParser {
26 26
         return OTPAuthParser.parse(url: url)
27 27
     }
28 28
     
29
-    static func parse(url: URL) -> [String: Any?]? {
29
+    static func parse(url: URL) -> [TokenKeys: Any?]? {
30 30
         let label = url.lastPathComponent.split(separator: ":")
31 31
         guard url.scheme == "otpauth",
32 32
             !label.isEmpty,
@@ -34,38 +34,38 @@ class OTPAuthParser {
34 34
             let secret = url["secret"]
35 35
             else { return nil }
36 36
         
37
-        var dict: [String: Any?] = [:]
38
-        dict["key"] = secret
39
-        dict["counterBased"] = url.host?.lowercased() == "hotp"
40
-        dict["issuer"] = String(label[0])
37
+        var dict: [TokenKeys: Any?] = [:]
38
+        dict[.key] = secret
39
+        dict[.counterBased] = url.host?.lowercased() == "hotp"
40
+        dict[.issuer] = String(label[0])
41 41
         
42 42
         if label.count == 2 {
43
-            dict["account"] = String(label[1])
43
+            dict[.account] = String(label[1])
44 44
         }
45 45
         
46 46
         if let issuer = url["issuer"] {
47
-            dict["provider"] = issuer
47
+            dict[.provider] = issuer
48 48
         }
49 49
         
50 50
         if let algorithm = url["algorithm"] {
51 51
             switch algorithm.uppercased() {
52 52
             case "SHA256":
53
-                dict["algorithm"] = OTPAlgorithm.sha256
53
+                dict[.algorithm] = OTPAlgorithm.sha256
54 54
             case "SHA512":
55
-                dict["algorithm"] = OTPAlgorithm.sha512
55
+                dict[.algorithm] = OTPAlgorithm.sha512
56 56
             case "MD5":
57
-                dict["algorithm"] = OTPAlgorithm.md5
57
+                dict[.algorithm] = OTPAlgorithm.md5
58 58
             default:
59
-                dict["algorithm"] = OTPAlgorithm.sha1
59
+                dict[.algorithm] = OTPAlgorithm.sha1
60 60
             }
61 61
         }
62 62
         
63 63
         if let digits = url["digits"] {
64
-            dict["digits"] = Double(digits)
64
+            dict[.digits] = Double(digits)
65 65
         }
66 66
         
67 67
         if let counter = url["counter"] {
68
-            dict["counter"] = Double(counter)
68
+            dict[.counter] = Double(counter)
69 69
         }
70 70
         
71 71
         return dict

+ 29
- 11
Authenticator/Models/Token.swift View File

@@ -25,7 +25,7 @@ class Token: Object {
25 25
     @objc dynamic var digits = 6
26 26
     @objc dynamic var algorithm = 0
27 27
 
28
-    convenience init(data: [String: Any?]) {
28
+    convenience init(data: [TokenKeys: Any?]) {
29 29
         self.init()
30 30
         self.update(data: data)
31 31
     }
@@ -107,35 +107,53 @@ class Token: Object {
107 107
     
108 108
     // MARK: - Update
109 109
 
110
-    public func update(data: [String: Any?]) {
110
+    public func update(data: [TokenKeys: Any?]) {
111 111
         // Essential
112
-        if let key = data["key"] as? String {
113
-            self.key = key
112
+        if let key = data[.key] as? String {
113
+			if let base32 = data[.base32] as? Bool, !base32 {
114
+				self.key = key.base32EncodedString
115
+			} else {
116
+            	self.key = key
117
+			}
114 118
         }
115
-        if let provider = data["provider"] as? String {
119
+        if let provider = data[.provider] as? String {
116 120
             self.provider = provider
117 121
         }
118 122
         
119 123
         // Advanced
120
-        if let counterBased = data["type"] as? Bool {
124
+        if let counterBased = data[.type] as? Bool {
121 125
             self.counterBased = counterBased
122 126
         }
123
-        if let counter = data["counter"] as? Double {
127
+        if let counter = data[.counter] as? Double {
124 128
             self.counter = Int(counter)
125 129
         }
126
-        if let algorithm = data["algorithm"] as? OTPAlgorithm {
130
+        if let algorithm = data[.algorithm] as? OTPAlgorithm {
127 131
             self.algorithm = algorithm.rawValue
128 132
         }
129
-        if let digits = data["digits"] as? Double {
133
+        if let digits = data[.digits] as? Double {
130 134
             self.digits = Int(digits)
131 135
         }
132 136
         
133 137
         // Custom
134
-        if let account = data["account"] as? String {
138
+        if let account = data[.account] as? String {
135 139
             self.account = account
136 140
         }
137
-        if let color = data["color"] as? UIColor {
141
+        if let color = data[.color] as? UIColor {
138 142
             self.color = color.hex()
139 143
         }
140 144
     }
141 145
 }
146
+
147
+enum TokenKeys: String {
148
+	case key
149
+	case provider
150
+	case issuer
151
+	case type
152
+	case counter
153
+	case algorithm
154
+	case counterBased
155
+	case digits
156
+	case account
157
+	case color
158
+	case base32
159
+}

+ 24
- 13
Authenticator/ViewModels/TokenViewModel.swift View File

@@ -17,9 +17,10 @@ class TokenViewModel {
17 17
         model.tokens = tokens.tokens
18 18
     }
19 19
     
20
-    let realm: Realm?
21
-    var tokens: List<Token>?
22
-    let reference = Token()
20
+    private let realm: Realm?
21
+    private var tokens: List<Token>?
22
+	private var filtered: List<Token>?
23
+    private let reference = Token()
23 24
     
24 25
     fileprivate init() {
25 26
         do {
@@ -43,15 +44,25 @@ class TokenViewModel {
43 44
         self.init()
44 45
         completion(self)
45 46
     }
46
-    
47
-    var isEmpty: Bool {
48
-        return tokens?.isEmpty ?? true
49
-    }
50
-    
51
-    var count: Int {
52
-        return tokens?.count ?? 0
53
-    }
54
-    
47
+	
48
+	var isEmpty: Bool {
49
+		return tokens?.isEmpty ?? true
50
+	}
51
+	
52
+	func count(filter: Bool) -> Int {
53
+		return tokens(filter: filter)?.count ?? 0
54
+	}
55
+	
56
+	func tokens(filter: Bool) -> List<Token>? {
57
+		return filter ? filtered : tokens
58
+	}
59
+	
60
+	func filter(_ text: String) {
61
+		filtered = tokens?.filter { token -> Bool in
62
+			return token.account.lowercased().contains(text) || token.provider.lowercased().contains(text)
63
+		}
64
+	}
65
+	
55 66
     func contains(token: Token?) -> Bool {
56 67
         guard let token = token else { return false }
57 68
         
@@ -64,7 +75,7 @@ class TokenViewModel {
64 75
         try? realm?.write { tokens?.append(token) }
65 76
     }
66 77
     
67
-    func update(token: Token?, data: [String: Any?]) {
78
+    func update(token: Token?, data: [TokenKeys: Any?]) {
68 79
         guard let token = token else { return }
69 80
         
70 81
         try? realm?.write { token.update(data: data) }

+ 4
- 9
Authenticator/Views/Base.lproj/Main.storyboard View File

@@ -1,22 +1,17 @@
1 1
 <?xml version="1.0" encoding="UTF-8"?>
2
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="xXX-nL-hYA">
2
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="xXX-nL-hYA">
3 3
     <device id="retina4_7" orientation="portrait">
4 4
         <adaptation id="fullscreen"/>
5 5
     </device>
6 6
     <dependencies>
7 7
         <deployment identifier="iOS"/>
8
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
8
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
9 9
         <capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
10 10
         <capability name="Constraints to layout margins" minToolsVersion="6.0"/>
11 11
         <capability name="Navigation items with more than one left or right bar item" minToolsVersion="7.0"/>
12 12
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
13 13
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
14 14
     </dependencies>
15
-    <customFonts key="customFonts">
16
-        <array key="Menlo.ttc">
17
-            <string>Menlo-Regular</string>
18
-        </array>
19
-    </customFonts>
20 15
     <scenes>
21 16
         <!--Settings-->
22 17
         <scene sceneID="3nB-o0-RvR">
@@ -97,7 +92,7 @@
97 92
         <!--Authenticator-->
98 93
         <scene sceneID="O4b-zH-aep">
99 94
             <objects>
100
-                <tableViewController id="OCe-kP-HpL" customClass="MainTableViewController" customModule="Authenticator" customModuleProvider="target" sceneMemberID="viewController">
95
+                <tableViewController definesPresentationContext="YES" id="OCe-kP-HpL" customClass="MainTableViewController" customModule="Authenticator" customModuleProvider="target" sceneMemberID="viewController">
101 96
                     <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" allowsSelectionDuringEditing="YES" rowHeight="64" estimatedRowHeight="64" sectionHeaderHeight="1" estimatedSectionHeaderHeight="1" sectionFooterHeight="28" id="Q9X-Gw-cs6">
102 97
                         <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
103 98
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -300,7 +295,7 @@
300 295
         <image name="settings" width="22" height="22"/>
301 296
     </resources>
302 297
     <inferredMetricsTieBreakers>
303
-        <segue reference="ZEi-RP-wwR"/>
298
+        <segue reference="rAA-P4-70b"/>
304 299
     </inferredMetricsTieBreakers>
305 300
     <color key="tintColor" name="mainColor"/>
306 301
 </document>

+ 51
- 15
Authenticator/Views/MainTableViewController.swift View File

@@ -10,13 +10,15 @@ import UIKit
10 10
 import Themeable
11 11
 import KeychainSwift
12 12
 
13
-class MainTableViewController: UITableViewController {
13
+class MainTableViewController: UITableViewController, UISearchResultsUpdating {
14 14
 
15 15
     let model = TokenViewModel.shared
16 16
     
17 17
     var timer: Timer?
18 18
     var timerView: UIView? = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 1))
19
-    
19
+	
20
+	let searchController = UISearchController(searchResultsController: nil)
21
+	
20 22
     // MARK: - View Cycle
21 23
     
22 24
     deinit {
@@ -25,12 +27,20 @@ class MainTableViewController: UITableViewController {
25 27
     
26 28
     override func viewDidLoad() {
27 29
         super.viewDidLoad()
28
-        
30
+		
29 31
         // Disables UINavigationBar shadow
30 32
         self.navigationController?.navigationBar.shadowImage = UIImage()
31
-        
32
-        self.navigationItem.leftBarButtonItem = self.editButtonItem
33
-        
33
+		
34
+		searchController.searchResultsUpdater = self
35
+		searchController.obscuresBackgroundDuringPresentation = false
36
+		searchController.hidesNavigationBarDuringPresentation = false
37
+		searchController.searchBar.placeholder = "Search".localized
38
+		searchController.searchBar.tintColor = Theme.shared.colors.tint
39
+		searchController.searchBar.searchBarStyle = .minimal
40
+		
41
+		self.navigationItem.searchController = searchController
42
+		self.navigationItem.leftBarButtonItem = self.editButtonItem
43
+		
34 44
         NotificationCenter.default.addObserver(self, selector: #selector(self.updateTokens), name: NSNotification.Name.UIApplicationWillEnterForeground, object: false)
35 45
         NotificationCenter.default.addObserver(self.tableView, selector: #selector(self.tableView.reloadData), name: AppDelegate.reloadData, object: nil)
36 46
     }
@@ -69,14 +79,14 @@ class MainTableViewController: UITableViewController {
69 79
     }
70 80
     
71 81
     override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
72
-        return model.count
82
+        return model.count(filter: isFilterActive)
73 83
     }
74 84
     
75 85
     override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
76 86
         guard let cell = tableView.dequeueReusableCell(withIdentifier: "TokenCell", for: indexPath) as? TokenTableViewCell else {
77 87
             return UITableViewCell()
78 88
         }
79
-        cell.token = model.tokens?[indexPath.row]
89
+		cell.token = model.tokens(filter: isFilterActive)?[indexPath.row]
80 90
         return cell
81 91
     }
82 92
     
@@ -113,15 +123,15 @@ class MainTableViewController: UITableViewController {
113 123
             guard let timerView = self.timerView else { return }
114 124
             
115 125
             if UserDefaults.standard[.animateTimer] {
116
-            timerView.backgroundColor = UIColor(red: 2.0 * red, green: 2.0 * (1.0 - red), blue: 0, alpha: 1.0)
126
+            	timerView.backgroundColor = UIColor(red: 2.0 * red, green: 2.0 * (1.0 - red), blue: 0, alpha: 1.0)
117 127
             } else {
118
-                timerView.backgroundColor = UIColor.green
128
+                timerView.backgroundColor = Theme.shared.colors.tint
119 129
             }
120 130
             
121 131
             timerView.frame = CGRect(x: width * ratio, y: 0, width: width, height: 1)
122 132
             timerView.setNeedsLayout()
123 133
         }, completion: { _ in
124
-            guard let _ = self.timerView else { return }
134
+            guard self.timerView != nil else { return }
125 135
             self.animateTimer()
126 136
         })
127 137
     }
@@ -139,7 +149,7 @@ class MainTableViewController: UITableViewController {
139 149
     
140 150
     override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
141 151
         if editingStyle == .delete {
142
-            guard let token = model.tokens?[indexPath.row] else {
152
+            guard let token = model.tokens(filter: isFilterActive)?[indexPath.row] else {
143 153
                 return
144 154
             }
145 155
             
@@ -164,7 +174,7 @@ class MainTableViewController: UITableViewController {
164 174
     override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
165 175
         tableView.deselectRow(at: indexPath, animated: true)
166 176
         if tableView.isEditing {
167
-            self.performSegue(withIdentifier: "editTokenSegue", sender: model.tokens?[indexPath.row])
177
+            self.performSegue(withIdentifier: "editTokenSegue", sender: model.tokens(filter: isFilterActive)?[indexPath.row])
168 178
         }
169 179
     }
170 180
     
@@ -172,13 +182,39 @@ class MainTableViewController: UITableViewController {
172 182
         guard let tokenCell = cell as? TokenTableViewCell else { return }
173 183
         tokenCell.willDisplay()
174 184
     }
185
+	
186
+	// MARK: - UISearchResultsUpdating
187
+	
188
+	var isFilterActive: Bool {
189
+		return searchController.isActive && !isFilterEmpty
190
+	}
191
+	
192
+	var isFilterEmpty: Bool {
193
+		return searchController.searchBar.text?.isEmpty ?? true
194
+	}
195
+	
196
+	func updateSearchResults(for searchController: UISearchController) {
197
+		guard let text = searchController.searchBar.text else {
198
+			return
199
+		}
200
+		
201
+		filterContent(for: text)
202
+	}
203
+	
204
+	func filterContent(for searchText: String?, scope: String = "All") {
205
+		if let text = searchText?.lowercased() {
206
+			model.filter(text)
207
+		}
208
+		
209
+		self.tableView.reloadData()
210
+	}
175 211
     
176 212
     // MARK: - Navigation
177 213
 
178 214
     override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
179 215
         if segue.identifier == "editTokenSegue" {
180
-            if let nc = segue.destination as? UINavigationController,
181
-                let destination = nc.viewControllers.first as? TokenViewController,
216
+            if let navigation = segue.destination as? UINavigationController,
217
+                let destination = navigation.viewControllers.first as? TokenViewController,
182 218
                 let token = sender as? Token {
183 219
                 destination.token = token
184 220
             }

+ 2
- 2
Authenticator/Views/ScannerViewController.swift View File

@@ -144,8 +144,8 @@ class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDel
144 144
     
145 145
     override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
146 146
         if segue.identifier == "addTokenSegue" {
147
-            if let nc = segue.destination as? UINavigationController,
148
-                let destination = nc.viewControllers.first as? TokenViewController,
147
+            if let navigation = segue.destination as? UINavigationController,
148
+                let destination = navigation.viewControllers.first as? TokenViewController,
149 149
                 let token = sender as? Token {
150 150
                 destination.token = token
151 151
             }

+ 2
- 16
Authenticator/Views/SettingsViewController.swift View File

@@ -104,9 +104,6 @@ class SettingsViewController: FormViewController, MFMailComposeViewControllerDel
104 104
                     UserDefaults.standard[.animateTimer] = row.value ?? true
105 105
                 })
106 106
             +++ Section("Support".localized)
107
-            <<< ButtonRow("support") { row in
108
-                row.title = "Authenticator Support Website".localized
109
-                }.onCellSelection { _, _ in self.openWebsite() }
110 107
             <<< ButtonRow("email") { row in
111 108
                 row.title = "Email Developer".localized
112 109
                 }.onCellSelection { _, _ in self.composeMail() }
@@ -141,25 +138,14 @@ class SettingsViewController: FormViewController, MFMailComposeViewControllerDel
141 138
                 Theme.shared.apply(force: true)
142 139
             })
143 140
         })
144
-        
141
+		
145 142
         alert.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel) { _ in
146 143
             
147 144
         })
148 145
         
149 146
         self.present(alert, animated: true, completion: nil)
150 147
     }
151
-    
152
-    func openWebsite() {
153
-        guard let url = URL(string: "https://github.com/divadretlaw/Authenticator") else {
154
-            return
155
-        }
156
-        let configuration = SFSafariViewController.Configuration()
157
-        let safari = SFSafariViewController(url: url, configuration: configuration)
158
-        safari.preferredControlTintColor = Theme.shared.colors.tint
159
-        safari.preferredBarTintColor = Theme.shared.colors.backgroundForm
160
-        self.present(safari, animated: true, completion: nil)
161
-    }
162
-    
148
+        
163 149
     func composeMail() {
164 150
         guard !MFMailComposeViewController.canSendMail() else {
165 151
             guard let url = URL(string: "mailto:support@davidwalter.at") else { return }

+ 152
- 132
Authenticator/Views/TokenViewController.swift View File

@@ -11,136 +11,156 @@ import Eureka
11 11
 import OTPGenerator
12 12
 
13 13
 class TokenViewController: FormViewController {
14
-    
15
-    var token: Token?
16
-    
17
-    override func viewDidLoad() {
18
-        super.viewDidLoad()
19
-        
20
-        if TokenViewModel.shared.contains(token: self.token) {
21
-            self.title = "Edit Token".localized
22
-        }
23
-        
24
-        self.form
25
-            +++ Section("Display Info".localized)
26
-                <<< TextRow("provider") { row in
27
-                    row.placeholder = "Issuer".localized
28
-                    row.value = self.token?.provider
29
-                }
30
-                <<< TextRow("account") { row in
31
-                    row.placeholder = "Account name".localized
32
-                    row.value = self.token?.account
33
-                }
34
-                <<< ColorPickerRow("color") { row in
35
-                    row.title = "Accent color".localized
36
-                    row.isCircular = true
37
-                    row.showsPaletteNames = false
38
-                    row.showsCurrentSwatch = true
39
-                    if let color = self.token?.color {
40
-                        row.value = UIColor(hex: color)
41
-                    } else {
42
-                        row.value = UIColor(hex: "#2CB6B1")
43
-                    }
44
-                    }.cellSetup { cell, _ in cell.palettes = [Merged().palette] }
45
-            +++ Section("Key Info".localized)
46
-                <<< PasswordRow("key") { row in
47
-                    row.placeholder = "Secret Key".localized
48
-                    row.value = self.token?.key
49
-                    
50
-                    self.navigationItem.rightBarButtonItem?.isEnabled = row.value != nil
51
-                    }.onChange({ row in
52
-                        self.navigationItem.rightBarButtonItem?.isEnabled = row.value != nil
53
-                    })
54
-                <<< SwitchRow("advanced") { row in
55
-                    row.title = "Advanced".localized
56
-                    row.value = false
57
-                }
58
-            +++ Section("Advanced".localized) { section in
59
-                section.hidden = Condition.function(["advanced"], { form in
60
-                    return !((form.rowBy(tag: "advanced") as? SwitchRow)?.value ?? false)
61
-                })
62
-            }
63
-                /*<<< SegmentedRow<Bool>("type") { row in
64
-                    row.value = self.token?.counterBased ?? false
65
-                    row.options = [false, true]
66
-                    row.displayValueFor = { bool in
67
-                        if let bool = bool, bool {
68
-                            return "Counter-based (HTOP)"
69
-                        }
70
-                        return "Time-based (TOTP)"
71
-                    }
72
-                }
73
-                <<< StepperRow("counter") { row in
74
-                    row.hidden = Condition.function(["type"], { form in
75
-                        return !((form.rowBy(tag: "type") as? SegmentedRow<Bool>)?.value ?? false)
76
-                    })
77
-                    row.title = "Counter value"
78
-                    if let value = self.token?.counter {
79
-                        row.value = Double(value)
80
-                    } else {
81
-                        row.value = 0
82
-                    }
83
-                    row.displayValueFor = { double in
84
-                        guard let double = double else {
85
-                            return ""
86
-                        }
87
-                        return "\(Int(double))"
88
-                    }
89
-                }*/
90
-                <<< PickerInlineRow<OTPAlgorithm>("algorithm") { row in
91
-                    row.title = "Algorithm".localized
92
-                    row.options = [.sha1, .sha256, .sha512, .md5]
93
-                    if let value = self.token?.counter {
94
-                        row.value = OTPAlgorithm(rawValue: value)
95
-                    } else {
96
-                        row.value = .sha1
97
-                    }
98
-                    row.displayValueFor = { value in
99
-                        guard let value = value else {
100
-                            return ""
101
-                        }
102
-                        
103
-                        switch value {
104
-                        case .sha256:
105
-                            return "SHA256"
106
-                        case .sha512:
107
-                            return "SHA512"
108
-                        case .md5:
109
-                            return "MD5"
110
-                        default:
111
-                            return "SHA1"
112
-                        }
113
-                    }
114
-                }
115
-                <<< StepperRow("digits") { row in
116
-                    row.title = "Number of digits".localized
117
-                    if let value = self.token?.digits {
118
-                        row.value = Double(value)
119
-                    } else {
120
-                        row.value = 6
121
-                    }
122
-                    row.displayValueFor = { double in
123
-                        guard let double = double else {
124
-                            return ""
125
-                        }
126
-                        return "\(Int(double))"
127
-                    }
128
-                    }.cellSetup({ cell, _ in
129
-                        cell.stepper.minimumValue = 1
130
-                        cell.stepper.maximumValue = 8
131
-                    })
132
-    }
133
-    
134
-    // MARK: - Navigation
135
-    
136
-    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
137
-        if segue.identifier == "saveSegue" {
138
-            if TokenViewModel.shared.contains(token: self.token) {
139
-                TokenViewModel.shared.update(token: self.token, data: self.form.values())
140
-            } else {
141
-                TokenViewModel.shared.add(token: Token(data: self.form.values()))
142
-            }
143
-        }
144
-    }
145
-    
14
+	
15
+	var token: Token?
16
+	
17
+	override func viewDidLoad() {
18
+		super.viewDidLoad()
19
+		
20
+		if TokenViewModel.shared.contains(token: self.token) {
21
+			self.title = "Edit Token".localized
22
+		}
23
+		
24
+		self.form
25
+			+++ Section("Display Info".localized)
26
+			<<< TextRow(TokenKeys.provider.rawValue) { row in
27
+				row.placeholder = "Issuer".localized
28
+				row.value = self.token?.provider
29
+			}
30
+			<<< TextRow(TokenKeys.account.rawValue) { row in
31
+				row.placeholder = "Account name".localized
32
+				row.value = self.token?.account
33
+			}
34
+			<<< ColorPickerRow(TokenKeys.color.rawValue) { row in
35
+				row.title = "Accent color".localized
36
+				row.isCircular = true
37
+				row.showsPaletteNames = false
38
+				row.showsCurrentSwatch = true
39
+				if let color = self.token?.color {
40
+					row.value = UIColor(hex: color)
41
+				} else {
42
+					row.value = UIColor(hex: "#2CB6B1")
43
+				}
44
+				}.cellSetup { cell, _ in cell.palettes = [Merged().palette] }
45
+			+++ Section("Key Info".localized)
46
+			<<< PasswordRow(TokenKeys.key.rawValue) { row in
47
+				row.placeholder = "Secret Key".localized
48
+				row.value = self.token?.key
49
+				
50
+				self.navigationItem.rightBarButtonItem?.isEnabled = row.value != nil
51
+				}.onChange({ row in
52
+					self.navigationItem.rightBarButtonItem?.isEnabled = row.value != nil
53
+				})
54
+			<<< SwitchRow("advanced") { row in
55
+				row.title = "Advanced".localized
56
+				row.value = false
57
+			}
58
+			+++ Section(header: "Advanced".localized, footer: "advanced_info".localized) { section in
59
+				section.hidden = Condition.function(["advanced"], { form in
60
+					return !((form.rowBy(tag: "advanced") as? SwitchRow)?.value ?? false)
61
+				})
62
+			}
63
+			/*<<< SegmentedRow<Bool>("type") { row in
64
+			row.value = self.token?.counterBased ?? false
65
+			row.options = [false, true]
66
+			row.displayValueFor = { bool in
67
+			if let bool = bool, bool {
68
+			return "Counter-based (HTOP)"
69
+			}
70
+			return "Time-based (TOTP)"
71
+			}
72
+			}
73
+			<<< StepperRow("counter") { row in
74
+			row.hidden = Condition.function(["type"], { form in
75
+			return !((form.rowBy(tag: "type") as? SegmentedRow<Bool>)?.value ?? false)
76
+			})
77
+			row.title = "Counter value"
78
+			if let value = self.token?.counter {
79
+			row.value = Double(value)
80
+			} else {
81
+			row.value = 0
82
+			}
83
+			row.displayValueFor = { double in
84
+			guard let double = double else {
85
+			return ""
86
+			}
87
+			return "\(Int(double))"
88
+			}
89
+			}*/
90
+			<<< PickerInlineRow<OTPAlgorithm>(TokenKeys.algorithm.rawValue) { row in
91
+				row.title = "Algorithm".localized
92
+				row.options = [.sha1, .sha256, .sha512, .md5]
93
+				if let value = self.token?.counter {
94
+					row.value = OTPAlgorithm(rawValue: value)
95
+				} else {
96
+					row.value = .sha1
97
+				}
98
+				row.displayValueFor = { value in
99
+					guard let value = value else {
100
+						return ""
101
+					}
102
+					
103
+					switch value {
104
+					case .sha256:
105
+						return "SHA256"
106
+					case .sha512:
107
+						return "SHA512"
108
+					case .md5:
109
+						return "MD5"
110
+					default:
111
+						return "SHA1"
112
+					}
113
+				}
114
+			}
115
+			<<< StepperRow(TokenKeys.digits.rawValue) { row in
116
+				row.title = "Number of digits".localized
117
+				if let value = self.token?.digits {
118
+					row.value = Double(value)
119
+				} else {
120
+					row.value = 6
121
+				}
122
+				row.displayValueFor = { double in
123
+					guard let double = double else {
124
+						return ""
125
+					}
126
+					return "\(Int(double))"
127
+				}
128
+				}.cellSetup({ cell, _ in
129
+					cell.stepper.minimumValue = 1
130
+					cell.stepper.maximumValue = 8
131
+				})
132
+			<<< SwitchRow(TokenKeys.base32.rawValue) { row in
133
+				row.title = "key_base32".localized
134
+				row.value = true
135
+		}
136
+		
137
+	}
138
+	
139
+	// MARK: - Navigation
140
+	
141
+	override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
142
+		if segue.identifier == "saveSegue" {
143
+			if TokenViewModel.shared.contains(token: self.token) {
144
+				TokenViewModel.shared.update(token: self.token, data: self.form.enumKeys())
145
+			} else {
146
+				TokenViewModel.shared.add(token: Token(data: self.form.enumKeys()))
147
+			}
148
+		}
149
+	}
150
+	
151
+}
152
+
153
+fileprivate extension Form {
154
+
155
+	func enumKeys<T: RawRepresentable>() -> [T: Any] where T.RawValue == String {
156
+		var enumKeys = [T: Any]()
157
+	
158
+		for (key, value) in self.values() {
159
+			if let key = T(rawValue: key) {
160
+				enumKeys[key] = value
161
+			}
162
+		}
163
+		return enumKeys
164
+	}
165
+	
146 166
 }

+ 4
- 1
Authenticator/de.lproj/Localizable.strings View File

@@ -17,6 +17,7 @@
17 17
 "Security" = "Sicherheit";
18 18
 "Options" = "Optionen";
19 19
 "Support" = "Support";
20
+"Search" = "Suche";
20 21
 
21 22
 "Copied code" = "Code kopiert";
22 23
 "delete_account_title" = "2-Schritte Authentifizierung zuerst deaktivieren";
@@ -44,6 +45,8 @@
44 45
 "Advanced" = "Erweitert";
45 46
 "Algorithm" = "Algorithmus";
46 47
 "Number of digits" = "Anzahl der Stellen";
48
+"key_base32" = "Geheimer Schlüssel ist in Base32";
49
+"advanced_info" = "Ändern Sie diese Einstellungen nicht, außer der 2FA-Anbieter stellt entsprechende Anweisungen zur Verfügung";
47 50
 
48 51
 // Settings
49 52
 
@@ -56,4 +59,4 @@
56 59
 
57 60
 "reset" = "Zurücksetzen";
58 61
 "reset_all_data" = "Alle Daten zurücksetzen";
59
-"reset_all_data_message" = "All Account data on this device will be permanently deleted, and all settings will be reset to default, you won't be able to undo this action.";
62
+"reset_all_data_message" = "Alle Authenticator-Daten auf diesem Gerät werden dadurch gelöscht, und alle Einstellungen werden auf die Standardeinstellungen zurückgesetzt. Ddiese Aktion kann nicht rückgängig gemachet werden.";

+ 1
- 1
Authenticator/de.lproj/Passcode.strings View File

@@ -6,7 +6,7 @@
6 6
   Copyright © 2018 David Walter. All rights reserved.
7 7
 */
8 8
 
9
-"biometricsReasonTouchID" = "Benutze einen Finger um dich zu authentifizieren";
9
+"biometricsReasonTouchID" = "Benutze deinen Finger um dich zu authentifizieren";
10 10
 "biometricsReasonFaceID" = "Benutze dein Gesicht um dich zu authentifizieren";
11 11
 "biometricsFallback" = "Passcode eingeben";
12 12
 

+ 4
- 2
Authenticator/en.lproj/Localizable.strings View File

@@ -17,6 +17,7 @@
17 17
 "Security" = "Security";
18 18
 "Options" = "Options";
19 19
 "Support" = "Support";
20
+"Search" = "Search";
20 21
 
21 22
 "Copied code" = "Copied code";
22 23
 "delete_account_title" = "Disable 2-Step Authentication First";
@@ -44,10 +45,11 @@
44 45
 "Advanced" = "Advanced";
45 46
 "Algorithm" = "Algorithm";
46 47
 "Number of digits" = "Number of digits";
48
+"key_base32" = "Secret Key is in Base32";
49
+"advanced_info" = "Do not change these settings unless instructions to do soe are provided by the 2FA provider";
47 50
 
48 51
 // Settings
49 52
 
50
-
51 53
 "Change Passcode" = "Change Passcode";
52 54
 
53 55
 "theme_color" = "Theme color";
@@ -57,5 +59,5 @@
57 59
 
58 60
 "reset" = "Reset";
59 61
 "reset_all_data" = "Reset all Data";
60
-"reset_all_data_message" = "All Account data on this device will be permanently deleted, and all settings will be reset to default, you won't be able to undo this action.";
62
+"reset_all_data_message" = "All Authenticator data on this device will be permanently deleted, and all settings will be reset to default, you won't be able to undo this action.";
61 63
 

+ 5
- 6
Cartfile.resolved View File

@@ -1,9 +1,8 @@
1
-github "divadretlaw/Passcode" "1.0"
2
-github "divadretlaw/Themeable" "dec7ba3d946fb2b37a668901bd94a16ab776a37d"
3
-github "divadretlaw/UserDefaults" "1.2"
4
-github "divadretlaw/ios-otp-generators" "f7374c6a533da7a120868e4a04478809188d7d1c"
1
+github "divadretlaw/Passcode" "1.0.1"
2
+github "divadretlaw/UserDefaults" "1.2.1"
3
+github "divadretlaw/ios-otp-generators" "09e9009a1e56f63be96aecea0f2577aaed659080"
5 4
 github "evgenyneu/keychain-swift" "10.0.0"
6 5
 github "hyperoslo/Hue" "3.0.1"
7 6
 github "lexrus/LTMorphingLabel" "0.5.6"
8
-github "realm/realm-cocoa" "v3.1.1"
9
-github "xmartlabs/Eureka" "4.0.1"
7
+github "realm/realm-cocoa" "v3.6.0"
8
+github "xmartlabs/Eureka" "4.1.1"