Merge pull request #73 from James-Sangalli/style-passcode-screen

Style passcode screen
pull/78/head
James Sangalli 7 years ago committed by GitHub
commit f1c005928c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 80
      Trust.xcodeproj/project.pbxproj
  2. 91
      Trust/AlphaWalletLock/ViewControllers/LockEnterPasscodeViewController.swift
  3. 1
      Trust/Lock/Coordinators/LockCreatePasscodeCoordinator.swift
  4. 63
      Trust/Lock/ViewControllers/LockCreatePasscodeViewController.swift
  5. 167
      Trust/Lock/ViewControllers/LockEnterPasscodeViewController.swift
  6. 251
      Trust/Lock/ViewControllers/LockPasscodeViewController.swift
  7. 126
      Trust/Lock/Views/LockView.swift
  8. 111
      Trust/Lock/Views/PasscodeCharacterView.swift
  9. 40
      Trust/Protection/Coordinators/LockEnterPasscodeCoordinator.swift
  10. 81
      Trust/Protection/Coordinators/ProtectionCoordinator.swift

@ -286,7 +286,10 @@
5E7C701BFF4469B35A074EB9 /* RequestViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C767497AD8DEE83F384D7 /* RequestViewModel.swift */; };
5E7C70BE9AE35408038E1971 /* HelpContentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7B089FD4C96810DD10FD /* HelpContentsViewController.swift */; };
5E7C70FF17622C0FFD45A542 /* AlphaWalletSettingPushRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7D2AAB777BF35B8B56BD /* AlphaWalletSettingPushRow.swift */; };
5E7C710331196CD591B51785 /* LockCreatePasscodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C741196D9D9C9C3EE5E30 /* LockCreatePasscodeViewController.swift */; };
5E7C71B52A77008694BFA5D1 /* TokensDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C74B9EB81C51E956566E7 /* TokensDataStore.swift */; };
5E7C71C3356BF80240720EFE /* LockEnterPasscodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C73495E0C0A207152EC25 /* LockEnterPasscodeViewController.swift */; };
5E7C71F8050CCF990539B293 /* LockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C79D674D45A07E694CE31 /* LockView.swift */; };
5E7C7248A9A732452BDC27D7 /* AdvancedSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C78581AA28CA5C3CBC468 /* AdvancedSettingsViewController.swift */; };
5E7C72670E16AFB8DAF64673 /* OnboardingPageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7E24936CC2190D2A16C2 /* OnboardingPageViewModel.swift */; };
5E7C72B0A10A92E591696E48 /* ContactUsBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7AE6FAE0DF969B4F52E9 /* ContactUsBannerView.swift */; };
@ -296,8 +299,13 @@
5E7C73FC3990D110C474C3D6 /* WalletFilterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C75CC640BAFFE0E789F44 /* WalletFilterViewModel.swift */; };
5E7C73FD5BD75D90C8D0EF3C /* WalletFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7C58586099F082973073 /* WalletFilterView.swift */; };
5E7C745DACB5FCCEBCEB49CA /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C793E23E2364B73C4D813 /* WelcomeViewController.swift */; };
5E7C7499A8D6814F7950DA70 /* LockCreatePasscodeCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7AB3440C01136DF4F3E9 /* LockCreatePasscodeCoordinator.swift */; };
5E7C74B99922D0CAB635970E /* PasscodeCharacterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7B9220E616F82EDA956F /* PasscodeCharacterView.swift */; };
5E7C75C99B9F595F26EDC405 /* LockPasscodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7D5F3CAE69CF932AB236 /* LockPasscodeViewController.swift */; };
5E7C75F80A7E178B49830BCD /* TicketsViewControllerHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C796039C0F47CDCA236C0 /* TicketsViewControllerHeader.swift */; };
5E7C760C7D55C97424F55138 /* TicketTableViewCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C75F877B2F2E24C7EF258 /* TicketTableViewCellViewModel.swift */; };
5E7C76A0365D128B7F19A0C2 /* ProtectionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C74BEC095303B66FB4B1E /* ProtectionCoordinator.swift */; };
5E7C76B917517C93D1E26B0A /* LockEnterPasscodeCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7981AB6584B25C72D46B /* LockEnterPasscodeCoordinator.swift */; };
5E7C76F8CB67466725C590CE /* TokenViewCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C79ED9F842D3FC102AC54 /* TokenViewCellViewModel.swift */; };
5E7C7793AB6B577906F2BCA3 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C7AFE9AF9FE6B58C925D4 /* SettingsViewController.swift */; };
5E7C77E844D710D7AFBC58D4 /* RequestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7C74DCC21272EC231A20E2 /* RequestViewController.swift */; };
@ -345,17 +353,9 @@
737EEDDA201BE3A8009D9D5D /* Lock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 737EEDD9201BE3A8009D9D5D /* Lock.swift */; };
739533971FEFF5FD0084AFAB /* Currency.swift in Sources */ = {isa = PBXBuildFile; fileRef = 739533961FEFF5FD0084AFAB /* Currency.swift */; };
73958DC720263525000A40EB /* SplashCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73958DC620263525000A40EB /* SplashCoordinatorTests.swift */; };
73ACEEF5201631A3003DD71D /* ProtectionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73ACEEF4201631A2003DD71D /* ProtectionCoordinator.swift */; };
73ACEEFB20163C94003DD71D /* LockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73ACEEFA20163C94003DD71D /* LockView.swift */; };
73ACEEFF20163E08003DD71D /* PasscodeCharacterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73ACEEFE20163E08003DD71D /* PasscodeCharacterView.swift */; };
73ACEF0120163ED4003DD71D /* LockViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73ACEF0020163ED4003DD71D /* LockViewModel.swift */; };
73ACEF0520163F46003DD71D /* LockEnterPasscodeCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73ACEF0420163F46003DD71D /* LockEnterPasscodeCoordinator.swift */; };
73BAA8D1201B2FBC00D6DD75 /* LockCreatePasscodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73BAA8D0201B2FBC00D6DD75 /* LockCreatePasscodeViewController.swift */; };
73BAA8D3201B2FF800D6DD75 /* LockEnterPasscodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73BAA8D2201B2FF800D6DD75 /* LockEnterPasscodeViewController.swift */; };
73C41C71201B46AD00243C6C /* LockEnterPasscodeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73C41C70201B46AD00243C6C /* LockEnterPasscodeViewModel.swift */; };
73C41C73201B5EFF00243C6C /* LockCreatePasscodeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73C41C72201B5EFF00243C6C /* LockCreatePasscodeViewModel.swift */; };
73C41C75201B65AD00243C6C /* LockCreatePasscodeCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73C41C74201B65AD00243C6C /* LockCreatePasscodeCoordinator.swift */; };
73C5CF45201A7CAB00C74316 /* LockPasscodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73C5CF44201A7CAB00C74316 /* LockPasscodeViewController.swift */; };
73CBC75F2020CBF800374666 /* AccountViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 73CBC75E2020CBF800374666 /* AccountViewCell.xib */; };
73CBC761202139FB00374666 /* FakeGetBalanceCoordinator(.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73CBC760202139FB00374666 /* FakeGetBalanceCoordinator(.swift */; };
73D26837202E827E009777A1 /* DecimalFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73D26836202E827E009777A1 /* DecimalFormatter.swift */; };
@ -761,8 +761,11 @@
5E7C71EBD4C95AD4E11F3352 /* AlphaWalletSettingsButtonRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AlphaWalletSettingsButtonRow.swift; path = Views/AlphaWalletSettingsButtonRow.swift; sourceTree = "<group>"; };
5E7C72142D5817EF8FA8CADA /* PrivacyPolicyViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrivacyPolicyViewController.swift; sourceTree = "<group>"; };
5E7C731B6F01534683227123 /* TicketTokenViewCellViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TicketTokenViewCellViewModel.swift; sourceTree = "<group>"; };
5E7C73495E0C0A207152EC25 /* LockEnterPasscodeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LockEnterPasscodeViewController.swift; path = Trust/AlphaWalletLock/ViewControllers/LockEnterPasscodeViewController.swift; sourceTree = SOURCE_ROOT; };
5E7C741196D9D9C9C3EE5E30 /* LockCreatePasscodeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockCreatePasscodeViewController.swift; sourceTree = "<group>"; };
5E7C74A2C738BF2412D412A7 /* TicketSellInfoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TicketSellInfoViewController.swift; sourceTree = "<group>"; };
5E7C74B9EB81C51E956566E7 /* TokensDataStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokensDataStore.swift; sourceTree = "<group>"; };
5E7C74BEC095303B66FB4B1E /* ProtectionCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProtectionCoordinator.swift; sourceTree = "<group>"; };
5E7C74DCC21272EC231A20E2 /* RequestViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestViewController.swift; sourceTree = "<group>"; };
5E7C74FABE14B7B1BEEC4F5E /* WhyUseEthereumInfoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WhyUseEthereumInfoViewController.swift; sourceTree = "<group>"; };
5E7C7534FB6BF4D199643246 /* AlphaWalletSettingsSwitchRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AlphaWalletSettingsSwitchRow.swift; path = Views/AlphaWalletSettingsSwitchRow.swift; sourceTree = "<group>"; };
@ -782,7 +785,10 @@
5E7C78B001F9F95F404D5FEF /* HowDoIGetMyMoneyInfoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HowDoIGetMyMoneyInfoViewController.swift; sourceTree = "<group>"; };
5E7C793E23E2364B73C4D813 /* WelcomeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WelcomeViewController.swift; sourceTree = "<group>"; };
5E7C796039C0F47CDCA236C0 /* TicketsViewControllerHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TicketsViewControllerHeader.swift; sourceTree = "<group>"; };
5E7C7981AB6584B25C72D46B /* LockEnterPasscodeCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockEnterPasscodeCoordinator.swift; sourceTree = "<group>"; };
5E7C79D674D45A07E694CE31 /* LockView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockView.swift; sourceTree = "<group>"; };
5E7C79ED9F842D3FC102AC54 /* TokenViewCellViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenViewCellViewModel.swift; sourceTree = "<group>"; };
5E7C7AB3440C01136DF4F3E9 /* LockCreatePasscodeCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockCreatePasscodeCoordinator.swift; sourceTree = "<group>"; };
5E7C7AE6FAE0DF969B4F52E9 /* ContactUsBannerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactUsBannerView.swift; sourceTree = "<group>"; };
5E7C7AF9A592D7224ED58016 /* OnboardingPageStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingPageStyle.swift; sourceTree = "<group>"; };
5E7C7AFE9AF9FE6B58C925D4 /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
@ -790,10 +796,12 @@
5E7C7B0BE9EE3B198AE7D92D /* WhatIsASeedPhraseInfoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WhatIsASeedPhraseInfoViewController.swift; sourceTree = "<group>"; };
5E7C7B1FB2702A2A8A4EBD76 /* SettingsCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsCoordinator.swift; sourceTree = "<group>"; };
5E7C7B3302309706CA0F972A /* TokensViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokensViewController.swift; sourceTree = "<group>"; };
5E7C7B9220E616F82EDA956F /* PasscodeCharacterView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasscodeCharacterView.swift; sourceTree = "<group>"; };
5E7C7C077372C3F2A4349FA1 /* TokenViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenViewCell.swift; sourceTree = "<group>"; };
5E7C7C58586099F082973073 /* WalletFilterView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletFilterView.swift; sourceTree = "<group>"; };
5E7C7D2AAB777BF35B8B56BD /* AlphaWalletSettingPushRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AlphaWalletSettingPushRow.swift; path = Views/AlphaWalletSettingPushRow.swift; sourceTree = "<group>"; };
5E7C7D4F7C566EDD30EF1C19 /* HowDoITransferETHIntoMyWalletInfoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HowDoITransferETHIntoMyWalletInfoViewController.swift; sourceTree = "<group>"; };
5E7C7D5F3CAE69CF932AB236 /* LockPasscodeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LockPasscodeViewController.swift; sourceTree = "<group>"; };
5E7C7E24936CC2190D2A16C2 /* OnboardingPageViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingPageViewModel.swift; sourceTree = "<group>"; };
5E7C7E2DCCE0D775ECF83088 /* WalletFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WalletFilter.swift; path = Models/WalletFilter.swift; sourceTree = "<group>"; };
5E7C7EE467A7F5F2E5B1F660 /* TokensViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokensViewModel.swift; sourceTree = "<group>"; };
@ -817,17 +825,9 @@
737EEDD9201BE3A8009D9D5D /* Lock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Lock.swift; sourceTree = "<group>"; };
739533961FEFF5FD0084AFAB /* Currency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Currency.swift; sourceTree = "<group>"; };
73958DC620263525000A40EB /* SplashCoordinatorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashCoordinatorTests.swift; sourceTree = "<group>"; };
73ACEEF4201631A2003DD71D /* ProtectionCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtectionCoordinator.swift; sourceTree = "<group>"; };
73ACEEFA20163C94003DD71D /* LockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockView.swift; sourceTree = "<group>"; };
73ACEEFE20163E08003DD71D /* PasscodeCharacterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasscodeCharacterView.swift; sourceTree = "<group>"; };
73ACEF0020163ED4003DD71D /* LockViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockViewModel.swift; sourceTree = "<group>"; };
73ACEF0420163F46003DD71D /* LockEnterPasscodeCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockEnterPasscodeCoordinator.swift; sourceTree = "<group>"; };
73BAA8D0201B2FBC00D6DD75 /* LockCreatePasscodeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockCreatePasscodeViewController.swift; sourceTree = "<group>"; };
73BAA8D2201B2FF800D6DD75 /* LockEnterPasscodeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockEnterPasscodeViewController.swift; sourceTree = "<group>"; };
73C41C70201B46AD00243C6C /* LockEnterPasscodeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockEnterPasscodeViewModel.swift; sourceTree = "<group>"; };
73C41C72201B5EFF00243C6C /* LockCreatePasscodeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockCreatePasscodeViewModel.swift; sourceTree = "<group>"; };
73C41C74201B65AD00243C6C /* LockCreatePasscodeCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockCreatePasscodeCoordinator.swift; sourceTree = "<group>"; };
73C5CF44201A7CAB00C74316 /* LockPasscodeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockPasscodeViewController.swift; sourceTree = "<group>"; };
73CBC75E2020CBF800374666 /* AccountViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountViewCell.xib; sourceTree = "<group>"; };
73CBC760202139FB00374666 /* FakeGetBalanceCoordinator(.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FakeGetBalanceCoordinator(.swift"; sourceTree = "<group>"; };
73D26836202E827E009777A1 /* DecimalFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecimalFormatter.swift; sourceTree = "<group>"; };
@ -1059,6 +1059,7 @@
76F1D1936604D6A022E9AE90 /* Market */,
76F1D47A29573DA8BD3E4979 /* Redeem */,
5E7C7ACB32FB112CD7D92977 /* AlphaWalletHelp */,
5E7C788AF1C199AC61863B85 /* AlphaWalletProtection */,
);
path = Trust;
sourceTree = "<group>";
@ -2193,6 +2194,14 @@
path = Views;
sourceTree = "<group>";
};
5E7C788AF1C199AC61863B85 /* AlphaWalletProtection */ = {
isa = PBXGroup;
children = (
5E7C7BC8799AC9C84FB6269F /* Coordinators */,
);
path = AlphaWalletProtection;
sourceTree = "<group>";
};
5E7C7ACB32FB112CD7D92977 /* AlphaWalletHelp */ = {
isa = PBXGroup;
children = (
@ -2202,6 +2211,13 @@
path = AlphaWalletHelp;
sourceTree = "<group>";
};
5E7C7BC8799AC9C84FB6269F /* Coordinators */ = {
isa = PBXGroup;
children = (
);
path = Coordinators;
sourceTree = "<group>";
};
615F10571FCBEF6A008A45AF /* Views */ = {
isa = PBXGroup;
children = (
@ -2223,8 +2239,9 @@
732086B3201507220047F605 /* Coordinators */ = {
isa = PBXGroup;
children = (
73ACEEF4201631A2003DD71D /* ProtectionCoordinator.swift */,
732086B8201508690047F605 /* SplashCoordinator.swift */,
5E7C7981AB6584B25C72D46B /* LockEnterPasscodeCoordinator.swift */,
5E7C74BEC095303B66FB4B1E /* ProtectionCoordinator.swift */,
);
path = Coordinators;
sourceTree = "<group>";
@ -2252,8 +2269,7 @@
73ACEEF720163B4E003DD71D /* Coordinators */ = {
isa = PBXGroup;
children = (
73ACEF0420163F46003DD71D /* LockEnterPasscodeCoordinator.swift */,
73C41C74201B65AD00243C6C /* LockCreatePasscodeCoordinator.swift */,
5E7C7AB3440C01136DF4F3E9 /* LockCreatePasscodeCoordinator.swift */,
);
path = Coordinators;
sourceTree = "<group>";
@ -2261,9 +2277,9 @@
73ACEEF920163B4E003DD71D /* ViewControllers */ = {
isa = PBXGroup;
children = (
73C5CF44201A7CAB00C74316 /* LockPasscodeViewController.swift */,
73BAA8D0201B2FBC00D6DD75 /* LockCreatePasscodeViewController.swift */,
73BAA8D2201B2FF800D6DD75 /* LockEnterPasscodeViewController.swift */,
5E7C7D5F3CAE69CF932AB236 /* LockPasscodeViewController.swift */,
5E7C741196D9D9C9C3EE5E30 /* LockCreatePasscodeViewController.swift */,
5E7C73495E0C0A207152EC25 /* LockEnterPasscodeViewController.swift */,
);
path = ViewControllers;
sourceTree = "<group>";
@ -2281,8 +2297,8 @@
73ACEEFD20163DA3003DD71D /* Views */ = {
isa = PBXGroup;
children = (
73ACEEFA20163C94003DD71D /* LockView.swift */,
73ACEEFE20163E08003DD71D /* PasscodeCharacterView.swift */,
5E7C79D674D45A07E694CE31 /* LockView.swift */,
5E7C7B9220E616F82EDA956F /* PasscodeCharacterView.swift */,
);
path = Views;
sourceTree = "<group>";
@ -3041,7 +3057,6 @@
296106D01F778A8D0006164B /* TransferType.swift in Sources */,
29E14FDB1F7F4F3D00185568 /* Transaction.swift in Sources */,
BBF4F9B72029D0B3009E04C0 /* GasViewModel.swift in Sources */,
73C41C75201B65AD00243C6C /* LockCreatePasscodeCoordinator.swift in Sources */,
AA26C628204134C500318B9B /* TicketTableViewCell.swift in Sources */,
299B5E471FD2C87F0051361C /* ConfigureTransactionError.swift in Sources */,
29E2E3411F7B1585000CF94A /* ActionButtonRow.swift in Sources */,
@ -3089,7 +3104,6 @@
29285B421F6FB3E60044CF29 /* SendViewController.swift in Sources */,
2996F14D1F6CA743005C33AE /* UIViewController.swift in Sources */,
2959961F1FAE759700DB66A8 /* RawTransaction.swift in Sources */,
73ACEEFF20163E08003DD71D /* PasscodeCharacterView.swift in Sources */,
295B61D41FE7D5B500642E60 /* CurrencyFormatter.swift in Sources */,
296421991F70C1F900EB363B /* EmptyView.swift in Sources */,
298542E51FBA9B0700CB5081 /* ShapeShift.swift in Sources */,
@ -3126,7 +3140,6 @@
29CAEB8E1F70A2FB00F7357D /* TransactionCellViewModel.swift in Sources */,
298542E81FBAD0B200CB5081 /* OperationType.swift in Sources */,
29BE3FD01F7071A200F6BFC2 /* UIColor.swift in Sources */,
73ACEEFB20163C94003DD71D /* LockView.swift in Sources */,
771AA964200D5EDB00D25403 /* WordCollectionViewCell.swift in Sources */,
77872D302026DC570032D687 /* SplashViewController.swift in Sources */,
29C80D4D1FB5202C0037B1E0 /* BalanceBaseViewModel.swift in Sources */,
@ -3154,14 +3167,12 @@
771A8471202F067D00528D28 /* NetworksViewController.swift in Sources */,
613D048B1FDE162B008DE72E /* TrustProvider.swift in Sources */,
61C359E02002AA5A0097B04D /* TransactionSigning.swift in Sources */,
73ACEEF5201631A3003DD71D /* ProtectionCoordinator.swift in Sources */,
29C0FCE1200DA94A004A13CB /* SignMessageCoordinator.swift in Sources */,
29B933F81F8609FF009FCABB /* PaymentFlow.swift in Sources */,
2963B6BF1F9AB9A2003063C1 /* ContractERC20Transfer.swift in Sources */,
29A13E331F6B1B7A00E432A2 /* AppStyle.swift in Sources */,
737EEDDA201BE3A8009D9D5D /* Lock.swift in Sources */,
29FF12F81F747D6C00AFD326 /* Error.swift in Sources */,
73BAA8D1201B2FBC00D6DD75 /* LockCreatePasscodeViewController.swift in Sources */,
B1DC375D203AEAE200C9756D /* OrdersRequest.swift in Sources */,
29AD8A061F93DC8C008E10E7 /* PushDevice.swift in Sources */,
294DFBA91FE6EBFB004CEB56 /* NewTokenViewController.swift in Sources */,
@ -3189,7 +3200,6 @@
BB5D6A9E20232EE8000FC5AB /* CurrencyRate+Fee.swift in Sources */,
299B5E291FCA8F040051361C /* GetERC20Balance.swift in Sources */,
77E0E773201FAD06009B4B31 /* BrowserURLParser.swift in Sources */,
73ACEF0520163F46003DD71D /* LockEnterPasscodeCoordinator.swift in Sources */,
29F1C863200375D2003780D8 /* Wallet.swift in Sources */,
29E6E06E1FE897EE0079265A /* BrowserViewController.swift in Sources */,
2912CCF91F6A830700C6CBE3 /* AppDelegate.swift in Sources */,
@ -3198,7 +3208,6 @@
771A847A2032344D00528D28 /* PreferencesController.swift in Sources */,
296AF9A91F737F6F0058AF78 /* SendRawTransactionRequest.swift in Sources */,
293112121FC4F48400966EEA /* ServiceProvider.swift in Sources */,
73C5CF45201A7CAB00C74316 /* LockPasscodeViewController.swift in Sources */,
2912CD2F1F6A83A100C6CBE3 /* ImportWalletViewController.swift in Sources */,
7721A6D0202EFD07004DB16C /* AddCustomNetworkCoordinator.swift in Sources */,
2963B6AD1F981A96003063C1 /* TransactionAppearance.swift in Sources */,
@ -3256,7 +3265,6 @@
29D03F1D1F712183006E548C /* Button.swift in Sources */,
291794FF1F95F5CE00539A30 /* Web3Request.swift in Sources */,
CCCD74FD1FD2D38D004A087D /* CheckDeviceCoordinator.swift in Sources */,
73BAA8D3201B2FF800D6DD75 /* LockEnterPasscodeViewController.swift in Sources */,
29E2E33E1F7A2423000CF94A /* TransactionHeaderView.swift in Sources */,
291F52BF1F6C874E00B369AB /* AccountsViewController.swift in Sources */,
2963B6AF1F9823E6003063C1 /* UnconfirmedTransaction.swift in Sources */,
@ -3340,6 +3348,14 @@
5E7C7C98EAF40E8110241DBD /* TicketTokenViewCell.swift in Sources */,
5E7C71B52A77008694BFA5D1 /* TokensDataStore.swift in Sources */,
5E7C7793AB6B577906F2BCA3 /* SettingsViewController.swift in Sources */,
5E7C75C99B9F595F26EDC405 /* LockPasscodeViewController.swift in Sources */,
5E7C710331196CD591B51785 /* LockCreatePasscodeViewController.swift in Sources */,
5E7C71C3356BF80240720EFE /* LockEnterPasscodeViewController.swift in Sources */,
5E7C7499A8D6814F7950DA70 /* LockCreatePasscodeCoordinator.swift in Sources */,
5E7C71F8050CCF990539B293 /* LockView.swift in Sources */,
5E7C74B99922D0CAB635970E /* PasscodeCharacterView.swift in Sources */,
5E7C76B917517C93D1E26B0A /* LockEnterPasscodeCoordinator.swift in Sources */,
5E7C76A0365D128B7F19A0C2 /* ProtectionCoordinator.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

@ -0,0 +1,91 @@
// Copyright SIX DAY LLC. All rights reserved.
// Copyright © 2018 Stormbird PTE. LTD.
import UIKit
import LocalAuthentication
class LockEnterPasscodeViewController: LockPasscodeViewController {
private lazy var lockEnterPasscodeViewModel: LockEnterPasscodeViewModel? = {
return self.model as? LockEnterPasscodeViewModel
}()
var unlockWithResult: ((_ success: Bool, _ bioUnlock: Bool) -> Void)?
private var context: LAContext!
override func viewDidLoad() {
super.viewDidLoad()
self.lockView.lockTitle.text = lockEnterPasscodeViewModel?.initialLabelText
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//If max attempt limit is reached we should valdiate if one minute gone.
if lock.incorrectMaxAttemptTimeIsSet() {
self.lockView.lockTitle.text = lockEnterPasscodeViewModel?.tryAfterOneMinute
maxAttemptTimerValidation()
}
}
func showBioMerickAuth() {
self.context = LAContext()
self.touchValidation()
}
override func enteredPasscode(_ passcode: String) {
super.enteredPasscode(passcode)
if lock.isPasscodeValid(passcode: passcode) {
lock.resetPasscodeAttemptHistory()
lock.removeIncorrectMaxAttemptTime()
self.lockView.lockTitle.text = lockEnterPasscodeViewModel?.initialLabelText
unlock(withResult: true, bioUnlock: false)
} else {
let numberOfAttempts = self.lock.numberOfAttempts()
let passcodeAttemptLimit = model.passcodeAttemptLimit()
let text = String(format: NSLocalizedString("lock.enter.passcode.view.model.incorrect.passcode", value: "Incorrect passcode. You have %d attempts.", comment: ""), passcodeAttemptLimit - numberOfAttempts)
lockView.lockTitle.text = text
lockView.shake()
if numberOfAttempts >= passcodeAttemptLimit {
exceededLimit()
return
}
self.lock.recordIncorrectPasscodeAttempt()
}
}
private func exceededLimit() {
self.lockView.lockTitle.text = lockEnterPasscodeViewModel?.tryAfterOneMinute
lock.recordIncorrectMaxAttemptTime()
self.hideKeyboard()
}
private func maxAttemptTimerValidation() {
let now = Date()
let maxAttemptTimer = lock.recordedMaxAttemptTime()
let interval = now.timeIntervalSince(maxAttemptTimer)
//if interval is greater or equal 60 seconds we give 1 attempt.
if interval >= 60 {
self.lockView.lockTitle.text = lockEnterPasscodeViewModel?.initialLabelText
self.showKeyboard()
}
}
private func unlock(withResult success: Bool, bioUnlock: Bool) {
self.view.endEditing(true)
if let unlock = unlockWithResult {
unlock(success, bioUnlock)
}
}
private func canEvaluatePolicy() -> Bool {
return context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
}
private func touchValidation() {
guard canEvaluatePolicy(), let reason = lockEnterPasscodeViewModel?.loginReason else {
return
}
self.hideKeyboard()
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { [weak self] success, _ in
DispatchQueue.main.async {
if success {
self?.lock.resetPasscodeAttemptHistory()
self?.lock.removeIncorrectMaxAttemptTime()
self?.lockView.lockTitle.text = self?.lockEnterPasscodeViewModel?.initialLabelText
self?.unlock(withResult: true, bioUnlock: true)
} else {
self?.showKeyboard()
}
}
}
}
}

@ -1,4 +1,5 @@
// Copyright SIX DAY LLC. All rights reserved.
// Copyright © 2018 Stormbird PTE. LTD.
import UIKit

@ -1,37 +1,38 @@
// Copyright SIX DAY LLC. All rights reserved.
// Copyright © 2018 Stormbird PTE. LTD.
import UIKit
class LockCreatePasscodeViewController: LockPasscodeViewController {
private lazy var lockCreatePasscodeViewModel: LockCreatePasscodeViewModel? = {
return self.model as? LockCreatePasscodeViewModel
}()
private var firstPasscode: String?
override func viewDidLoad() {
super.viewDidLoad()
self.title = lockCreatePasscodeViewModel?.title
self.lockView.lockTitle.text = lockCreatePasscodeViewModel?.initialLabelText
}
override func enteredPasscode(_ passcode: String) {
super.enteredPasscode(passcode)
if let first = firstPasscode {
if passcode == first {
lock.setPasscode(passcode: passcode)
finish(withResult: true, animated: true)
} else {
lockView.shake()
firstPasscode = nil
showFirstPasscodeView()
}
} else {
firstPasscode = passcode
showConfirmPasscodeView()
}
}
private func showFirstPasscodeView() {
self.lockView.lockTitle.text = lockCreatePasscodeViewModel?.initialLabelText
}
private func showConfirmPasscodeView() {
self.lockView.lockTitle.text = lockCreatePasscodeViewModel?.confirmLabelText
}
private lazy var lockCreatePasscodeViewModel: LockCreatePasscodeViewModel? = {
return self.model as? LockCreatePasscodeViewModel
}()
private var firstPasscode: String?
override func viewDidLoad() {
super.viewDidLoad()
self.title = lockCreatePasscodeViewModel?.title
self.lockView.lockTitle.text = lockCreatePasscodeViewModel?.initialLabelText
}
override func enteredPasscode(_ passcode: String) {
super.enteredPasscode(passcode)
if let first = firstPasscode {
if passcode == first {
lock.setPasscode(passcode: passcode)
finish(withResult: true, animated: true)
} else {
lockView.shake()
firstPasscode = nil
showFirstPasscodeView()
}
} else {
firstPasscode = passcode
showConfirmPasscodeView()
}
}
private func showFirstPasscodeView() {
self.lockView.lockTitle.text = lockCreatePasscodeViewModel?.initialLabelText
}
private func showConfirmPasscodeView() {
self.lockView.lockTitle.text = lockCreatePasscodeViewModel?.confirmLabelText
}
}

@ -1,90 +1,91 @@
// Copyright SIX DAY LLC. All rights reserved.
// Copyright © 2018 Stormbird PTE. LTD.
import UIKit
import LocalAuthentication
class LockEnterPasscodeViewController: LockPasscodeViewController {
private lazy var lockEnterPasscodeViewModel: LockEnterPasscodeViewModel? = {
return self.model as? LockEnterPasscodeViewModel
}()
var unlockWithResult: ((_ success: Bool, _ bioUnlock: Bool) -> Void)?
private var context: LAContext!
override func viewDidLoad() {
super.viewDidLoad()
self.lockView.lockTitle.text = lockEnterPasscodeViewModel?.initialLabelText
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//If max attempt limit is reached we should valdiate if one minute gone.
if lock.incorrectMaxAttemptTimeIsSet() {
self.lockView.lockTitle.text = lockEnterPasscodeViewModel?.tryAfterOneMinute
maxAttemptTimerValidation()
}
}
func showBioMerickAuth() {
self.context = LAContext()
self.touchValidation()
}
override func enteredPasscode(_ passcode: String) {
super.enteredPasscode(passcode)
if lock.isPasscodeValid(passcode: passcode) {
lock.resetPasscodeAttemptHistory()
lock.removeIncorrectMaxAttemptTime()
self.lockView.lockTitle.text = lockEnterPasscodeViewModel?.initialLabelText
unlock(withResult: true, bioUnlock: false)
} else {
let numberOfAttempts = self.lock.numberOfAttempts()
let passcodeAttemptLimit = model.passcodeAttemptLimit()
let text = String(format: NSLocalizedString("lock.enter.passcode.view.model.incorrect.passcode", value: "Incorrect passcode. You have %d attempts.", comment: ""), passcodeAttemptLimit - numberOfAttempts)
lockView.lockTitle.text = text
lockView.shake()
if numberOfAttempts >= passcodeAttemptLimit {
exceededLimit()
return
}
self.lock.recordIncorrectPasscodeAttempt()
}
}
private func exceededLimit() {
self.lockView.lockTitle.text = lockEnterPasscodeViewModel?.tryAfterOneMinute
lock.recordIncorrectMaxAttemptTime()
self.hideKeyboard()
}
private func maxAttemptTimerValidation() {
let now = Date()
let maxAttemptTimer = lock.recordedMaxAttemptTime()
let interval = now.timeIntervalSince(maxAttemptTimer)
//if interval is greater or equal 60 seconds we give 1 attempt.
if interval >= 60 {
self.lockView.lockTitle.text = lockEnterPasscodeViewModel?.initialLabelText
self.showKeyboard()
}
}
private func unlock(withResult success: Bool, bioUnlock: Bool) {
self.view.endEditing(true)
if let unlock = unlockWithResult {
unlock(success, bioUnlock)
}
}
private func canEvaluatePolicy() -> Bool {
return context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
}
private func touchValidation() {
guard canEvaluatePolicy(), let reason = lockEnterPasscodeViewModel?.loginReason else {
return
}
self.hideKeyboard()
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { [weak self] success, _ in
DispatchQueue.main.async {
if success {
self?.lock.resetPasscodeAttemptHistory()
self?.lock.removeIncorrectMaxAttemptTime()
self?.lockView.lockTitle.text = self?.lockEnterPasscodeViewModel?.initialLabelText
self?.unlock(withResult: true, bioUnlock: true)
} else {
self?.showKeyboard()
}
}
}
}
private lazy var lockEnterPasscodeViewModel: LockEnterPasscodeViewModel? = {
return self.model as? LockEnterPasscodeViewModel
}()
var unlockWithResult: ((_ success: Bool, _ bioUnlock: Bool) -> Void)?
private var context: LAContext!
override func viewDidLoad() {
super.viewDidLoad()
self.lockView.lockTitle.text = lockEnterPasscodeViewModel?.initialLabelText
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//If max attempt limit is reached we should valdiate if one minute gone.
if lock.incorrectMaxAttemptTimeIsSet() {
self.lockView.lockTitle.text = lockEnterPasscodeViewModel?.tryAfterOneMinute
maxAttemptTimerValidation()
}
}
func showBioMerickAuth() {
self.context = LAContext()
self.touchValidation()
}
override func enteredPasscode(_ passcode: String) {
super.enteredPasscode(passcode)
if lock.isPasscodeValid(passcode: passcode) {
lock.resetPasscodeAttemptHistory()
lock.removeIncorrectMaxAttemptTime()
self.lockView.lockTitle.text = lockEnterPasscodeViewModel?.initialLabelText
unlock(withResult: true, bioUnlock: false)
} else {
let numberOfAttempts = self.lock.numberOfAttempts()
let passcodeAttemptLimit = model.passcodeAttemptLimit()
let text = String(format: NSLocalizedString("lock.enter.passcode.view.model.incorrect.passcode", value: "Incorrect passcode. You have %d attempts.", comment: ""), passcodeAttemptLimit - numberOfAttempts)
lockView.lockTitle.text = text
lockView.shake()
if numberOfAttempts >= passcodeAttemptLimit {
exceededLimit()
return
}
self.lock.recordIncorrectPasscodeAttempt()
}
}
private func exceededLimit() {
self.lockView.lockTitle.text = lockEnterPasscodeViewModel?.tryAfterOneMinute
lock.recordIncorrectMaxAttemptTime()
self.hideKeyboard()
}
private func maxAttemptTimerValidation() {
let now = Date()
let maxAttemptTimer = lock.recordedMaxAttemptTime()
let interval = now.timeIntervalSince(maxAttemptTimer)
//if interval is greater or equal 60 seconds we give 1 attempt.
if interval >= 60 {
self.lockView.lockTitle.text = lockEnterPasscodeViewModel?.initialLabelText
self.showKeyboard()
}
}
private func unlock(withResult success: Bool, bioUnlock: Bool) {
self.view.endEditing(true)
if let unlock = unlockWithResult {
unlock(success, bioUnlock)
}
}
private func canEvaluatePolicy() -> Bool {
return context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
}
private func touchValidation() {
guard canEvaluatePolicy(), let reason = lockEnterPasscodeViewModel?.loginReason else {
return
}
self.hideKeyboard()
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { [weak self] success, _ in
DispatchQueue.main.async {
if success {
self?.lock.resetPasscodeAttemptHistory()
self?.lock.removeIncorrectMaxAttemptTime()
self?.lockView.lockTitle.text = self?.lockEnterPasscodeViewModel?.initialLabelText
self?.unlock(withResult: true, bioUnlock: true)
} else {
self?.showKeyboard()
}
}
}
}
}

@ -1,134 +1,135 @@
// Copyright SIX DAY LLC. All rights reserved.
// Copyright © 2018 Stormbird PTE. LTD.
import UIKit
class LockPasscodeViewController: UIViewController {
var willFinishWithResult: ((_ success: Bool) -> Void)?
let model: LockViewModel
var lockView: LockView!
let lock = Lock()
private var invisiblePasscodeField = UITextField()
private var shouldIgnoreTextFieldDelegateCalls = false
init(model: LockViewModel) {
self.model = model
super.init(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
self.navigationItem.hidesBackButton = true
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: .UIKeyboardWillShow, object: nil)
self.view.backgroundColor = UIColor.white
self.configureInvisiblePasscodeField()
self.configureNavigationItems()
self.configureLockView()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if !invisiblePasscodeField.isFirstResponder && !lock.incorrectMaxAttemptTimeIsSet() {
invisiblePasscodeField.becomeFirstResponder()
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if invisiblePasscodeField.isFirstResponder {
invisiblePasscodeField.resignFirstResponder()
}
}
private func configureInvisiblePasscodeField() {
invisiblePasscodeField = UITextField()
invisiblePasscodeField.keyboardType = .numberPad
invisiblePasscodeField.isSecureTextEntry = true
invisiblePasscodeField.delegate = self
invisiblePasscodeField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
view.addSubview(invisiblePasscodeField)
}
private func configureNavigationItems() {
navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("Cancel", value: "Cancel", comment: ""), style: .plain, target: self, action: #selector(self.userTappedCancel))
}
private func configureLockView() {
lockView = LockView(model)
lockView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(lockView)
lockView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
lockView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
lockView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
lockView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
}
@objc func userTappedCancel() {
if let finish = willFinishWithResult {
finish(false)
}
dismiss(animated: true, completion: nil)
}
@objc func enteredPasscode(_ passcode: String) {
shouldIgnoreTextFieldDelegateCalls = false
clearPasscode()
}
func clearPasscode() {
invisiblePasscodeField.text = ""
for characterView in lockView.characters {
characterView.setEmpty(true)
}
}
func hideKeyboard() {
invisiblePasscodeField.resignFirstResponder()
}
func showKeyboard() {
invisiblePasscodeField.becomeFirstResponder()
}
func finish(withResult success: Bool, animated: Bool) {
invisiblePasscodeField.resignFirstResponder()
if let finish = willFinishWithResult {
finish(success)
} else {
dismiss(animated: true, completion: nil)
}
}
@objc func keyboardWillShow(_ notification: Notification) {
if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.lockView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -keyboardSize.height).isActive = true
})
}
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
NotificationCenter.default.removeObserver(self)
}
var willFinishWithResult: ((_ success: Bool) -> Void)?
let model: LockViewModel
var lockView: LockView!
let lock = Lock()
private var invisiblePasscodeField = UITextField()
private var shouldIgnoreTextFieldDelegateCalls = false
init(model: LockViewModel) {
self.model = model
super.init(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
self.navigationItem.hidesBackButton = true
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: .UIKeyboardWillShow, object: nil)
self.view.backgroundColor = Colors.appBackground
self.configureInvisiblePasscodeField()
self.configureNavigationItems()
self.configureLockView()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if !invisiblePasscodeField.isFirstResponder && !lock.incorrectMaxAttemptTimeIsSet() {
invisiblePasscodeField.becomeFirstResponder()
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if invisiblePasscodeField.isFirstResponder {
invisiblePasscodeField.resignFirstResponder()
}
}
private func configureInvisiblePasscodeField() {
invisiblePasscodeField = UITextField()
invisiblePasscodeField.keyboardType = .numberPad
invisiblePasscodeField.isSecureTextEntry = true
invisiblePasscodeField.delegate = self
invisiblePasscodeField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
view.addSubview(invisiblePasscodeField)
}
private func configureNavigationItems() {
navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("Cancel", value: "Cancel", comment: ""), style: .plain, target: self, action: #selector(self.userTappedCancel))
}
private func configureLockView() {
lockView = LockView(model)
lockView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(lockView)
lockView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
lockView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
lockView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
lockView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
}
@objc func userTappedCancel() {
if let finish = willFinishWithResult {
finish(false)
}
dismiss(animated: true, completion: nil)
}
@objc func enteredPasscode(_ passcode: String) {
shouldIgnoreTextFieldDelegateCalls = false
clearPasscode()
}
func clearPasscode() {
invisiblePasscodeField.text = ""
for characterView in lockView.characters {
characterView.setEmpty(true)
}
}
func hideKeyboard() {
invisiblePasscodeField.resignFirstResponder()
}
func showKeyboard() {
invisiblePasscodeField.becomeFirstResponder()
}
func finish(withResult success: Bool, animated: Bool) {
invisiblePasscodeField.resignFirstResponder()
if let finish = willFinishWithResult {
finish(success)
} else {
dismiss(animated: true, completion: nil)
}
}
@objc func keyboardWillShow(_ notification: Notification) {
if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.lockView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -keyboardSize.height).isActive = true
})
}
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
extension LockPasscodeViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if shouldIgnoreTextFieldDelegateCalls {
return false
}
let newString: String? = (textField.text as NSString?)?.replacingCharacters(in: range, with: string)
let newLength: Int = newString?.count ?? 0
if newLength > model.charCount() {
lockView.shake()
textField.text = ""
return false
} else {
for characterView in lockView.characters {
let index: Int = lockView.characters.index(of: characterView)!
characterView.setEmpty(index >= newLength)
}
return true
}
}
@objc func textFieldDidChange(_ textField: UITextField) {
if shouldIgnoreTextFieldDelegateCalls {
return
}
let newString: String? = textField.text
let newLength: Int = newString?.count ?? 0
if newLength == model.charCount() {
shouldIgnoreTextFieldDelegateCalls = true
textField.text = ""
perform(#selector(self.enteredPasscode), with: newString, afterDelay: 0.3)
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if shouldIgnoreTextFieldDelegateCalls {
return false
}
let newString: String? = (textField.text as NSString?)?.replacingCharacters(in: range, with: string)
let newLength: Int = newString?.count ?? 0
if newLength > model.charCount() {
lockView.shake()
textField.text = ""
return false
} else {
for characterView in lockView.characters {
let index: Int = lockView.characters.index(of: characterView)!
characterView.setEmpty(index >= newLength)
}
return true
}
}
@objc func textFieldDidChange(_ textField: UITextField) {
if shouldIgnoreTextFieldDelegateCalls {
return
}
let newString: String? = textField.text
let newLength: Int = newString?.count ?? 0
if newLength == model.charCount() {
shouldIgnoreTextFieldDelegateCalls = true
textField.text = ""
perform(#selector(self.enteredPasscode), with: newString, afterDelay: 0.3)
}
}
}

@ -1,68 +1,70 @@
// Copyright SIX DAY LLC. All rights reserved.
// Copyright © 2018 Stormbird PTE. LTD.
import UIKit
class LockView: UIView {
var characterView = UIStackView()
var lockTitle = UILabel()
var model: LockViewModel!
var characters: [PasscodeCharacterView]!
init(_ model: LockViewModel) {
super.init(frame: CGRect.zero)
self.model = model
self.characters = passcodeCharacters()
configCharacterView()
configLabel()
addUiElements()
applyConstraints()
}
private func configCharacterView() {
characterView = UIStackView(arrangedSubviews: self.characters)
characterView.axis = .horizontal
characterView.distribution = .fillEqually
characterView.alignment = .fill
characterView.spacing = 20
characterView.translatesAutoresizingMaskIntoConstraints = false
}
private func configLabel() {
lockTitle.font = UIFont.systemFont(ofSize: 19)
lockTitle.textAlignment = .center
lockTitle.translatesAutoresizingMaskIntoConstraints = false
}
private func applyConstraints() {
characterView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
characterView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
characterView.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
lockTitle.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
lockTitle.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
lockTitle.bottomAnchor.constraint(equalTo: characterView.topAnchor, constant: -20).isActive = true
}
private func addUiElements() {
self.backgroundColor = UIColor.white
self.addSubview(lockTitle)
self.addSubview(characterView)
}
private func passcodeCharacters() -> [PasscodeCharacterView] {
var characters = [PasscodeCharacterView]()
for _ in 0..<model.charCount() {
let passcodeCharacterView = PasscodeCharacterView()
passcodeCharacterView.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
passcodeCharacterView.widthAnchor.constraint(equalToConstant: 20).isActive = true
characters.append(passcodeCharacterView)
}
return characters
}
func shake() {
let keypath = "position"
let animation = CABasicAnimation(keyPath: keypath)
animation.duration = 0.07
animation.repeatCount = 4
animation.autoreverses = true
animation.fromValue = NSValue(cgPoint: CGPoint(x: characterView.center.x - 10, y: characterView.center.y))
animation.toValue = NSValue(cgPoint: CGPoint(x: characterView.center.x + 10, y: characterView.center.y))
characterView.layer.add(animation, forKey: keypath)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var characterView = UIStackView()
var lockTitle = UILabel()
var model: LockViewModel!
var characters: [PasscodeCharacterView]!
init(_ model: LockViewModel) {
super.init(frame: CGRect.zero)
self.model = model
self.characters = passcodeCharacters()
configCharacterView()
configLabel()
addUiElements()
applyConstraints()
}
private func configCharacterView() {
characterView = UIStackView(arrangedSubviews: self.characters)
characterView.axis = .horizontal
characterView.distribution = .fillEqually
characterView.alignment = .fill
characterView.spacing = 20
characterView.translatesAutoresizingMaskIntoConstraints = false
}
private func configLabel() {
lockTitle.font = Fonts.light(size: 20)
lockTitle.textAlignment = .center
lockTitle.translatesAutoresizingMaskIntoConstraints = false
lockTitle.textColor = Colors.appWhite
}
private func applyConstraints() {
characterView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
characterView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
characterView.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
lockTitle.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
lockTitle.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
lockTitle.bottomAnchor.constraint(equalTo: characterView.topAnchor, constant: -20).isActive = true
}
private func addUiElements() {
self.backgroundColor = Colors.appBackground
self.addSubview(lockTitle)
self.addSubview(characterView)
}
private func passcodeCharacters() -> [PasscodeCharacterView] {
var characters = [PasscodeCharacterView]()
for _ in 0..<model.charCount() {
let passcodeCharacterView = PasscodeCharacterView()
passcodeCharacterView.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
passcodeCharacterView.widthAnchor.constraint(equalToConstant: 20).isActive = true
characters.append(passcodeCharacterView)
}
return characters
}
func shake() {
let keypath = "position"
let animation = CABasicAnimation(keyPath: keypath)
animation.duration = 0.07
animation.repeatCount = 4
animation.autoreverses = true
animation.fromValue = NSValue(cgPoint: CGPoint(x: characterView.center.x - 10, y: characterView.center.y))
animation.toValue = NSValue(cgPoint: CGPoint(x: characterView.center.x + 10, y: characterView.center.y))
characterView.layer.add(animation, forKey: keypath)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

@ -1,61 +1,62 @@
// Copyright SIX DAY LLC. All rights reserved.
// Copyright © 2018 Stormbird PTE. LTD.
import UIKit
class PasscodeCharacterView: UIView {
var isEmpty = true
private var circle: CAShapeLayer?
private var hyphen: CAShapeLayer?
override func layoutSubviews() {
commonInit()
}
private func commonInit() {
isEmpty = true
backgroundColor = UIColor.clear
drawCircle()
drawHyphen()
redraw()
}
private func redraw() {
circle?.isHidden = isEmpty
hyphen?.isHidden = !isEmpty
}
private func drawCircle() {
let borderWidth: CGFloat = 2
let radius: CGFloat = bounds.width / 2 - borderWidth
let circle = CAShapeLayer()
circle.path = UIBezierPath(roundedRect: CGRect(x: borderWidth, y: borderWidth, width: 2.0 * radius, height: 2.0 * radius), cornerRadius: radius).cgPath
let circleColor: UIColor? = UIColor.black
circle.fillColor = circleColor?.cgColor
circle.strokeColor = circleColor?.cgColor
circle.borderWidth = borderWidth
layer.addSublayer(circle)
self.circle = circle
}
private func drawHyphen() {
let horizontalMargin: CGFloat = 2
let hyphenHeight: CGFloat = bounds.height / 7
let hyphen = CAShapeLayer()
let hyphenPath = UIBezierPath()
let leftTopCorner = CGPoint(x: horizontalMargin, y: 3 * hyphenHeight)
let rightTopCorner = CGPoint(x: bounds.width - horizontalMargin, y: 3 * hyphenHeight)
let rightBottomCorner = CGPoint(x: bounds.width - horizontalMargin, y: 4 * hyphenHeight)
let leftBottomCorner = CGPoint(x: horizontalMargin, y: 4 * hyphenHeight)
hyphenPath.move(to: leftTopCorner)
hyphenPath.addLine(to: rightTopCorner)
hyphenPath.addLine(to: rightBottomCorner)
hyphenPath.addLine(to: leftBottomCorner)
hyphen.path = hyphenPath.cgPath
let hyphenColor: UIColor? = UIColor.black
hyphen.fillColor = hyphenColor?.cgColor
hyphen.strokeColor = hyphenColor?.cgColor
layer.addSublayer(hyphen)
self.hyphen = hyphen
}
func setEmpty(_ isEmpty: Bool) {
if self.isEmpty != isEmpty {
self.isEmpty = isEmpty
redraw()
}
}
var isEmpty = true
private var circle: CAShapeLayer?
private var hyphen: CAShapeLayer?
override func layoutSubviews() {
commonInit()
}
private func commonInit() {
isEmpty = true
backgroundColor = UIColor.clear
drawCircle()
drawHyphen()
redraw()
}
private func redraw() {
circle?.isHidden = isEmpty
hyphen?.isHidden = !isEmpty
}
private func drawCircle() {
let borderWidth: CGFloat = 2
let radius: CGFloat = bounds.width / 2 - borderWidth
let circle = CAShapeLayer()
circle.path = UIBezierPath(roundedRect: CGRect(x: borderWidth, y: borderWidth, width: 2.0 * radius, height: 2.0 * radius), cornerRadius: radius).cgPath
let circleColor: UIColor? = Colors.appWhite
circle.fillColor = circleColor?.cgColor
circle.strokeColor = circleColor?.cgColor
circle.borderWidth = borderWidth
layer.addSublayer(circle)
self.circle = circle
}
private func drawHyphen() {
let horizontalMargin: CGFloat = 2
let hyphenHeight: CGFloat = bounds.height / 7
let hyphen = CAShapeLayer()
let hyphenPath = UIBezierPath()
let leftTopCorner = CGPoint(x: horizontalMargin, y: 3 * hyphenHeight)
let rightTopCorner = CGPoint(x: bounds.width - horizontalMargin, y: 3 * hyphenHeight)
let rightBottomCorner = CGPoint(x: bounds.width - horizontalMargin, y: 4 * hyphenHeight)
let leftBottomCorner = CGPoint(x: horizontalMargin, y: 4 * hyphenHeight)
hyphenPath.move(to: leftTopCorner)
hyphenPath.addLine(to: rightTopCorner)
hyphenPath.addLine(to: rightBottomCorner)
hyphenPath.addLine(to: leftBottomCorner)
hyphen.path = hyphenPath.cgPath
let hyphenColor: UIColor? = Colors.appWhite
hyphen.fillColor = hyphenColor?.cgColor
hyphen.strokeColor = hyphenColor?.cgColor
layer.addSublayer(hyphen)
self.hyphen = hyphen
}
func setEmpty(_ isEmpty: Bool) {
if self.isEmpty != isEmpty {
self.isEmpty = isEmpty
redraw()
}
}
}

@ -0,0 +1,40 @@
// Copyright SIX DAY LLC. All rights reserved.
// Copyright © 2018 Stormbird PTE. LTD.
import UIKit
class LockEnterPasscodeCoordinator: Coordinator {
var coordinators: [Coordinator] = []
var protectionWasShown = false
private let window: UIWindow = UIWindow()
private let model: LockEnterPasscodeViewModel
private let lock: LockInterface
private lazy var lockEnterPasscodeViewController: LockEnterPasscodeViewController = {
return LockEnterPasscodeViewController(model: model)
}()
init(model: LockEnterPasscodeViewModel, lock: LockInterface = Lock()) {
self.window.windowLevel = UIWindowLevelStatusBar + 1.0
self.model = model
self.lock = lock
lockEnterPasscodeViewController.unlockWithResult = { [weak self] (state, bioUnlock) in
if state {
self?.stop()
}
self?.protectionWasShown = bioUnlock
}
}
func start() {
guard lock.isPasscodeSet() else {
return
}
protectionWasShown = true
window.rootViewController = lockEnterPasscodeViewController
window.makeKeyAndVisible()
//Because of the usage of the window and rootViewController we are not able to receive properly view life circle events. So we should call this methods manually.
lockEnterPasscodeViewController.showKeyboard()
lockEnterPasscodeViewController.showBioMerickAuth()
}
func stop() {
window.isHidden = true
}
}

@ -1,46 +1,47 @@
// Copyright SIX DAY LLC. All rights reserved.
// Copyright © 2018 Stormbird PTE. LTD.
import UIKit
class ProtectionCoordinator: Coordinator {
var coordinators: [Coordinator] = []
lazy var splashCoordinator: SplashCoordinator = {
return SplashCoordinator(window: self.protectionWindow)
}()
lazy var lockEnterPasscodeCoordinator: LockEnterPasscodeCoordinator = {
return LockEnterPasscodeCoordinator(model: LockEnterPasscodeViewModel())
}()
let protectionWindow = UIWindow()
init() {
protectionWindow.windowLevel = UIWindowLevelStatusBar + 2.0
}
func didFinishLaunchingWithOptions() {
splashCoordinator.start()
lockEnterPasscodeCoordinator.start()
}
func applicationWillResignActive() {
splashCoordinator.start()
}
func applicationDidBecomeActive() {
//We track protectionWasShown because of the Touch ID that will trigger applicationDidBecomeActive method after valdiation.
if !lockEnterPasscodeCoordinator.protectionWasShown {
lockEnterPasscodeCoordinator.start()
} else {
lockEnterPasscodeCoordinator.protectionWasShown = false
}
//We should dismiss spalsh screen when app become active.
splashCoordinator.stop()
}
func applicationDidEnterBackground() {
splashCoordinator.start()
}
func applicationWillEnterForeground() {
splashCoordinator.stop()
}
var coordinators: [Coordinator] = []
lazy var splashCoordinator: SplashCoordinator = {
return SplashCoordinator(window: self.protectionWindow)
}()
lazy var lockEnterPasscodeCoordinator: LockEnterPasscodeCoordinator = {
return LockEnterPasscodeCoordinator(model: LockEnterPasscodeViewModel())
}()
let protectionWindow = UIWindow()
init() {
protectionWindow.windowLevel = UIWindowLevelStatusBar + 2.0
}
func didFinishLaunchingWithOptions() {
splashCoordinator.start()
lockEnterPasscodeCoordinator.start()
}
func applicationWillResignActive() {
splashCoordinator.start()
}
func applicationDidBecomeActive() {
//We track protectionWasShown because of the Touch ID that will trigger applicationDidBecomeActive method after valdiation.
if !lockEnterPasscodeCoordinator.protectionWasShown {
lockEnterPasscodeCoordinator.start()
} else {
lockEnterPasscodeCoordinator.protectionWasShown = false
}
//We should dismiss spalsh screen when app become active.
splashCoordinator.stop()
}
func applicationDidEnterBackground() {
splashCoordinator.start()
}
func applicationWillEnterForeground() {
splashCoordinator.stop()
}
}

Loading…
Cancel
Save