Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f86210f5da | ||
|
|
e96f678236 | ||
|
|
8b9067ab4b | ||
|
|
5af383235a | ||
|
|
25b13dfb22 | ||
|
|
635692ef09 | ||
|
|
e6bab4e504 | ||
|
|
cd6b7e6051 | ||
|
|
b180202e07 | ||
|
|
f83f2af529 | ||
|
|
cf2c30cfe6 | ||
|
|
834e94ebd5 | ||
|
|
6d9be3fdfe | ||
|
|
07e55140ac | ||
|
|
fbbd08790d | ||
|
|
fcaa5d1d8c | ||
|
|
ea5be8efcb | ||
|
|
c8b4933c3d | ||
|
|
981ee171ae | ||
|
|
3ed6b93736 | ||
|
|
56a515c5ea | ||
|
|
15ac7a2dbf | ||
|
|
c5c7999753 | ||
|
|
bb58ed0169 | ||
|
|
4545a5c745 | ||
|
|
da8c7064fe | ||
|
|
d9bd604436 | ||
|
|
c99252809d | ||
|
|
d704f451a3 | ||
|
|
2c9ab5d153 | ||
|
|
d5d33da12f | ||
|
|
cbef1a611b | ||
|
|
0a1f215a1a | ||
|
|
907d2a8ca6 |
2
platform-darwin/External/InAppSettingsKit
vendored
2
platform-darwin/External/InAppSettingsKit
vendored
Submodule platform-darwin/External/InAppSettingsKit updated: b58b72563a...2dcb598d18
2
platform-darwin/External/Pearl
vendored
2
platform-darwin/External/Pearl
vendored
Submodule platform-darwin/External/Pearl updated: 3ceb601ba4...af1cf4cba3
@@ -19,7 +19,9 @@
|
||||
93D3932889B6B4206E66A6D6 /* PearlEMail.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */; };
|
||||
93D39392DEDA376F93C6C718 /* MPCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39BAA71DE51B4D8A1286C /* MPCell.m */; };
|
||||
93D3939661CE37180AF7CD6A /* MPStoreViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3957D76F71A652716EECC /* MPStoreViewController.m */; };
|
||||
93D393AA69A1193401160418 /* UIView+AlphaScale.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39488AB33616661725929 /* UIView+AlphaScale.m */; };
|
||||
93D393DB5325820241BA90A7 /* PearlSizedTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39A4759186F6D2D34AA6B /* PearlSizedTextView.h */; };
|
||||
93D3942C1B117EE4851AA7B6 /* UIView+Visible.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3952910EDB8E0EBC94BA9 /* UIView+Visible.m */; };
|
||||
93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */; };
|
||||
93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */; };
|
||||
93D39508A6814612A5B3C226 /* MPMessageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399B36CDB2004D7C51391 /* MPMessageViewController.m */; };
|
||||
@@ -28,6 +30,7 @@
|
||||
93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */; };
|
||||
93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39B381350802A194BF332 /* MPAvatarCell.m */; };
|
||||
93D39577FD8BB0945DB2F0A3 /* MPAlgorithmV3.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39FD9623E8D5571C0AEB3 /* MPAlgorithmV3.m */; };
|
||||
93D3959696396A91961C6148 /* UIView+AlphaScale.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D392D76C091DEA3319F11D /* UIView+AlphaScale.h */; };
|
||||
93D395B715D15F2B56F2A2EE /* mpw-types.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D392C5A6572DB0EB5B82C8 /* mpw-types.c */; };
|
||||
93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; };
|
||||
93D39673DDC085BE72C34D7C /* MPPopdownSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */; };
|
||||
@@ -39,12 +42,11 @@
|
||||
93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */; };
|
||||
93D399246DC90F50913A1287 /* UIResponder+PearlFirstResponder.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A1DDFA09AE2E14D26DC /* UIResponder+PearlFirstResponder.m */; };
|
||||
93D3992FA1546E01F498F665 /* PearlNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */; };
|
||||
93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; };
|
||||
93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D396C311C3725870343EE0 /* mpw-util.c */; };
|
||||
93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D395105935859D71679931 /* MPOverlayViewController.m */; };
|
||||
93D399E4BC1E092A8C8B12AE /* NSOrderedSetOrArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39FBF8FCEB4C106272334 /* NSOrderedSetOrArray.h */; };
|
||||
93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399A8E3181B442D347CD7 /* MPAlgorithmV2.m */; };
|
||||
93D39A53D76CA70786423458 /* UICollectionView+PearlReloadFromArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */; };
|
||||
93D39A53D76CA70786423458 /* UICollectionView+PearlReloadItems.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadItems.h */; };
|
||||
93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */; };
|
||||
93D39A8EA1C49CE43B63F47B /* PearlUICollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D8A953779B35403AF6E /* PearlUICollectionView.m */; };
|
||||
93D39AA4A0BE66A872CCC02E /* NSPersistentStore+PearlMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D397F4BAFFF7CF3F1B21A4 /* NSPersistentStore+PearlMigration.h */; };
|
||||
@@ -53,8 +55,9 @@
|
||||
93D39B842AB9A5D072810D76 /* NSError+PearlFullDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */; };
|
||||
93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */; };
|
||||
93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */; };
|
||||
93D39BFB5F5F9337F6565DE3 /* UIView+Visible.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39B7B765546B1F1900CB7 /* UIView+Visible.h */; };
|
||||
93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; };
|
||||
93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadFromArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */; };
|
||||
93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadItems.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadItems.m */; };
|
||||
93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393310223DDB35218467A /* MPCombinedViewController.m */; };
|
||||
93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39CC01630D0421205C4C4 /* MPNavigationController.m */; };
|
||||
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; };
|
||||
@@ -67,6 +70,36 @@
|
||||
DA071BF4190187FE00179766 /* empty.png in Resources */ = {isa = PBXBuildFile; fileRef = DA071BF2190187FE00179766 /* empty.png */; };
|
||||
DA095E75172F4CD8001C948B /* MPLogsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3979190DACEBD1F6AE9F4 /* MPLogsViewController.m */; };
|
||||
DA0979171E9A81EE00F0BFE8 /* libsodium.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA0979161E9A81EE00F0BFE8 /* libsodium.a */; };
|
||||
DA0CC5291EAB99BA009A8ED9 /* IASKAppSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5071EAB99BA009A8ED9 /* IASKAppSettingsViewController.m */; };
|
||||
DA0CC52A1EAB99BA009A8ED9 /* IASKAppSettingsWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5091EAB99BA009A8ED9 /* IASKAppSettingsWebViewController.m */; };
|
||||
DA0CC52B1EAB99BA009A8ED9 /* IASKMultipleValueSelection.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC50B1EAB99BA009A8ED9 /* IASKMultipleValueSelection.m */; };
|
||||
DA0CC52C1EAB99BA009A8ED9 /* IASKSpecifierValuesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC50D1EAB99BA009A8ED9 /* IASKSpecifierValuesViewController.m */; };
|
||||
DA0CC52D1EAB99BA009A8ED9 /* IASKSettingsReader.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5111EAB99BA009A8ED9 /* IASKSettingsReader.m */; };
|
||||
DA0CC52E1EAB99BA009A8ED9 /* IASKSettingsStore.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5131EAB99BA009A8ED9 /* IASKSettingsStore.m */; };
|
||||
DA0CC52F1EAB99BA009A8ED9 /* IASKSettingsStoreFile.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5151EAB99BA009A8ED9 /* IASKSettingsStoreFile.m */; };
|
||||
DA0CC5301EAB99BA009A8ED9 /* IASKSettingsStoreUserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5171EAB99BA009A8ED9 /* IASKSettingsStoreUserDefaults.m */; };
|
||||
DA0CC5311EAB99BA009A8ED9 /* IASKSpecifier.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5191EAB99BA009A8ED9 /* IASKSpecifier.m */; };
|
||||
DA0CC5321EAB99BA009A8ED9 /* IASKPSSliderSpecifierViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC51C1EAB99BA009A8ED9 /* IASKPSSliderSpecifierViewCell.m */; };
|
||||
DA0CC5331EAB99BA009A8ED9 /* IASKPSTextFieldSpecifierViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC51E1EAB99BA009A8ED9 /* IASKPSTextFieldSpecifierViewCell.m */; };
|
||||
DA0CC5341EAB99BA009A8ED9 /* IASKSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5201EAB99BA009A8ED9 /* IASKSlider.m */; };
|
||||
DA0CC5351EAB99BA009A8ED9 /* IASKSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5221EAB99BA009A8ED9 /* IASKSwitch.m */; };
|
||||
DA0CC5361EAB99BA009A8ED9 /* IASKTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5241EAB99BA009A8ED9 /* IASKTextField.m */; };
|
||||
DA0CC5371EAB99BA009A8ED9 /* IASKTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5261EAB99BA009A8ED9 /* IASKTextView.m */; };
|
||||
DA0CC5381EAB99BA009A8ED9 /* IASKTextViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5281EAB99BA009A8ED9 /* IASKTextViewCell.m */; };
|
||||
DA0CC53B1EB57B5C009A8ED9 /* Fabric.plist in Resources */ = {isa = PBXBuildFile; fileRef = DA0CC53A1EB57B5C009A8ED9 /* Fabric.plist */; };
|
||||
DA0CC5411EB57BD4009A8ED9 /* Fabric.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA0CC53F1EB57B91009A8ED9 /* Fabric.framework */; };
|
||||
DA0CC5421EB57BD4009A8ED9 /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAA141191922FED80032B392 /* Crashlytics.framework */; };
|
||||
DA0CC54E1EB6AD0E009A8ED9 /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5441EB6AD0E009A8ED9 /* MasterPassword.xcdatamodeld */; };
|
||||
DA0CC58C1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5791EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.m */; };
|
||||
DA0CC58D1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC57B1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataProperties.m */; };
|
||||
DA0CC58E1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC57D1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.m */; };
|
||||
DA0CC58F1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC57F1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataProperties.m */; };
|
||||
DA0CC5901EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataClass.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5811EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataClass.m */; };
|
||||
DA0CC5911EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5831EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.m */; };
|
||||
DA0CC5921EB6B030009A8ED9 /* MPUserEntity+CoreDataClass.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5851EB6B030009A8ED9 /* MPUserEntity+CoreDataClass.m */; };
|
||||
DA0CC5931EB6B030009A8ED9 /* MPUserEntity+CoreDataProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5871EB6B030009A8ED9 /* MPUserEntity+CoreDataProperties.m */; };
|
||||
DA0CC5941EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5891EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m */; };
|
||||
DA0CC5951EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC58B1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m */; };
|
||||
DA24EBAE19DAD08900FF010B /* tip_basic_black_top.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38941711E29700CF925C /* tip_basic_black_top.png */; };
|
||||
DA24EBAF19DAD08C00FF010B /* tip_basic_black_top@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38951711E29700CF925C /* tip_basic_black_top@2x.png */; };
|
||||
DA24EBE819DAD6DE00FF010B /* Icon-320.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBE619DAD6DE00FF010B /* Icon-320.png */; };
|
||||
@@ -94,7 +127,6 @@
|
||||
DA29993219C9132F00AF7DF1 /* thumb_generated_login@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA29993119C9132F00AF7DF1 /* thumb_generated_login@3x.png */; };
|
||||
DA29993319C9214600AF7DF1 /* icon_star-hollow.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD382A1711E29600CF925C /* icon_star-hollow.png */; };
|
||||
DA29993419C9214600AF7DF1 /* icon_star-hollow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD382B1711E29600CF925C /* icon_star-hollow@2x.png */; };
|
||||
DA2C3D611BD95EEE001137B3 /* Fabric.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA2C3D601BD95EEE001137B3 /* Fabric.framework */; };
|
||||
DA2C3D631BD96126001137B3 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA2C3D621BD96126001137B3 /* libc++.tbd */; };
|
||||
DA2C3D651BD9612F001137B3 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA2C3D641BD9612F001137B3 /* libz.tbd */; };
|
||||
DA2CA4DD18D28859007798F8 /* NSArray+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4D918D28859007798F8 /* NSArray+Pearl.m */; };
|
||||
@@ -108,10 +140,6 @@
|
||||
DA30E9D415722EF400A68B4C /* Pearl-UIKit.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D315722EF400A68B4C /* Pearl-UIKit.m */; };
|
||||
DA30E9D715723E6900A68B4C /* PearlLazy.h in Headers */ = {isa = PBXBuildFile; fileRef = DA30E9D515723E6900A68B4C /* PearlLazy.h */; };
|
||||
DA30E9D815723E6900A68B4C /* PearlLazy.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D615723E6900A68B4C /* PearlLazy.m */; };
|
||||
DA32CFF019CF1C8F004F3F0E /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFE619CF1C8F004F3F0E /* MPUserEntity.m */; };
|
||||
DA32CFF119CF1C8F004F3F0E /* MPStoredSiteEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFE819CF1C8F004F3F0E /* MPStoredSiteEntity.m */; };
|
||||
DA32CFF319CF1C8F004F3F0E /* MPSiteEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFEC19CF1C8F004F3F0E /* MPSiteEntity.m */; };
|
||||
DA32CFF419CF1C8F004F3F0E /* MPGeneratedSiteEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFEE19CF1C8F004F3F0E /* MPGeneratedSiteEntity.m */; };
|
||||
DA32D00919CF5C55004F3F0E /* icon_question.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37FE1711E29600CF925C /* icon_question.png */; };
|
||||
DA32D00A19CF5C55004F3F0E /* icon_question@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37FF1711E29600CF925C /* icon_question@2x.png */; };
|
||||
DA32D01A19D046E1004F3F0E /* PearlFixedTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32D01819D046E1004F3F0E /* PearlFixedTableView.m */; };
|
||||
@@ -129,7 +157,6 @@
|
||||
DA32D05019D2F59B004F3F0E /* meter_fuel.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D04D19D2F59B004F3F0E /* meter_fuel.png */; };
|
||||
DA32D05119D3D107004F3F0E /* icon_meter.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37BA1711E29600CF925C /* icon_meter.png */; };
|
||||
DA32D05219D3D107004F3F0E /* icon_meter@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37BB1711E29600CF925C /* icon_meter@2x.png */; };
|
||||
DA32D05519D741DC004F3F0E /* MPSiteQuestionEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32D05419D741DC004F3F0E /* MPSiteQuestionEntity.m */; };
|
||||
DA32D07A19D7D784004F3F0E /* background@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D07719D7D784004F3F0E /* background@3x.png */; };
|
||||
DA32D07B19D7D784004F3F0E /* background@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D07819D7D784004F3F0E /* background@2x.png */; };
|
||||
DA32D07C19D7D784004F3F0E /* background.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D07919D7D784004F3F0E /* background.png */; };
|
||||
@@ -145,7 +172,6 @@
|
||||
DA45224A190628A1008F650A /* icon_wrench@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD386B1711E29700CF925C /* icon_wrench@2x.png */; };
|
||||
DA45224B190628B2008F650A /* icon_gear.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37821711E29500CF925C /* icon_gear.png */; };
|
||||
DA45224C190628B2008F650A /* icon_gear@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37831711E29500CF925C /* icon_gear@2x.png */; };
|
||||
DA48856019A5A82E000C2D79 /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAA141191922FED80032B392 /* Crashlytics.framework */; };
|
||||
DA4DA1D91564471A00F6F596 /* libjrswizzle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6326C148680650075AEA5 /* libjrswizzle.a */; };
|
||||
DA4DA1DA1564471F00F6F596 /* libuicolor-utilities.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6325D1486805C0075AEA5 /* libuicolor-utilities.a */; };
|
||||
DA5A09DF171A70E4005284AB /* play.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5A09DD171A70E4005284AB /* play.png */; };
|
||||
@@ -182,7 +208,6 @@
|
||||
DA945C8717E3F3FD0053236B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA945C8617E3F3FD0053236B /* Images.xcassets */; };
|
||||
DA95B50C1C476B6A0067F5EF /* LocalAuthentication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA95B50B1C476B6A0067F5EF /* LocalAuthentication.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||
DA95B50F1C4776F00067F5EF /* NSMutableSet+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA95B50E1C4776F00067F5EF /* NSMutableSet+Pearl.m */; };
|
||||
DA95B5191C477DB50067F5EF /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DA95B5101C477DB50067F5EF /* MasterPassword.xcdatamodeld */; };
|
||||
DA95D5F214DF0B2C008D1B94 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA95D5F014DF0B1E008D1B94 /* MessageUI.framework */; };
|
||||
DAA141201922FF020032B392 /* PearlTween.m in Sources */ = {isa = PBXBuildFile; fileRef = DAA1411C1922FF020032B392 /* PearlTween.m */; };
|
||||
DAA141211922FF020032B392 /* PearlTween.h in Headers */ = {isa = PBXBuildFile; fileRef = DAA1411D1922FF020032B392 /* PearlTween.h */; };
|
||||
@@ -315,7 +340,6 @@
|
||||
DAC77CAE148291A600BCF976 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
|
||||
DAC8DF47192831E100BA7D71 /* icon_key.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD379A1711E29600CF925C /* icon_key.png */; };
|
||||
DAC8DF48192831E100BA7D71 /* icon_key@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD379B1711E29600CF925C /* icon_key@2x.png */; };
|
||||
DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA269A1705DF81002C6C22 /* Crashlytics.plist */; };
|
||||
DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA29711705E1A8002C6C22 /* ciphers.plist */; };
|
||||
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */ = {isa = PBXBuildFile; fileRef = DACA29721705E1A8002C6C22 /* dictionary.lst */; };
|
||||
DACA298D1705E2BD002C6C22 /* JRSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA29771705E2BD002C6C22 /* JRSwizzle.h */; };
|
||||
@@ -344,20 +368,6 @@
|
||||
DAF4EF50190A81E400023C90 /* NSManagedObject+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DAF4EF4E190A81E400023C90 /* NSManagedObject+Pearl.m */; };
|
||||
DAF4EF51190A81E400023C90 /* NSManagedObject+Pearl.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF4EF4F190A81E400023C90 /* NSManagedObject+Pearl.h */; };
|
||||
DAFC5656172C573B00CB5CC5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
|
||||
DAFC5683172C57EC00CB5CC5 /* IASKAppSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5665172C57EC00CB5CC5 /* IASKAppSettingsViewController.m */; };
|
||||
DAFC5684172C57EC00CB5CC5 /* IASKAppSettingsWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5667172C57EC00CB5CC5 /* IASKAppSettingsWebViewController.m */; };
|
||||
DAFC5685172C57EC00CB5CC5 /* IASKSpecifierValuesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5669172C57EC00CB5CC5 /* IASKSpecifierValuesViewController.m */; };
|
||||
DAFC5686172C57EC00CB5CC5 /* IASKSettingsReader.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC566D172C57EC00CB5CC5 /* IASKSettingsReader.m */; };
|
||||
DAFC5687172C57EC00CB5CC5 /* IASKSettingsStore.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC566F172C57EC00CB5CC5 /* IASKSettingsStore.m */; };
|
||||
DAFC5688172C57EC00CB5CC5 /* IASKSettingsStoreFile.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5671172C57EC00CB5CC5 /* IASKSettingsStoreFile.m */; };
|
||||
DAFC5689172C57EC00CB5CC5 /* IASKSettingsStoreUserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5673172C57EC00CB5CC5 /* IASKSettingsStoreUserDefaults.m */; };
|
||||
DAFC568A172C57EC00CB5CC5 /* IASKSpecifier.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5675172C57EC00CB5CC5 /* IASKSpecifier.m */; };
|
||||
DAFC568B172C57EC00CB5CC5 /* IASKPSSliderSpecifierViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5678172C57EC00CB5CC5 /* IASKPSSliderSpecifierViewCell.m */; };
|
||||
DAFC568C172C57EC00CB5CC5 /* IASKPSTextFieldSpecifierViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC567A172C57EC00CB5CC5 /* IASKPSTextFieldSpecifierViewCell.m */; };
|
||||
DAFC568D172C57EC00CB5CC5 /* IASKPSTitleValueSpecifierViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC567C172C57EC00CB5CC5 /* IASKPSTitleValueSpecifierViewCell.m */; };
|
||||
DAFC568E172C57EC00CB5CC5 /* IASKSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC567E172C57EC00CB5CC5 /* IASKSlider.m */; };
|
||||
DAFC568F172C57EC00CB5CC5 /* IASKSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5680172C57EC00CB5CC5 /* IASKSwitch.m */; };
|
||||
DAFC5690172C57EC00CB5CC5 /* IASKTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5682172C57EC00CB5CC5 /* IASKTextField.m */; };
|
||||
DAFE4A1315039824003ABA7C /* NSObject+PearlExport.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE45D815039823003ABA7C /* NSObject+PearlExport.h */; };
|
||||
DAFE4A1415039824003ABA7C /* NSObject+PearlExport.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE45D915039823003ABA7C /* NSObject+PearlExport.m */; };
|
||||
DAFE4A1515039824003ABA7C /* NSString+PearlNSArrayFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE45DA15039823003ABA7C /* NSString+PearlNSArrayFormat.h */; };
|
||||
@@ -460,7 +470,7 @@
|
||||
/* Begin PBXFileReference section */
|
||||
93D390519405B76CC6A57C4F /* MPCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCell.h; sourceTree = "<group>"; };
|
||||
93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = "<group>"; };
|
||||
93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionView+PearlReloadFromArray.m"; sourceTree = "<group>"; };
|
||||
93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadItems.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionView+PearlReloadItems.m"; sourceTree = "<group>"; };
|
||||
93D390A3B351FEF1B9EDAB56 /* mpw-algorithm_v2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v2.c"; sourceTree = "<group>"; };
|
||||
93D390A99850139D0FF0211E /* mpw-algorithm_v0.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v0.c"; sourceTree = "<group>"; };
|
||||
93D390FADEB325D8D54A957D /* PearlOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlOverlay.m; sourceTree = "<group>"; };
|
||||
@@ -471,11 +481,12 @@
|
||||
93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppSettingsViewController.m; sourceTree = "<group>"; };
|
||||
93D391943675426839501BB8 /* MPLogsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPLogsViewController.h; sourceTree = "<group>"; };
|
||||
93D391AA32F24290C424438E /* NSNotificationCenter+PearlEasyCleanup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+PearlEasyCleanup.h"; sourceTree = "<group>"; };
|
||||
93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+PearlReloadFromArray.h"; sourceTree = "<group>"; };
|
||||
93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadItems.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+PearlReloadItems.h"; sourceTree = "<group>"; };
|
||||
93D3924D6F77E6BF41AC32D3 /* MPRootSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPRootSegue.h; sourceTree = "<group>"; };
|
||||
93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordsViewController.m; sourceTree = "<group>"; };
|
||||
93D392876BE5C011DE73B43F /* MPPopdownSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPopdownSegue.h; sourceTree = "<group>"; };
|
||||
93D392C5A6572DB0EB5B82C8 /* mpw-types.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-types.c"; sourceTree = "<group>"; };
|
||||
93D392D76C091DEA3319F11D /* UIView+AlphaScale.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+AlphaScale.h"; sourceTree = "<group>"; };
|
||||
93D393310223DDB35218467A /* MPCombinedViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCombinedViewController.m; sourceTree = "<group>"; };
|
||||
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; };
|
||||
93D393BB973253D4BAAC84AA /* PearlEMail.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlEMail.m; sourceTree = "<group>"; };
|
||||
@@ -483,8 +494,10 @@
|
||||
93D394077F8FAB8167647187 /* Twitter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Twitter.framework; path = System/Library/Frameworks/Twitter.framework; sourceTree = SDKROOT; };
|
||||
93D3942A356B639724157982 /* PearlOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlOverlay.h; sourceTree = "<group>"; };
|
||||
93D394482BB07F90E8FD1314 /* UIResponder+PearlFirstResponder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIResponder+PearlFirstResponder.h"; sourceTree = "<group>"; };
|
||||
93D39488AB33616661725929 /* UIView+AlphaScale.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+AlphaScale.m"; sourceTree = "<group>"; };
|
||||
93D394D73F5BC92297CE8D7B /* MPAlgorithmV3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAlgorithmV3.h; sourceTree = "<group>"; };
|
||||
93D395105935859D71679931 /* MPOverlayViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPOverlayViewController.m; sourceTree = "<group>"; };
|
||||
93D3952910EDB8E0EBC94BA9 /* UIView+Visible.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Visible.m"; sourceTree = "<group>"; };
|
||||
93D3956915634581E737B38C /* PearlNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlNavigationController.m; sourceTree = "<group>"; };
|
||||
93D3957D76F71A652716EECC /* MPStoreViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPStoreViewController.m; sourceTree = "<group>"; };
|
||||
93D3969393A3A46BD27D7078 /* mpw-algorithm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm.c"; sourceTree = "<group>"; };
|
||||
@@ -524,6 +537,7 @@
|
||||
93D39B1D8177A86C5B9EDDE3 /* PearlUICollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlUICollectionView.h; sourceTree = "<group>"; };
|
||||
93D39B381350802A194BF332 /* MPAvatarCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAvatarCell.m; sourceTree = "<group>"; };
|
||||
93D39B455A71EC98C749E623 /* MPOverlayViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPOverlayViewController.h; sourceTree = "<group>"; };
|
||||
93D39B7B765546B1F1900CB7 /* UIView+Visible.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Visible.h"; sourceTree = "<group>"; };
|
||||
93D39BAA71DE51B4D8A1286C /* MPCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCell.m; sourceTree = "<group>"; };
|
||||
93D39C41A27AA42D044D68AE /* NSString+MPMarkDown.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+MPMarkDown.m"; path = "iOS/NSString+MPMarkDown.m"; sourceTree = "<group>"; };
|
||||
93D39C426E03358384018E85 /* MPAnswersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAnswersViewController.m; sourceTree = "<group>"; };
|
||||
@@ -609,6 +623,85 @@
|
||||
DA0979131E9A81EE00F0BFE8 /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = "<group>"; };
|
||||
DA0979141E9A81EE00F0BFE8 /* sodium.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sodium.h; sourceTree = "<group>"; };
|
||||
DA0979161E9A81EE00F0BFE8 /* libsodium.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsodium.a; sourceTree = "<group>"; };
|
||||
DA0CC4F61EAB99BA009A8ED9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC4F71EAB99BA009A8ED9 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC4F81EAB99BA009A8ED9 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC4F91EAB99BA009A8ED9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC4FA1EAB99BA009A8ED9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC4FB1EAB99BA009A8ED9 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC4FC1EAB99BA009A8ED9 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC4FD1EAB99BA009A8ED9 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC4FE1EAB99BA009A8ED9 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC4FF1EAB99BA009A8ED9 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/IASKLocalizable.strings"; sourceTree = "<group>"; };
|
||||
DA0CC5001EAB99BA009A8ED9 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC5011EAB99BA009A8ED9 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC5021EAB99BA009A8ED9 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC5031EAB99BA009A8ED9 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC5041EAB99BA009A8ED9 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC5061EAB99BA009A8ED9 /* IASKAppSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKAppSettingsViewController.h; sourceTree = "<group>"; };
|
||||
DA0CC5071EAB99BA009A8ED9 /* IASKAppSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKAppSettingsViewController.m; sourceTree = "<group>"; };
|
||||
DA0CC5081EAB99BA009A8ED9 /* IASKAppSettingsWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKAppSettingsWebViewController.h; sourceTree = "<group>"; };
|
||||
DA0CC5091EAB99BA009A8ED9 /* IASKAppSettingsWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKAppSettingsWebViewController.m; sourceTree = "<group>"; };
|
||||
DA0CC50A1EAB99BA009A8ED9 /* IASKMultipleValueSelection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKMultipleValueSelection.h; sourceTree = "<group>"; };
|
||||
DA0CC50B1EAB99BA009A8ED9 /* IASKMultipleValueSelection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKMultipleValueSelection.m; sourceTree = "<group>"; };
|
||||
DA0CC50C1EAB99BA009A8ED9 /* IASKSpecifierValuesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSpecifierValuesViewController.h; sourceTree = "<group>"; };
|
||||
DA0CC50D1EAB99BA009A8ED9 /* IASKSpecifierValuesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSpecifierValuesViewController.m; sourceTree = "<group>"; };
|
||||
DA0CC50E1EAB99BA009A8ED9 /* IASKViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKViewController.h; sourceTree = "<group>"; };
|
||||
DA0CC5101EAB99BA009A8ED9 /* IASKSettingsReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSettingsReader.h; sourceTree = "<group>"; };
|
||||
DA0CC5111EAB99BA009A8ED9 /* IASKSettingsReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSettingsReader.m; sourceTree = "<group>"; };
|
||||
DA0CC5121EAB99BA009A8ED9 /* IASKSettingsStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSettingsStore.h; sourceTree = "<group>"; };
|
||||
DA0CC5131EAB99BA009A8ED9 /* IASKSettingsStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSettingsStore.m; sourceTree = "<group>"; };
|
||||
DA0CC5141EAB99BA009A8ED9 /* IASKSettingsStoreFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSettingsStoreFile.h; sourceTree = "<group>"; };
|
||||
DA0CC5151EAB99BA009A8ED9 /* IASKSettingsStoreFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSettingsStoreFile.m; sourceTree = "<group>"; };
|
||||
DA0CC5161EAB99BA009A8ED9 /* IASKSettingsStoreUserDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSettingsStoreUserDefaults.h; sourceTree = "<group>"; };
|
||||
DA0CC5171EAB99BA009A8ED9 /* IASKSettingsStoreUserDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSettingsStoreUserDefaults.m; sourceTree = "<group>"; };
|
||||
DA0CC5181EAB99BA009A8ED9 /* IASKSpecifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSpecifier.h; sourceTree = "<group>"; };
|
||||
DA0CC5191EAB99BA009A8ED9 /* IASKSpecifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSpecifier.m; sourceTree = "<group>"; };
|
||||
DA0CC51B1EAB99BA009A8ED9 /* IASKPSSliderSpecifierViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKPSSliderSpecifierViewCell.h; sourceTree = "<group>"; };
|
||||
DA0CC51C1EAB99BA009A8ED9 /* IASKPSSliderSpecifierViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKPSSliderSpecifierViewCell.m; sourceTree = "<group>"; };
|
||||
DA0CC51D1EAB99BA009A8ED9 /* IASKPSTextFieldSpecifierViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKPSTextFieldSpecifierViewCell.h; sourceTree = "<group>"; };
|
||||
DA0CC51E1EAB99BA009A8ED9 /* IASKPSTextFieldSpecifierViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKPSTextFieldSpecifierViewCell.m; sourceTree = "<group>"; };
|
||||
DA0CC51F1EAB99BA009A8ED9 /* IASKSlider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSlider.h; sourceTree = "<group>"; };
|
||||
DA0CC5201EAB99BA009A8ED9 /* IASKSlider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSlider.m; sourceTree = "<group>"; };
|
||||
DA0CC5211EAB99BA009A8ED9 /* IASKSwitch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSwitch.h; sourceTree = "<group>"; };
|
||||
DA0CC5221EAB99BA009A8ED9 /* IASKSwitch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSwitch.m; sourceTree = "<group>"; };
|
||||
DA0CC5231EAB99BA009A8ED9 /* IASKTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKTextField.h; sourceTree = "<group>"; };
|
||||
DA0CC5241EAB99BA009A8ED9 /* IASKTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKTextField.m; sourceTree = "<group>"; };
|
||||
DA0CC5251EAB99BA009A8ED9 /* IASKTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKTextView.h; sourceTree = "<group>"; };
|
||||
DA0CC5261EAB99BA009A8ED9 /* IASKTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKTextView.m; sourceTree = "<group>"; };
|
||||
DA0CC5271EAB99BA009A8ED9 /* IASKTextViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKTextViewCell.h; sourceTree = "<group>"; };
|
||||
DA0CC5281EAB99BA009A8ED9 /* IASKTextViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKTextViewCell.m; sourceTree = "<group>"; };
|
||||
DA0CC53A1EB57B5C009A8ED9 /* Fabric.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Fabric.plist; sourceTree = "<group>"; };
|
||||
DA0CC53F1EB57B91009A8ED9 /* Fabric.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Fabric.framework; sourceTree = "<group>"; };
|
||||
DA0CC5451EB6AD0E009A8ED9 /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5461EB6AD0E009A8ED9 /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5471EB6AD0E009A8ED9 /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5481EB6AD0E009A8ED9 /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5491EB6AD0E009A8ED9 /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC54A1EB6AD0E009A8ED9 /* MasterPassword 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 6.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC54B1EB6AD0E009A8ED9 /* MasterPassword 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 7.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC54C1EB6AD0E009A8ED9 /* MasterPassword 8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 8.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC54D1EB6AD0E009A8ED9 /* MasterPassword 9.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 9.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5781EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPGeneratedSiteEntity+CoreDataClass.h"; sourceTree = "<group>"; };
|
||||
DA0CC5791EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPGeneratedSiteEntity+CoreDataClass.m"; sourceTree = "<group>"; };
|
||||
DA0CC57A1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPGeneratedSiteEntity+CoreDataProperties.h"; sourceTree = "<group>"; };
|
||||
DA0CC57B1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPGeneratedSiteEntity+CoreDataProperties.m"; sourceTree = "<group>"; };
|
||||
DA0CC57C1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPSiteQuestionEntity+CoreDataClass.h"; sourceTree = "<group>"; };
|
||||
DA0CC57D1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPSiteQuestionEntity+CoreDataClass.m"; sourceTree = "<group>"; };
|
||||
DA0CC57E1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPSiteQuestionEntity+CoreDataProperties.h"; sourceTree = "<group>"; };
|
||||
DA0CC57F1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPSiteQuestionEntity+CoreDataProperties.m"; sourceTree = "<group>"; };
|
||||
DA0CC5801EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPStoredSiteEntity+CoreDataClass.h"; sourceTree = "<group>"; };
|
||||
DA0CC5811EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPStoredSiteEntity+CoreDataClass.m"; sourceTree = "<group>"; };
|
||||
DA0CC5821EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPStoredSiteEntity+CoreDataProperties.h"; sourceTree = "<group>"; };
|
||||
DA0CC5831EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPStoredSiteEntity+CoreDataProperties.m"; sourceTree = "<group>"; };
|
||||
DA0CC5841EB6B030009A8ED9 /* MPUserEntity+CoreDataClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPUserEntity+CoreDataClass.h"; sourceTree = "<group>"; };
|
||||
DA0CC5851EB6B030009A8ED9 /* MPUserEntity+CoreDataClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPUserEntity+CoreDataClass.m"; sourceTree = "<group>"; };
|
||||
DA0CC5861EB6B030009A8ED9 /* MPUserEntity+CoreDataProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPUserEntity+CoreDataProperties.h"; sourceTree = "<group>"; };
|
||||
DA0CC5871EB6B030009A8ED9 /* MPUserEntity+CoreDataProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPUserEntity+CoreDataProperties.m"; sourceTree = "<group>"; };
|
||||
DA0CC5881EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPSiteEntity+CoreDataClass.h"; sourceTree = "<group>"; };
|
||||
DA0CC5891EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPSiteEntity+CoreDataClass.m"; sourceTree = "<group>"; };
|
||||
DA0CC58A1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPSiteEntity+CoreDataProperties.h"; sourceTree = "<group>"; };
|
||||
DA0CC58B1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPSiteEntity+CoreDataProperties.m"; sourceTree = "<group>"; };
|
||||
DA24EBB219DAD4D000FF010B /* Icon-60.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60.png"; sourceTree = "<group>"; };
|
||||
DA24EBB319DAD4D000FF010B /* Icon-60@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60@2x.png"; sourceTree = "<group>"; };
|
||||
DA24EBB419DAD4D000FF010B /* Icon-60@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60@3x.png"; sourceTree = "<group>"; };
|
||||
@@ -657,14 +750,6 @@
|
||||
DA30E9D315722EF400A68B4C /* Pearl-UIKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Pearl-UIKit.m"; sourceTree = "<group>"; };
|
||||
DA30E9D515723E6900A68B4C /* PearlLazy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlLazy.h; sourceTree = "<group>"; };
|
||||
DA30E9D615723E6900A68B4C /* PearlLazy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlLazy.m; sourceTree = "<group>"; };
|
||||
DA32CFE619CF1C8F004F3F0E /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = "<group>"; };
|
||||
DA32CFE719CF1C8F004F3F0E /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = "<group>"; };
|
||||
DA32CFE819CF1C8F004F3F0E /* MPStoredSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPStoredSiteEntity.m; sourceTree = "<group>"; };
|
||||
DA32CFE919CF1C8F004F3F0E /* MPStoredSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPStoredSiteEntity.h; sourceTree = "<group>"; };
|
||||
DA32CFEC19CF1C8F004F3F0E /* MPSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteEntity.m; sourceTree = "<group>"; };
|
||||
DA32CFED19CF1C8F004F3F0E /* MPSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteEntity.h; sourceTree = "<group>"; };
|
||||
DA32CFEE19CF1C8F004F3F0E /* MPGeneratedSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPGeneratedSiteEntity.m; sourceTree = "<group>"; };
|
||||
DA32CFEF19CF1C8F004F3F0E /* MPGeneratedSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPGeneratedSiteEntity.h; sourceTree = "<group>"; };
|
||||
DA32D01819D046E1004F3F0E /* PearlFixedTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlFixedTableView.m; sourceTree = "<group>"; };
|
||||
DA32D01919D046E1004F3F0E /* PearlFixedTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlFixedTableView.h; sourceTree = "<group>"; };
|
||||
DA32D02019D111C6004F3F0E /* libKCOrderedAccessorFix.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libKCOrderedAccessorFix.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -679,8 +764,6 @@
|
||||
DA32D04B19D2F59B004F3F0E /* meter_fuel@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "meter_fuel@3x.png"; sourceTree = "<group>"; };
|
||||
DA32D04C19D2F59B004F3F0E /* meter_fuel@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "meter_fuel@2x.png"; sourceTree = "<group>"; };
|
||||
DA32D04D19D2F59B004F3F0E /* meter_fuel.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = meter_fuel.png; sourceTree = "<group>"; };
|
||||
DA32D05319D741DC004F3F0E /* MPSiteQuestionEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteQuestionEntity.h; sourceTree = "<group>"; };
|
||||
DA32D05419D741DC004F3F0E /* MPSiteQuestionEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteQuestionEntity.m; sourceTree = "<group>"; };
|
||||
DA32D07719D7D784004F3F0E /* background@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "background@3x.png"; path = "ios/launch/background@3x.png"; sourceTree = "<group>"; };
|
||||
DA32D07819D7D784004F3F0E /* background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "background@2x.png"; path = "ios/launch/background@2x.png"; sourceTree = "<group>"; };
|
||||
DA32D07919D7D784004F3F0E /* background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = background.png; path = ios/launch/background.png; sourceTree = "<group>"; };
|
||||
@@ -717,14 +800,6 @@
|
||||
DA95B50B1C476B6A0067F5EF /* LocalAuthentication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LocalAuthentication.framework; path = System/Library/Frameworks/LocalAuthentication.framework; sourceTree = SDKROOT; };
|
||||
DA95B50D1C4776F00067F5EF /* NSMutableSet+Pearl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableSet+Pearl.h"; sourceTree = "<group>"; };
|
||||
DA95B50E1C4776F00067F5EF /* NSMutableSet+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableSet+Pearl.m"; sourceTree = "<group>"; };
|
||||
DA95B5111C477DB50067F5EF /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5121C477DB50067F5EF /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5131C477DB50067F5EF /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5141C477DB50067F5EF /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5151C477DB50067F5EF /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5161C477DB50067F5EF /* MasterPassword 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 6.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5171C477DB50067F5EF /* MasterPassword 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 7.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5181C477DB50067F5EF /* MasterPassword 8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 8.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95D5F014DF0B1E008D1B94 /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; };
|
||||
DAA141191922FED80032B392 /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Crashlytics.framework; sourceTree = "<group>"; };
|
||||
DAA1411C1922FF020032B392 /* PearlTween.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlTween.m; sourceTree = "<group>"; };
|
||||
@@ -1438,7 +1513,6 @@
|
||||
DAC632871486D95D0075AEA5 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
|
||||
DAC77CAD148291A600BCF976 /* libPearl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPearl.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DAC77CB1148291A600BCF976 /* Pearl-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "Pearl-Prefix.pch"; path = "../../Source/Pearl/Pearl-Prefix.pch"; sourceTree = "<group>"; };
|
||||
DACA269A1705DF81002C6C22 /* Crashlytics.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Crashlytics.plist; sourceTree = "<group>"; };
|
||||
DACA29711705E1A8002C6C22 /* ciphers.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ciphers.plist; sourceTree = "<group>"; };
|
||||
DACA29721705E1A8002C6C22 /* dictionary.lst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dictionary.lst; sourceTree = "<group>"; };
|
||||
DACA29771705E2BD002C6C22 /* JRSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JRSwizzle.h; sourceTree = "<group>"; };
|
||||
@@ -1468,35 +1542,6 @@
|
||||
DAF4EF4E190A81E400023C90 /* NSManagedObject+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObject+Pearl.m"; sourceTree = "<group>"; };
|
||||
DAF4EF4F190A81E400023C90 /* NSManagedObject+Pearl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSManagedObject+Pearl.h"; sourceTree = "<group>"; };
|
||||
DAFC5655172C573B00CB5CC5 /* libInAppSettingsKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libInAppSettingsKit.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DAFC5664172C57EC00CB5CC5 /* IASKAppSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKAppSettingsViewController.h; sourceTree = "<group>"; };
|
||||
DAFC5665172C57EC00CB5CC5 /* IASKAppSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKAppSettingsViewController.m; sourceTree = "<group>"; };
|
||||
DAFC5666172C57EC00CB5CC5 /* IASKAppSettingsWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKAppSettingsWebViewController.h; sourceTree = "<group>"; };
|
||||
DAFC5667172C57EC00CB5CC5 /* IASKAppSettingsWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKAppSettingsWebViewController.m; sourceTree = "<group>"; };
|
||||
DAFC5668172C57EC00CB5CC5 /* IASKSpecifierValuesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSpecifierValuesViewController.h; sourceTree = "<group>"; };
|
||||
DAFC5669172C57EC00CB5CC5 /* IASKSpecifierValuesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSpecifierValuesViewController.m; sourceTree = "<group>"; };
|
||||
DAFC566A172C57EC00CB5CC5 /* IASKViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKViewController.h; sourceTree = "<group>"; };
|
||||
DAFC566C172C57EC00CB5CC5 /* IASKSettingsReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSettingsReader.h; sourceTree = "<group>"; };
|
||||
DAFC566D172C57EC00CB5CC5 /* IASKSettingsReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSettingsReader.m; sourceTree = "<group>"; };
|
||||
DAFC566E172C57EC00CB5CC5 /* IASKSettingsStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSettingsStore.h; sourceTree = "<group>"; };
|
||||
DAFC566F172C57EC00CB5CC5 /* IASKSettingsStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSettingsStore.m; sourceTree = "<group>"; };
|
||||
DAFC5670172C57EC00CB5CC5 /* IASKSettingsStoreFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSettingsStoreFile.h; sourceTree = "<group>"; };
|
||||
DAFC5671172C57EC00CB5CC5 /* IASKSettingsStoreFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSettingsStoreFile.m; sourceTree = "<group>"; };
|
||||
DAFC5672172C57EC00CB5CC5 /* IASKSettingsStoreUserDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSettingsStoreUserDefaults.h; sourceTree = "<group>"; };
|
||||
DAFC5673172C57EC00CB5CC5 /* IASKSettingsStoreUserDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSettingsStoreUserDefaults.m; sourceTree = "<group>"; };
|
||||
DAFC5674172C57EC00CB5CC5 /* IASKSpecifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSpecifier.h; sourceTree = "<group>"; };
|
||||
DAFC5675172C57EC00CB5CC5 /* IASKSpecifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSpecifier.m; sourceTree = "<group>"; };
|
||||
DAFC5677172C57EC00CB5CC5 /* IASKPSSliderSpecifierViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKPSSliderSpecifierViewCell.h; sourceTree = "<group>"; };
|
||||
DAFC5678172C57EC00CB5CC5 /* IASKPSSliderSpecifierViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKPSSliderSpecifierViewCell.m; sourceTree = "<group>"; };
|
||||
DAFC5679172C57EC00CB5CC5 /* IASKPSTextFieldSpecifierViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKPSTextFieldSpecifierViewCell.h; sourceTree = "<group>"; };
|
||||
DAFC567A172C57EC00CB5CC5 /* IASKPSTextFieldSpecifierViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKPSTextFieldSpecifierViewCell.m; sourceTree = "<group>"; };
|
||||
DAFC567B172C57EC00CB5CC5 /* IASKPSTitleValueSpecifierViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKPSTitleValueSpecifierViewCell.h; sourceTree = "<group>"; };
|
||||
DAFC567C172C57EC00CB5CC5 /* IASKPSTitleValueSpecifierViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKPSTitleValueSpecifierViewCell.m; sourceTree = "<group>"; };
|
||||
DAFC567D172C57EC00CB5CC5 /* IASKSlider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSlider.h; sourceTree = "<group>"; };
|
||||
DAFC567E172C57EC00CB5CC5 /* IASKSlider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSlider.m; sourceTree = "<group>"; };
|
||||
DAFC567F172C57EC00CB5CC5 /* IASKSwitch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSwitch.h; sourceTree = "<group>"; };
|
||||
DAFC5680172C57EC00CB5CC5 /* IASKSwitch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSwitch.m; sourceTree = "<group>"; };
|
||||
DAFC5681172C57EC00CB5CC5 /* IASKTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKTextField.h; sourceTree = "<group>"; };
|
||||
DAFC5682172C57EC00CB5CC5 /* IASKTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKTextField.m; sourceTree = "<group>"; };
|
||||
DAFE45D815039823003ABA7C /* NSObject+PearlExport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+PearlExport.h"; sourceTree = "<group>"; };
|
||||
DAFE45D915039823003ABA7C /* NSObject+PearlExport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+PearlExport.m"; sourceTree = "<group>"; };
|
||||
DAFE45DA15039823003ABA7C /* NSString+PearlNSArrayFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+PearlNSArrayFormat.h"; sourceTree = "<group>"; };
|
||||
@@ -1594,6 +1639,7 @@
|
||||
DA2C3D651BD9612F001137B3 /* libz.tbd in Frameworks */,
|
||||
DA2C3D631BD96126001137B3 /* libc++.tbd in Frameworks */,
|
||||
DAA1761B19D86D0D0044227B /* libAttributedMarkdown.a in Frameworks */,
|
||||
DA0CC5421EB57BD4009A8ED9 /* Crashlytics.framework in Frameworks */,
|
||||
DA32D03E19D11293004F3F0E /* libKCOrderedAccessorFix.a in Frameworks */,
|
||||
DA04E33E14B1E70400ECA4F3 /* MobileCoreServices.framework in Frameworks */,
|
||||
DAE2725A19C93B8E007C5262 /* StoreKit.framework in Frameworks */,
|
||||
@@ -1605,15 +1651,13 @@
|
||||
DA672D3014F9413D004A189C /* libPearl.a in Frameworks */,
|
||||
DAEBC45314F6364500987BF6 /* QuartzCore.framework in Frameworks */,
|
||||
DA95D5F214DF0B2C008D1B94 /* MessageUI.framework in Frameworks */,
|
||||
DA48856019A5A82E000C2D79 /* Crashlytics.framework in Frameworks */,
|
||||
DAC632891486D9690075AEA5 /* Security.framework in Frameworks */,
|
||||
DA5BFA49147E415C00F98B1E /* UIKit.framework in Frameworks */,
|
||||
DA0979171E9A81EE00F0BFE8 /* libsodium.a in Frameworks */,
|
||||
DA5BFA4B147E415C00F98B1E /* Foundation.framework in Frameworks */,
|
||||
DA5BFA4D147E415C00F98B1E /* CoreGraphics.framework in Frameworks */,
|
||||
DA5BFA4F147E415C00F98B1E /* CoreData.framework in Frameworks */,
|
||||
93D399433EA75E50656040CB /* Twitter.framework in Frameworks */,
|
||||
DA2C3D611BD95EEE001137B3 /* Fabric.framework in Frameworks */,
|
||||
DA0CC5411EB57BD4009A8ED9 /* Fabric.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -1770,6 +1814,76 @@
|
||||
path = lib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA0CC4F41EAB99BA009A8ED9 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA0CC4F51EAB99BA009A8ED9 /* IASKLocalizable.strings */,
|
||||
);
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA0CC5051EAB99BA009A8ED9 /* Controllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA0CC5061EAB99BA009A8ED9 /* IASKAppSettingsViewController.h */,
|
||||
DA0CC5071EAB99BA009A8ED9 /* IASKAppSettingsViewController.m */,
|
||||
DA0CC5081EAB99BA009A8ED9 /* IASKAppSettingsWebViewController.h */,
|
||||
DA0CC5091EAB99BA009A8ED9 /* IASKAppSettingsWebViewController.m */,
|
||||
DA0CC50A1EAB99BA009A8ED9 /* IASKMultipleValueSelection.h */,
|
||||
DA0CC50B1EAB99BA009A8ED9 /* IASKMultipleValueSelection.m */,
|
||||
DA0CC50C1EAB99BA009A8ED9 /* IASKSpecifierValuesViewController.h */,
|
||||
DA0CC50D1EAB99BA009A8ED9 /* IASKSpecifierValuesViewController.m */,
|
||||
DA0CC50E1EAB99BA009A8ED9 /* IASKViewController.h */,
|
||||
);
|
||||
path = Controllers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA0CC50F1EAB99BA009A8ED9 /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA0CC5101EAB99BA009A8ED9 /* IASKSettingsReader.h */,
|
||||
DA0CC5111EAB99BA009A8ED9 /* IASKSettingsReader.m */,
|
||||
DA0CC5121EAB99BA009A8ED9 /* IASKSettingsStore.h */,
|
||||
DA0CC5131EAB99BA009A8ED9 /* IASKSettingsStore.m */,
|
||||
DA0CC5141EAB99BA009A8ED9 /* IASKSettingsStoreFile.h */,
|
||||
DA0CC5151EAB99BA009A8ED9 /* IASKSettingsStoreFile.m */,
|
||||
DA0CC5161EAB99BA009A8ED9 /* IASKSettingsStoreUserDefaults.h */,
|
||||
DA0CC5171EAB99BA009A8ED9 /* IASKSettingsStoreUserDefaults.m */,
|
||||
DA0CC5181EAB99BA009A8ED9 /* IASKSpecifier.h */,
|
||||
DA0CC5191EAB99BA009A8ED9 /* IASKSpecifier.m */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA0CC51A1EAB99BA009A8ED9 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA0CC51B1EAB99BA009A8ED9 /* IASKPSSliderSpecifierViewCell.h */,
|
||||
DA0CC51C1EAB99BA009A8ED9 /* IASKPSSliderSpecifierViewCell.m */,
|
||||
DA0CC51D1EAB99BA009A8ED9 /* IASKPSTextFieldSpecifierViewCell.h */,
|
||||
DA0CC51E1EAB99BA009A8ED9 /* IASKPSTextFieldSpecifierViewCell.m */,
|
||||
DA0CC51F1EAB99BA009A8ED9 /* IASKSlider.h */,
|
||||
DA0CC5201EAB99BA009A8ED9 /* IASKSlider.m */,
|
||||
DA0CC5211EAB99BA009A8ED9 /* IASKSwitch.h */,
|
||||
DA0CC5221EAB99BA009A8ED9 /* IASKSwitch.m */,
|
||||
DA0CC5231EAB99BA009A8ED9 /* IASKTextField.h */,
|
||||
DA0CC5241EAB99BA009A8ED9 /* IASKTextField.m */,
|
||||
DA0CC5251EAB99BA009A8ED9 /* IASKTextView.h */,
|
||||
DA0CC5261EAB99BA009A8ED9 /* IASKTextView.m */,
|
||||
DA0CC5271EAB99BA009A8ED9 /* IASKTextViewCell.h */,
|
||||
DA0CC5281EAB99BA009A8ED9 /* IASKTextViewCell.m */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA0CC5391EB57B5C009A8ED9 /* Fabric */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA0CC53A1EB57B5C009A8ED9 /* Fabric.plist */,
|
||||
);
|
||||
path = Fabric;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA24EBB019DAD4D000FF010B /* ios */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -1931,6 +2045,7 @@
|
||||
DAA141181922FED80032B392 /* iOS */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA0CC53F1EB57B91009A8ED9 /* Fabric.framework */,
|
||||
DAA141191922FED80032B392 /* Crashlytics.framework */,
|
||||
);
|
||||
path = iOS;
|
||||
@@ -2680,7 +2795,7 @@
|
||||
children = (
|
||||
DABD3BD71711E2DC00CF925C /* iOS */,
|
||||
DA771FE41E6E1595004D7EDE /* MasterPassword-Prefix.pch */,
|
||||
DA95B5101C477DB50067F5EF /* MasterPassword.xcdatamodeld */,
|
||||
DA0CC5441EB6AD0E009A8ED9 /* MasterPassword.xcdatamodeld */,
|
||||
DABD3BA01711E2DC00CF925C /* MPAlgorithm.h */,
|
||||
DABD3BA11711E2DC00CF925C /* MPAlgorithm.m */,
|
||||
DABD3BA21711E2DC00CF925C /* MPAlgorithmV0.h */,
|
||||
@@ -2705,20 +2820,30 @@
|
||||
DABD3BB51711E2DC00CF925C /* MPEntities.m */,
|
||||
93D399F244BB522A317811BB /* MPFixable.h */,
|
||||
93D39A813CA9D7E192261ED2 /* MPFixable.m */,
|
||||
DA32CFEF19CF1C8F004F3F0E /* MPGeneratedSiteEntity.h */,
|
||||
DA32CFEE19CF1C8F004F3F0E /* MPGeneratedSiteEntity.m */,
|
||||
DA0CC5781EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.h */,
|
||||
DA0CC5791EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.m */,
|
||||
DA0CC57A1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataProperties.h */,
|
||||
DA0CC57B1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataProperties.m */,
|
||||
DABD3BB61711E2DC00CF925C /* MPKey.h */,
|
||||
DABD3BB71711E2DC00CF925C /* MPKey.m */,
|
||||
DA32CFED19CF1C8F004F3F0E /* MPSiteEntity.h */,
|
||||
DA32CFEC19CF1C8F004F3F0E /* MPSiteEntity.m */,
|
||||
DA32D05319D741DC004F3F0E /* MPSiteQuestionEntity.h */,
|
||||
DA32D05419D741DC004F3F0E /* MPSiteQuestionEntity.m */,
|
||||
DA32CFE919CF1C8F004F3F0E /* MPStoredSiteEntity.h */,
|
||||
DA32CFE819CF1C8F004F3F0E /* MPStoredSiteEntity.m */,
|
||||
DA0CC5881EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.h */,
|
||||
DA0CC5891EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m */,
|
||||
DA0CC58A1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.h */,
|
||||
DA0CC58B1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m */,
|
||||
DA0CC57C1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.h */,
|
||||
DA0CC57D1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.m */,
|
||||
DA0CC57E1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataProperties.h */,
|
||||
DA0CC57F1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataProperties.m */,
|
||||
DA0CC5801EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataClass.h */,
|
||||
DA0CC5811EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataClass.m */,
|
||||
DA0CC5821EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.h */,
|
||||
DA0CC5831EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.m */,
|
||||
DABD3BB81711E2DC00CF925C /* MPTypes.h */,
|
||||
93D39D72239990DDAC2D75B0 /* MPTypes.m */,
|
||||
DA32CFE719CF1C8F004F3F0E /* MPUserEntity.h */,
|
||||
DA32CFE619CF1C8F004F3F0E /* MPUserEntity.m */,
|
||||
DA0CC5841EB6B030009A8ED9 /* MPUserEntity+CoreDataClass.h */,
|
||||
DA0CC5851EB6B030009A8ED9 /* MPUserEntity+CoreDataClass.m */,
|
||||
DA0CC5861EB6B030009A8ED9 /* MPUserEntity+CoreDataProperties.h */,
|
||||
DA0CC5871EB6B030009A8ED9 /* MPUserEntity+CoreDataProperties.m */,
|
||||
93D393CB0B1F4EC8C17CFE43 /* NSString+MPMarkDown.h */,
|
||||
93D39C41A27AA42D044D68AE /* NSString+MPMarkDown.m */,
|
||||
);
|
||||
@@ -2817,7 +2942,7 @@
|
||||
DACA23B41705DF7D002C6C22 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DACA26991705DF81002C6C22 /* Crashlytics */,
|
||||
DA0CC5391EB57B5C009A8ED9 /* Fabric */,
|
||||
DACA29701705E1A8002C6C22 /* Data */,
|
||||
DAE1EF2417E942DE00BC0086 /* Localizable.strings */,
|
||||
DABD360D1711E29400CF925C /* Media */,
|
||||
@@ -2825,14 +2950,6 @@
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DACA26991705DF81002C6C22 /* Crashlytics */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DACA269A1705DF81002C6C22 /* Crashlytics.plist */,
|
||||
);
|
||||
path = Crashlytics;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DACA29701705E1A8002C6C22 /* Data */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -2865,64 +2982,15 @@
|
||||
DAFC5662172C57EC00CB5CC5 /* InAppSettingsKit */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DAFC5663172C57EC00CB5CC5 /* Controllers */,
|
||||
DAFC566B172C57EC00CB5CC5 /* Models */,
|
||||
DAFC5676172C57EC00CB5CC5 /* Views */,
|
||||
DA0CC4F41EAB99BA009A8ED9 /* Resources */,
|
||||
DA0CC5051EAB99BA009A8ED9 /* Controllers */,
|
||||
DA0CC50F1EAB99BA009A8ED9 /* Models */,
|
||||
DA0CC51A1EAB99BA009A8ED9 /* Views */,
|
||||
);
|
||||
name = InAppSettingsKit;
|
||||
path = InAppSettingsKit/InAppSettingsKit;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DAFC5663172C57EC00CB5CC5 /* Controllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DAFC5664172C57EC00CB5CC5 /* IASKAppSettingsViewController.h */,
|
||||
DAFC5665172C57EC00CB5CC5 /* IASKAppSettingsViewController.m */,
|
||||
DAFC5666172C57EC00CB5CC5 /* IASKAppSettingsWebViewController.h */,
|
||||
DAFC5667172C57EC00CB5CC5 /* IASKAppSettingsWebViewController.m */,
|
||||
DAFC5668172C57EC00CB5CC5 /* IASKSpecifierValuesViewController.h */,
|
||||
DAFC5669172C57EC00CB5CC5 /* IASKSpecifierValuesViewController.m */,
|
||||
DAFC566A172C57EC00CB5CC5 /* IASKViewController.h */,
|
||||
);
|
||||
path = Controllers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DAFC566B172C57EC00CB5CC5 /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DAFC566C172C57EC00CB5CC5 /* IASKSettingsReader.h */,
|
||||
DAFC566D172C57EC00CB5CC5 /* IASKSettingsReader.m */,
|
||||
DAFC566E172C57EC00CB5CC5 /* IASKSettingsStore.h */,
|
||||
DAFC566F172C57EC00CB5CC5 /* IASKSettingsStore.m */,
|
||||
DAFC5670172C57EC00CB5CC5 /* IASKSettingsStoreFile.h */,
|
||||
DAFC5671172C57EC00CB5CC5 /* IASKSettingsStoreFile.m */,
|
||||
DAFC5672172C57EC00CB5CC5 /* IASKSettingsStoreUserDefaults.h */,
|
||||
DAFC5673172C57EC00CB5CC5 /* IASKSettingsStoreUserDefaults.m */,
|
||||
DAFC5674172C57EC00CB5CC5 /* IASKSpecifier.h */,
|
||||
DAFC5675172C57EC00CB5CC5 /* IASKSpecifier.m */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DAFC5676172C57EC00CB5CC5 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DAFC5677172C57EC00CB5CC5 /* IASKPSSliderSpecifierViewCell.h */,
|
||||
DAFC5678172C57EC00CB5CC5 /* IASKPSSliderSpecifierViewCell.m */,
|
||||
DAFC5679172C57EC00CB5CC5 /* IASKPSTextFieldSpecifierViewCell.h */,
|
||||
DAFC567A172C57EC00CB5CC5 /* IASKPSTextFieldSpecifierViewCell.m */,
|
||||
DAFC567B172C57EC00CB5CC5 /* IASKPSTitleValueSpecifierViewCell.h */,
|
||||
DAFC567C172C57EC00CB5CC5 /* IASKPSTitleValueSpecifierViewCell.m */,
|
||||
DAFC567D172C57EC00CB5CC5 /* IASKSlider.h */,
|
||||
DAFC567E172C57EC00CB5CC5 /* IASKSlider.m */,
|
||||
DAFC567F172C57EC00CB5CC5 /* IASKSwitch.h */,
|
||||
DAFC5680172C57EC00CB5CC5 /* IASKSwitch.m */,
|
||||
DAFC5681172C57EC00CB5CC5 /* IASKTextField.h */,
|
||||
DAFC5682172C57EC00CB5CC5 /* IASKTextField.m */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DAFE45D715039823003ABA7C /* Pearl */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -3072,8 +3140,8 @@
|
||||
DAFE462515039823003ABA7C /* Resources */,
|
||||
DA250A16195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h */,
|
||||
DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */,
|
||||
93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */,
|
||||
93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */,
|
||||
93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadItems.h */,
|
||||
93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadItems.m */,
|
||||
DAFE4A63150399FF003ABA83 /* UIControl+PearlBlocks.h */,
|
||||
DAFE4A63150399FF003ABA81 /* UIControl+PearlBlocks.m */,
|
||||
DAFE4A63150399FF003ABA8B /* UIControl+PearlSelect.h */,
|
||||
@@ -3098,6 +3166,10 @@
|
||||
DAEFB01C19BCBD9E00525079 /* UIView+LayoutGone.m */,
|
||||
DAEC85B418E3DD9A007FC0DF /* UIView+Touches.h */,
|
||||
DAEC85B118E3DD9A007FC0DF /* UIView+Touches.m */,
|
||||
93D39488AB33616661725929 /* UIView+AlphaScale.m */,
|
||||
93D392D76C091DEA3319F11D /* UIView+AlphaScale.h */,
|
||||
93D3952910EDB8E0EBC94BA9 /* UIView+Visible.m */,
|
||||
93D39B7B765546B1F1900CB7 /* UIView+Visible.h */,
|
||||
);
|
||||
path = "Pearl-UIKit";
|
||||
sourceTree = "<group>";
|
||||
@@ -3203,9 +3275,11 @@
|
||||
93D39536EB550E811CCD04BC /* UIResponder+PearlFirstResponder.h in Headers */,
|
||||
93D393DB5325820241BA90A7 /* PearlSizedTextView.h in Headers */,
|
||||
93D392A8777DC30C11361647 /* UITextView+PearlAttributes.h in Headers */,
|
||||
93D39A53D76CA70786423458 /* UICollectionView+PearlReloadFromArray.h in Headers */,
|
||||
93D39A53D76CA70786423458 /* UICollectionView+PearlReloadItems.h in Headers */,
|
||||
93D39AA4A0BE66A872CCC02E /* NSPersistentStore+PearlMigration.h in Headers */,
|
||||
93D399E4BC1E092A8C8B12AE /* NSOrderedSetOrArray.h in Headers */,
|
||||
93D3959696396A91961C6148 /* UIView+AlphaScale.h in Headers */,
|
||||
93D39BFB5F5F9337F6565DE3 /* UIView+Visible.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -3239,7 +3313,7 @@
|
||||
DA5BFA41147E415C00F98B1E /* Frameworks */,
|
||||
DA5BFA42147E415C00F98B1E /* Resources */,
|
||||
DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */,
|
||||
DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */,
|
||||
DAD3125D155288AA00A3F9ED /* Run Script: Fabric */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -3475,6 +3549,8 @@
|
||||
zh_HK,
|
||||
zh_TW,
|
||||
zu,
|
||||
Base,
|
||||
"pt-PT",
|
||||
);
|
||||
mainGroup = DA5BFA39147E415C00F98B1E;
|
||||
productRefGroup = DA5BFA45147E415C00F98B1E /* Products */;
|
||||
@@ -3498,7 +3574,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DAFE4A5A1503982E003ABA7C /* Pearl.strings in Resources */,
|
||||
DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */,
|
||||
DA0CC53B1EB57B5C009A8ED9 /* Fabric.plist in Resources */,
|
||||
DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */,
|
||||
DA32D04F19D2F59B004F3F0E /* meter_fuel@2x.png in Resources */,
|
||||
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */,
|
||||
@@ -3728,19 +3804,19 @@
|
||||
shellScript = "exec Scripts/genassets";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */ = {
|
||||
DAD3125D155288AA00A3F9ED /* Run Script: Fabric */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Run Script: Crashlytics";
|
||||
name = "Run Script: Fabric";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = "/bin/bash -e";
|
||||
shellScript = "[[ $DEPLOYMENT_LOCATION != YES ]] && exit\n\napiKey=$(/usr/libexec/PlistBuddy -c \"Print :'API Key'\" Resources/Crashlytics/Crashlytics.plist)\n[[ $apiKey ]] && External/iOS/Crashlytics.framework/run \"$apiKey\"";
|
||||
shellScript = "[[ $DEPLOYMENT_LOCATION != YES ]] && exit\n\napiKey=$(/usr/libexec/PlistBuddy -c \"Print :'API Key'\" Resources/Fabric/Fabric.plist)\n[[ $apiKey ]] && External/iOS/Fabric.framework/run \"$apiKey\"";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
@@ -3758,7 +3834,9 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DA0CC5921EB6B030009A8ED9 /* MPUserEntity+CoreDataClass.m in Sources */,
|
||||
DABD3BFD1711E2DC00CF925C /* MPAlgorithm.m in Sources */,
|
||||
DA0CC5951EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m in Sources */,
|
||||
DABD3BFE1711E2DC00CF925C /* MPAlgorithmV0.m in Sources */,
|
||||
DABD3BFF1711E2DC00CF925C /* MPAlgorithmV1.m in Sources */,
|
||||
DABD3C001711E2DC00CF925C /* MPAppDelegate_Key.m in Sources */,
|
||||
@@ -3766,38 +3844,40 @@
|
||||
DABD3C021711E2DC00CF925C /* MPAppDelegate_Store.m in Sources */,
|
||||
DABD3C031711E2DC00CF925C /* MPConfig.m in Sources */,
|
||||
DABD3C071711E2DC00CF925C /* MPEntities.m in Sources */,
|
||||
DA32CFF319CF1C8F004F3F0E /* MPSiteEntity.m in Sources */,
|
||||
DABD3C081711E2DC00CF925C /* MPKey.m in Sources */,
|
||||
DABD3C151711E2DC00CF925C /* MPiOSAppDelegate.m in Sources */,
|
||||
DA0CC5941EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m in Sources */,
|
||||
DABD3C1C1711E2DC00CF925C /* MPGuideViewController.m in Sources */,
|
||||
DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */,
|
||||
DABD3C1F1711E2DC00CF925C /* MPTypeViewController.m in Sources */,
|
||||
DABD3C211711E2DC00CF925C /* MPiOSConfig.m in Sources */,
|
||||
DA0CC5901EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataClass.m in Sources */,
|
||||
DA0CC58D1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataProperties.m in Sources */,
|
||||
DABD3C271711E2DC00CF925C /* main.m in Sources */,
|
||||
DA0CC5931EB6B030009A8ED9 /* MPUserEntity+CoreDataProperties.m in Sources */,
|
||||
93D39F8A9254177891F38705 /* MPSetupViewController.m in Sources */,
|
||||
DA095E75172F4CD8001C948B /* MPLogsViewController.m in Sources */,
|
||||
93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */,
|
||||
DA32CFF419CF1C8F004F3F0E /* MPGeneratedSiteEntity.m in Sources */,
|
||||
93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */,
|
||||
93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */,
|
||||
DA32D05519D741DC004F3F0E /* MPSiteQuestionEntity.m in Sources */,
|
||||
93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */,
|
||||
93D39392DEDA376F93C6C718 /* MPCell.m in Sources */,
|
||||
DA0CC58E1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.m in Sources */,
|
||||
93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */,
|
||||
93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */,
|
||||
DA95B50F1C4776F00067F5EF /* NSMutableSet+Pearl.m in Sources */,
|
||||
93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */,
|
||||
DA0CC5911EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.m in Sources */,
|
||||
93D39673DDC085BE72C34D7C /* MPPopdownSegue.m in Sources */,
|
||||
93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */,
|
||||
DA0CC58F1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataProperties.m in Sources */,
|
||||
93D396D8B67DA6522CDBA142 /* MPCoachmarkViewController.m in Sources */,
|
||||
DAADBFE01A68763B00F7A756 /* mpw-algorithm.c in Sources */,
|
||||
DA95B5191C477DB50067F5EF /* MasterPassword.xcdatamodeld in Sources */,
|
||||
DA0CC54E1EB6AD0E009A8ED9 /* MasterPassword.xcdatamodeld in Sources */,
|
||||
93D39EAA4D064193074D3021 /* MPFixable.m in Sources */,
|
||||
DA32CFF119CF1C8F004F3F0E /* MPStoredSiteEntity.m in Sources */,
|
||||
93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */,
|
||||
93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */,
|
||||
93D3939661CE37180AF7CD6A /* MPStoreViewController.m in Sources */,
|
||||
DA32CFF019CF1C8F004F3F0E /* MPUserEntity.m in Sources */,
|
||||
93D390C1B93F9D3AE37DD0A5 /* MPAnswersViewController.m in Sources */,
|
||||
93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */,
|
||||
93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */,
|
||||
@@ -3807,6 +3887,7 @@
|
||||
93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */,
|
||||
93D39577FD8BB0945DB2F0A3 /* MPAlgorithmV3.m in Sources */,
|
||||
93D39E5F7F6D7F5C0FAD090F /* MPTypes.m in Sources */,
|
||||
DA0CC58C1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.m in Sources */,
|
||||
DA92614E1BE1A57500369DE5 /* MPAppDelegate_InApp.m in Sources */,
|
||||
93D39508A6814612A5B3C226 /* MPMessageViewController.m in Sources */,
|
||||
);
|
||||
@@ -3909,9 +3990,11 @@
|
||||
DAA141201922FF020032B392 /* PearlTween.m in Sources */,
|
||||
93D391ECBD9BD2C64115B5DD /* PearlSizedTextView.m in Sources */,
|
||||
93D39E34FD28D24FE3442C48 /* UITextView+PearlAttributes.m in Sources */,
|
||||
93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadFromArray.m in Sources */,
|
||||
93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadItems.m in Sources */,
|
||||
93D3928D629EA563F9EC4909 /* NSPersistentStore+PearlMigration.m in Sources */,
|
||||
93D392A33CCE85431E910C7B /* NSOrderedSetOrArray.m in Sources */,
|
||||
93D393AA69A1193401160418 /* UIView+AlphaScale.m in Sources */,
|
||||
93D3942C1B117EE4851AA7B6 /* UIView+Visible.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -3919,26 +4002,50 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DAFC5683172C57EC00CB5CC5 /* IASKAppSettingsViewController.m in Sources */,
|
||||
DAFC5684172C57EC00CB5CC5 /* IASKAppSettingsWebViewController.m in Sources */,
|
||||
DAFC5685172C57EC00CB5CC5 /* IASKSpecifierValuesViewController.m in Sources */,
|
||||
DAFC5686172C57EC00CB5CC5 /* IASKSettingsReader.m in Sources */,
|
||||
DAFC5687172C57EC00CB5CC5 /* IASKSettingsStore.m in Sources */,
|
||||
DAFC5688172C57EC00CB5CC5 /* IASKSettingsStoreFile.m in Sources */,
|
||||
DAFC5689172C57EC00CB5CC5 /* IASKSettingsStoreUserDefaults.m in Sources */,
|
||||
DAFC568A172C57EC00CB5CC5 /* IASKSpecifier.m in Sources */,
|
||||
DAFC568B172C57EC00CB5CC5 /* IASKPSSliderSpecifierViewCell.m in Sources */,
|
||||
DAFC568C172C57EC00CB5CC5 /* IASKPSTextFieldSpecifierViewCell.m in Sources */,
|
||||
DAFC568D172C57EC00CB5CC5 /* IASKPSTitleValueSpecifierViewCell.m in Sources */,
|
||||
DAFC568E172C57EC00CB5CC5 /* IASKSlider.m in Sources */,
|
||||
DAFC568F172C57EC00CB5CC5 /* IASKSwitch.m in Sources */,
|
||||
DAFC5690172C57EC00CB5CC5 /* IASKTextField.m in Sources */,
|
||||
DA0CC52B1EAB99BA009A8ED9 /* IASKMultipleValueSelection.m in Sources */,
|
||||
DA0CC5321EAB99BA009A8ED9 /* IASKPSSliderSpecifierViewCell.m in Sources */,
|
||||
DA0CC52D1EAB99BA009A8ED9 /* IASKSettingsReader.m in Sources */,
|
||||
DA0CC52A1EAB99BA009A8ED9 /* IASKAppSettingsWebViewController.m in Sources */,
|
||||
DA0CC5291EAB99BA009A8ED9 /* IASKAppSettingsViewController.m in Sources */,
|
||||
DA0CC5381EAB99BA009A8ED9 /* IASKTextViewCell.m in Sources */,
|
||||
DA0CC5311EAB99BA009A8ED9 /* IASKSpecifier.m in Sources */,
|
||||
DA0CC5341EAB99BA009A8ED9 /* IASKSlider.m in Sources */,
|
||||
DA0CC5351EAB99BA009A8ED9 /* IASKSwitch.m in Sources */,
|
||||
DA0CC5331EAB99BA009A8ED9 /* IASKPSTextFieldSpecifierViewCell.m in Sources */,
|
||||
DA0CC52E1EAB99BA009A8ED9 /* IASKSettingsStore.m in Sources */,
|
||||
DA0CC52C1EAB99BA009A8ED9 /* IASKSpecifierValuesViewController.m in Sources */,
|
||||
DA0CC5361EAB99BA009A8ED9 /* IASKTextField.m in Sources */,
|
||||
DA0CC5371EAB99BA009A8ED9 /* IASKTextView.m in Sources */,
|
||||
DA0CC52F1EAB99BA009A8ED9 /* IASKSettingsStoreFile.m in Sources */,
|
||||
DA0CC5301EAB99BA009A8ED9 /* IASKSettingsStoreUserDefaults.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
DA0CC4F51EAB99BA009A8ED9 /* IASKLocalizable.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
DA0CC4F61EAB99BA009A8ED9 /* Base */,
|
||||
DA0CC4F71EAB99BA009A8ED9 /* de */,
|
||||
DA0CC4F81EAB99BA009A8ED9 /* el */,
|
||||
DA0CC4F91EAB99BA009A8ED9 /* en */,
|
||||
DA0CC4FA1EAB99BA009A8ED9 /* es */,
|
||||
DA0CC4FB1EAB99BA009A8ED9 /* fr */,
|
||||
DA0CC4FC1EAB99BA009A8ED9 /* it */,
|
||||
DA0CC4FD1EAB99BA009A8ED9 /* ja */,
|
||||
DA0CC4FE1EAB99BA009A8ED9 /* nl */,
|
||||
DA0CC4FF1EAB99BA009A8ED9 /* pt-PT */,
|
||||
DA0CC5001EAB99BA009A8ED9 /* pt */,
|
||||
DA0CC5011EAB99BA009A8ED9 /* ru */,
|
||||
DA0CC5021EAB99BA009A8ED9 /* sv */,
|
||||
DA0CC5031EAB99BA009A8ED9 /* th */,
|
||||
DA0CC5041EAB99BA009A8ED9 /* tr */,
|
||||
);
|
||||
name = IASKLocalizable.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DABD3BFA1711E2DC00CF925C /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
@@ -4005,7 +4112,6 @@
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"ADHOC=1",
|
||||
"$(inherited)",
|
||||
"NDEBUG=1",
|
||||
"NS_BLOCK_ASSERTIONS=1",
|
||||
@@ -4062,6 +4168,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = Source/iOS/MasterPassword.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
@@ -4071,8 +4178,9 @@
|
||||
"\"$(SRCROOT)/External/iOS\"",
|
||||
);
|
||||
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
|
||||
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
|
||||
INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib",
|
||||
@@ -4188,6 +4296,7 @@
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
"CRASHLYTICS=1",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
|
||||
@@ -4273,7 +4382,6 @@
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"ADHOC=1",
|
||||
"$(inherited)",
|
||||
"NDEBUG=1",
|
||||
"NS_BLOCK_ASSERTIONS=1",
|
||||
@@ -4330,6 +4438,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = Source/iOS/MasterPassword.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
@@ -4337,8 +4446,9 @@
|
||||
"\"$(SRCROOT)/External/iOS\"",
|
||||
);
|
||||
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
|
||||
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
|
||||
INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib",
|
||||
@@ -4356,6 +4466,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = Source/iOS/MasterPassword.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
@@ -4365,8 +4476,9 @@
|
||||
"\"$(SRCROOT)/External/iOS\"",
|
||||
);
|
||||
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
|
||||
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
|
||||
INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib",
|
||||
@@ -4553,19 +4665,20 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCVersionGroup section */
|
||||
DA95B5101C477DB50067F5EF /* MasterPassword.xcdatamodeld */ = {
|
||||
DA0CC5441EB6AD0E009A8ED9 /* MasterPassword.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DA95B5111C477DB50067F5EF /* MasterPassword 1.xcdatamodel */,
|
||||
DA95B5121C477DB50067F5EF /* MasterPassword 2.xcdatamodel */,
|
||||
DA95B5131C477DB50067F5EF /* MasterPassword 3.xcdatamodel */,
|
||||
DA95B5141C477DB50067F5EF /* MasterPassword 4.xcdatamodel */,
|
||||
DA95B5151C477DB50067F5EF /* MasterPassword 5.xcdatamodel */,
|
||||
DA95B5161C477DB50067F5EF /* MasterPassword 6.xcdatamodel */,
|
||||
DA95B5171C477DB50067F5EF /* MasterPassword 7.xcdatamodel */,
|
||||
DA95B5181C477DB50067F5EF /* MasterPassword 8.xcdatamodel */,
|
||||
DA0CC5451EB6AD0E009A8ED9 /* MasterPassword 1.xcdatamodel */,
|
||||
DA0CC5461EB6AD0E009A8ED9 /* MasterPassword 2.xcdatamodel */,
|
||||
DA0CC5471EB6AD0E009A8ED9 /* MasterPassword 3.xcdatamodel */,
|
||||
DA0CC5481EB6AD0E009A8ED9 /* MasterPassword 4.xcdatamodel */,
|
||||
DA0CC5491EB6AD0E009A8ED9 /* MasterPassword 5.xcdatamodel */,
|
||||
DA0CC54A1EB6AD0E009A8ED9 /* MasterPassword 6.xcdatamodel */,
|
||||
DA0CC54B1EB6AD0E009A8ED9 /* MasterPassword 7.xcdatamodel */,
|
||||
DA0CC54C1EB6AD0E009A8ED9 /* MasterPassword 8.xcdatamodel */,
|
||||
DA0CC54D1EB6AD0E009A8ED9 /* MasterPassword 9.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DA95B5181C477DB50067F5EF /* MasterPassword 8.xcdatamodel */;
|
||||
currentVersion = DA0CC54D1EB6AD0E009A8ED9 /* MasterPassword 9.xcdatamodel */;
|
||||
path = MasterPassword.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
DA09745B1E99582900F0BFE8 /* mpw-tests.c in Sources */ = {isa = PBXBuildFile; fileRef = DA0974571E99582200F0BFE8 /* mpw-tests.c */; };
|
||||
DA09745E1E99586600F0BFE8 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA09745D1E99586600F0BFE8 /* libxml2.tbd */; };
|
||||
DA0979681E9A834C00F0BFE8 /* libsodium.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA0979571E9A824700F0BFE8 /* libsodium.a */; };
|
||||
DA0CC53E1EB57B69009A8ED9 /* Fabric.plist in Resources */ = {isa = PBXBuildFile; fileRef = DA0CC53D1EB57B69009A8ED9 /* Fabric.plist */; };
|
||||
DA0CC5591EB6AE45009A8ED9 /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC54F1EB6AE45009A8ED9 /* MasterPassword.xcdatamodeld */; };
|
||||
DA1000801998A4C6002B873F /* openssl in Headers */ = {isa = PBXBuildFile; fileRef = DAE8E65719867AF500416A0F /* openssl */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
DA16B341170661DB000A0EAB /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA16B340170661DB000A0EAB /* Carbon.framework */; };
|
||||
DA16B342170661E0000A0EAB /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC632871486D95D0075AEA5 /* Security.framework */; };
|
||||
@@ -96,7 +98,6 @@
|
||||
DA9261521BE1A86700369DE5 /* Fabric.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9261501BE1A86700369DE5 /* Fabric.framework */; };
|
||||
DA9261541BE1A88900369DE5 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9261531BE1A88900369DE5 /* libc++.tbd */; };
|
||||
DA9261561BE1A89600369DE5 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9261551BE1A89600369DE5 /* libz.tbd */; };
|
||||
DA95B5231C477DE10067F5EF /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DA95B51A1C477DE10067F5EF /* MasterPassword.xcdatamodeld */; };
|
||||
DAAA81B0195A8D1300FA30D9 /* gradient.png in Resources */ = {isa = PBXBuildFile; fileRef = DAAA81AF195A8D1300FA30D9 /* gradient.png */; };
|
||||
DAADCC4719FAFFAD00987B1D /* NSNotificationCenter+PearlEasyCleanup.h in Headers */ = {isa = PBXBuildFile; fileRef = DAADCC3E19FAFFAD00987B1D /* NSNotificationCenter+PearlEasyCleanup.h */; };
|
||||
DAADCC4819FAFFAD00987B1D /* NSPersistentStore+PearlMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = DAADCC3F19FAFFAD00987B1D /* NSPersistentStore+PearlMigration.h */; };
|
||||
@@ -148,7 +149,6 @@
|
||||
DACA27381705DF81002C6C22 /* menu-icon.png in Resources */ = {isa = PBXBuildFile; fileRef = DACA24581705DF7D002C6C22 /* menu-icon.png */; };
|
||||
DACA29671705DF81002C6C22 /* SourceCodePro-ExtraLight.otf in Resources */ = {isa = PBXBuildFile; fileRef = DACA268E1705DF81002C6C22 /* SourceCodePro-ExtraLight.otf */; };
|
||||
DACA29681705DF81002C6C22 /* SourceCodePro-Black.otf in Resources */ = {isa = PBXBuildFile; fileRef = DACA268F1705DF81002C6C22 /* SourceCodePro-Black.otf */; };
|
||||
DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA269A1705DF81002C6C22 /* Crashlytics.plist */; };
|
||||
DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA29711705E1A8002C6C22 /* ciphers.plist */; };
|
||||
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */ = {isa = PBXBuildFile; fileRef = DACA29721705E1A8002C6C22 /* dictionary.lst */; };
|
||||
DACA298D1705E2BD002C6C22 /* JRSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA29771705E2BD002C6C22 /* JRSwizzle.h */; };
|
||||
@@ -350,6 +350,16 @@
|
||||
DA0979531E9A824700F0BFE8 /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = "<group>"; };
|
||||
DA0979541E9A824700F0BFE8 /* sodium.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sodium.h; sourceTree = "<group>"; };
|
||||
DA0979571E9A824700F0BFE8 /* libsodium.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsodium.a; sourceTree = "<group>"; };
|
||||
DA0CC53D1EB57B69009A8ED9 /* Fabric.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Fabric.plist; sourceTree = "<group>"; };
|
||||
DA0CC5501EB6AE45009A8ED9 /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5511EB6AE45009A8ED9 /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5521EB6AE45009A8ED9 /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5531EB6AE45009A8ED9 /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5541EB6AE45009A8ED9 /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5551EB6AE45009A8ED9 /* MasterPassword 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 6.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5561EB6AE45009A8ED9 /* MasterPassword 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 7.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5571EB6AE45009A8ED9 /* MasterPassword 8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 8.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5581EB6AE45009A8ED9 /* MasterPassword 9.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 9.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA16B340170661DB000A0EAB /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
|
||||
DA16B343170661EE000A0EAB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||
DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPPasswordWindowController.xib; sourceTree = "<group>"; };
|
||||
@@ -849,14 +859,6 @@
|
||||
DA9261501BE1A86700369DE5 /* Fabric.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Fabric.framework; sourceTree = "<group>"; };
|
||||
DA9261531BE1A88900369DE5 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
|
||||
DA9261551BE1A89600369DE5 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
|
||||
DA95B51B1C477DE10067F5EF /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B51C1C477DE10067F5EF /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B51D1C477DE10067F5EF /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B51E1C477DE10067F5EF /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B51F1C477DE10067F5EF /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5201C477DE10067F5EF /* MasterPassword 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 6.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5211C477DE10067F5EF /* MasterPassword 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 7.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5221C477DE10067F5EF /* MasterPassword 8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 8.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DAAA81AF195A8D1300FA30D9 /* gradient.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = gradient.png; sourceTree = "<group>"; };
|
||||
DAADCC3E19FAFFAD00987B1D /* NSNotificationCenter+PearlEasyCleanup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+PearlEasyCleanup.h"; sourceTree = "<group>"; };
|
||||
DAADCC3F19FAFFAD00987B1D /* NSPersistentStore+PearlMigration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSPersistentStore+PearlMigration.h"; sourceTree = "<group>"; };
|
||||
@@ -911,7 +913,6 @@
|
||||
DACA24581705DF7D002C6C22 /* menu-icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu-icon.png"; sourceTree = "<group>"; };
|
||||
DACA268E1705DF81002C6C22 /* SourceCodePro-ExtraLight.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-ExtraLight.otf"; sourceTree = "<group>"; };
|
||||
DACA268F1705DF81002C6C22 /* SourceCodePro-Black.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-Black.otf"; sourceTree = "<group>"; };
|
||||
DACA269A1705DF81002C6C22 /* Crashlytics.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Crashlytics.plist; sourceTree = "<group>"; };
|
||||
DACA29711705E1A8002C6C22 /* ciphers.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ciphers.plist; sourceTree = "<group>"; };
|
||||
DACA29721705E1A8002C6C22 /* dictionary.lst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dictionary.lst; sourceTree = "<group>"; };
|
||||
DACA29771705E2BD002C6C22 /* JRSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JRSwizzle.h; sourceTree = "<group>"; };
|
||||
@@ -1132,6 +1133,14 @@
|
||||
path = lib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA0CC53C1EB57B69009A8ED9 /* Fabric */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA0CC53D1EB57B69009A8ED9 /* Fabric.plist */,
|
||||
);
|
||||
path = Fabric;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA2508F819513C1400AC23F1 /* Other Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -1212,7 +1221,7 @@
|
||||
children = (
|
||||
DA5E5CB21724A667003798D8 /* Mac */,
|
||||
DA771FE51E6E15A1004D7EDE /* MasterPassword-Prefix.pch */,
|
||||
DA95B51A1C477DE10067F5EF /* MasterPassword.xcdatamodeld */,
|
||||
DA0CC54F1EB6AE45009A8ED9 /* MasterPassword.xcdatamodeld */,
|
||||
DA5E5C971724A667003798D8 /* MPAlgorithm.h */,
|
||||
DA5E5C981724A667003798D8 /* MPAlgorithm.m */,
|
||||
DA5E5C991724A667003798D8 /* MPAlgorithmV0.h */,
|
||||
@@ -1772,8 +1781,8 @@
|
||||
DACA23B41705DF7D002C6C22 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA0CC53C1EB57B69009A8ED9 /* Fabric */,
|
||||
DA09745F1E995EB500F0BFE8 /* mpw_tests.xml */,
|
||||
DACA26991705DF81002C6C22 /* Crashlytics */,
|
||||
DACA29701705E1A8002C6C22 /* Data */,
|
||||
DACA23B51705DF7D002C6C22 /* Media */,
|
||||
);
|
||||
@@ -1856,14 +1865,6 @@
|
||||
path = Fonts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DACA26991705DF81002C6C22 /* Crashlytics */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DACA269A1705DF81002C6C22 /* Crashlytics.plist */,
|
||||
);
|
||||
path = Crashlytics;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DACA29701705E1A8002C6C22 /* Data */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -2086,7 +2087,7 @@
|
||||
DA5BFA42147E415C00F98B1E /* Resources */,
|
||||
DAD9B5EE1762CA3A001835F9 /* Copy LoginHelper */,
|
||||
DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */,
|
||||
DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */,
|
||||
DAD3125D155288AA00A3F9ED /* Run Script: Fabric */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -2295,13 +2296,13 @@
|
||||
DACA27331705DF81002C6C22 /* avatar-12@2x.png in Resources */,
|
||||
DACA27341705DF81002C6C22 /* avatar-2@2x.png in Resources */,
|
||||
DA60717D195D040500CA98B5 /* icon_gear@2x.png in Resources */,
|
||||
DA0CC53E1EB57B69009A8ED9 /* Fabric.plist in Resources */,
|
||||
DACA27351705DF81002C6C22 /* avatar-11.png in Resources */,
|
||||
DACA27361705DF81002C6C22 /* avatar-0@2x.png in Resources */,
|
||||
DACA27371705DF81002C6C22 /* avatar-10@2x.png in Resources */,
|
||||
DACA27381705DF81002C6C22 /* menu-icon.png in Resources */,
|
||||
DACA29671705DF81002C6C22 /* SourceCodePro-ExtraLight.otf in Resources */,
|
||||
DACA29681705DF81002C6C22 /* SourceCodePro-Black.otf in Resources */,
|
||||
DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */,
|
||||
DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */,
|
||||
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */,
|
||||
DA5E5D081724A667003798D8 /* MasterPassword.entitlements in Resources */,
|
||||
@@ -2359,19 +2360,19 @@
|
||||
shellPath = "/bin/sh -e";
|
||||
shellScript = "exec Scripts/updatePlist";
|
||||
};
|
||||
DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */ = {
|
||||
DAD3125D155288AA00A3F9ED /* Run Script: Fabric */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Run Script: Crashlytics";
|
||||
name = "Run Script: Fabric";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = "/bin/sh -e";
|
||||
shellScript = "[[ $DEPLOYMENT_LOCATION != YES ]] && exit\n\napiKey=$(/usr/libexec/PlistBuddy -c \"Print :'API Key'\" Resources/Crashlytics/Crashlytics.plist)\n[[ $apiKey ]] && External/Mac/Fabric.framework/run \"$apiKey\" 410fb41450e3a2e50fa8357682d812ecd3e1846f2141a99bdb9d3a6a981ad69c";
|
||||
shellScript = "[[ $DEPLOYMENT_LOCATION != YES ]] && exit\n\napiKey=$(/usr/libexec/PlistBuddy -c \"Print :'API Key'\" Resources/Fabric/Fabric.plist)\n[[ $apiKey ]] && External/Mac/Fabric.framework/run \"$apiKey\" 410fb41450e3a2e50fa8357682d812ecd3e1846f2141a99bdb9d3a6a981ad69c";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
@@ -2408,8 +2409,8 @@
|
||||
93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */,
|
||||
DA32CFDF19CF1C70004F3F0E /* MPSiteEntity.m in Sources */,
|
||||
93D394C4254EEB45FB335AFB /* MPSitesTableView.m in Sources */,
|
||||
DA95B5231C477DE10067F5EF /* MasterPassword.xcdatamodeld in Sources */,
|
||||
DA6774291A4746AF004F356A /* mpw-algorithm.c in Sources */,
|
||||
DA0CC5591EB6AE45009A8ED9 /* MasterPassword.xcdatamodeld in Sources */,
|
||||
93D395E4830290EBB6E71F34 /* MPNoStateButton.m in Sources */,
|
||||
DA4DAE941A7D8117003E5423 /* MPAlgorithmV3.m in Sources */,
|
||||
DA4DAE951A7D8117003E5423 /* MPTypes.m in Sources */,
|
||||
@@ -2568,7 +2569,6 @@
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"ADHOC=1",
|
||||
"$(inherited)",
|
||||
"NDEBUG=1",
|
||||
"NS_BLOCK_ASSERTIONS=1",
|
||||
@@ -2838,7 +2838,6 @@
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"ADHOC=1",
|
||||
"$(inherited)",
|
||||
"NDEBUG=1",
|
||||
"NS_BLOCK_ASSERTIONS=1",
|
||||
@@ -3131,19 +3130,20 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCVersionGroup section */
|
||||
DA95B51A1C477DE10067F5EF /* MasterPassword.xcdatamodeld */ = {
|
||||
DA0CC54F1EB6AE45009A8ED9 /* MasterPassword.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DA95B51B1C477DE10067F5EF /* MasterPassword 1.xcdatamodel */,
|
||||
DA95B51C1C477DE10067F5EF /* MasterPassword 2.xcdatamodel */,
|
||||
DA95B51D1C477DE10067F5EF /* MasterPassword 3.xcdatamodel */,
|
||||
DA95B51E1C477DE10067F5EF /* MasterPassword 4.xcdatamodel */,
|
||||
DA95B51F1C477DE10067F5EF /* MasterPassword 5.xcdatamodel */,
|
||||
DA95B5201C477DE10067F5EF /* MasterPassword 6.xcdatamodel */,
|
||||
DA95B5211C477DE10067F5EF /* MasterPassword 7.xcdatamodel */,
|
||||
DA95B5221C477DE10067F5EF /* MasterPassword 8.xcdatamodel */,
|
||||
DA0CC5501EB6AE45009A8ED9 /* MasterPassword 1.xcdatamodel */,
|
||||
DA0CC5511EB6AE45009A8ED9 /* MasterPassword 2.xcdatamodel */,
|
||||
DA0CC5521EB6AE45009A8ED9 /* MasterPassword 3.xcdatamodel */,
|
||||
DA0CC5531EB6AE45009A8ED9 /* MasterPassword 4.xcdatamodel */,
|
||||
DA0CC5541EB6AE45009A8ED9 /* MasterPassword 5.xcdatamodel */,
|
||||
DA0CC5551EB6AE45009A8ED9 /* MasterPassword 6.xcdatamodel */,
|
||||
DA0CC5561EB6AE45009A8ED9 /* MasterPassword 7.xcdatamodel */,
|
||||
DA0CC5571EB6AE45009A8ED9 /* MasterPassword 8.xcdatamodel */,
|
||||
DA0CC5581EB6AE45009A8ED9 /* MasterPassword 9.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DA95B5221C477DE10067F5EF /* MasterPassword 8.xcdatamodel */;
|
||||
currentVersion = DA0CC5581EB6AE45009A8ED9 /* MasterPassword 9.xcdatamodel */;
|
||||
path = MasterPassword.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
|
||||
@@ -41,9 +41,9 @@ case $PLATFORM_NAME in
|
||||
*) ftl 'ERROR: Unknown platform: %s.' "$PLATFORM_NAME"; exit 1 ;;
|
||||
esac
|
||||
|
||||
description=$(git describe --always --dirty --long --match "*-$platform-*")
|
||||
description=$(git describe --always --dirty --long --match "*-$platform*")
|
||||
version=${description%-g*} build=${version##*-} version=${version%-$build}
|
||||
version=${version//-*-/.} release=${version%%-*} commit=${description##*-g}
|
||||
version=${version//-$platform/} version=${version//-/.} commit=${description##*-g}
|
||||
|
||||
addPlistWithKey GITDescription string "$description"
|
||||
setPlistWithKey CFBundleVersion "$(hr "${version%%.*}" 14).${version#*.}"
|
||||
@@ -55,14 +55,14 @@ setSettingWithTitle "Copyright" "$(getPlistWithKey NSHumanReadableCopyright)"
|
||||
|
||||
if [[ $DEPLOYMENT_LOCATION = YES ]]; then
|
||||
# This build is a release. Do some release checks.
|
||||
crashlyticsPlist="$BUILT_PRODUCTS_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/Crashlytics.plist"
|
||||
fabricPlist="$BUILT_PRODUCTS_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/Fabric.plist"
|
||||
passed=1
|
||||
[[ $description != *-dirty ]] || \
|
||||
{ passed=0; err 'ERROR: Cannot release a dirty version, first commit any changes.'; }
|
||||
[[ $build == 0 ]] || \
|
||||
{ passed=0; err 'ERROR: Commit is not tagged for release, first tag accordingly.'; }
|
||||
[[ -r "$crashlyticsPlist" && $(PlistBuddy -c "Print :'API Key'" "$crashlyticsPlist" 2>/dev/null) ]] || \
|
||||
{ passed=0; err 'ERROR: Cannot release: Crashlytics API key is missing.'; }
|
||||
[[ -r "$fabricPlist" && $(PlistBuddy -c "Print :'API Key'" "$fabricPlist" 2>/dev/null) ]] || \
|
||||
{ passed=0; err 'ERROR: Cannot release: Fabric API key is missing.'; }
|
||||
(( passed )) || \
|
||||
{ ftl "Failed to pass release checks. Fix the above errors and re-try. Aborting."; exit 1; }
|
||||
fi
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
//==============================================================================
|
||||
|
||||
#import "MPKey.h"
|
||||
#import "MPStoredSiteEntity.h"
|
||||
#import "MPGeneratedSiteEntity.h"
|
||||
#import "MPSiteQuestionEntity.h"
|
||||
#import "MPStoredSiteEntity+CoreDataClass.h"
|
||||
#import "MPGeneratedSiteEntity+CoreDataClass.h"
|
||||
#import "MPSiteQuestionEntity+CoreDataClass.h"
|
||||
#import "mpw-algorithm.h"
|
||||
|
||||
#define MPAlgorithmDefaultVersion MPAlgorithmVersionCurrent
|
||||
@@ -58,6 +58,7 @@ NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack);
|
||||
- (Class)classOfType:(MPSiteType)type;
|
||||
- (NSArray *)allTypes;
|
||||
- (NSArray *)allTypesStartingWith:(MPSiteType)startingType;
|
||||
- (MPSiteType)defaultType;
|
||||
- (MPSiteType)nextType:(MPSiteType)type;
|
||||
- (MPSiteType)previousType:(MPSiteType)type;
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d AND user == %@", self.version, user];
|
||||
NSArray *migrationSites = [moc executeFetchRequest:migrationRequest error:&error];
|
||||
if (!migrationSites) {
|
||||
err( @"While looking for sites to migrate: %@", [error fullDescription] );
|
||||
MPError( error, @"While looking for sites to migrate." );
|
||||
return NO;
|
||||
}
|
||||
|
||||
@@ -286,6 +286,11 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
return allTypes;
|
||||
}
|
||||
|
||||
- (MPSiteType)defaultType {
|
||||
|
||||
return MPSiteTypeGeneratedLong;
|
||||
}
|
||||
|
||||
- (MPSiteType)nextType:(MPSiteType)type {
|
||||
|
||||
switch (type) {
|
||||
@@ -309,9 +314,9 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
return MPSiteTypeStoredDevicePrivate;
|
||||
case MPSiteTypeStoredDevicePrivate:
|
||||
return MPSiteTypeGeneratedPhrase;
|
||||
default:
|
||||
return MPSiteTypeGeneratedLong;
|
||||
}
|
||||
|
||||
return [self defaultType];
|
||||
}
|
||||
|
||||
- (MPSiteType)previousType:(MPSiteType)type {
|
||||
@@ -430,58 +435,38 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
|
||||
- (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_group_enter( group );
|
||||
__block NSString *result = nil;
|
||||
[self resolveLoginForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||
result = result_;
|
||||
dispatch_group_leave( group );
|
||||
}];
|
||||
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
|
||||
|
||||
return result;
|
||||
return PearlAwait( ^(void (^setResult)(id)) {
|
||||
[self resolveLoginForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||
setResult( result_ );
|
||||
}];
|
||||
} );
|
||||
}
|
||||
|
||||
- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_group_enter( group );
|
||||
__block NSString *result = nil;
|
||||
[self resolvePasswordForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||
result = result_;
|
||||
dispatch_group_leave( group );
|
||||
}];
|
||||
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
|
||||
|
||||
return result;
|
||||
return PearlAwait( ^(void (^setResult)(id)) {
|
||||
[self resolvePasswordForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||
setResult( result_ );
|
||||
}];
|
||||
} );
|
||||
}
|
||||
|
||||
- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_group_enter( group );
|
||||
__block NSString *result = nil;
|
||||
[self resolveAnswerForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||
result = result_;
|
||||
dispatch_group_leave( group );
|
||||
}];
|
||||
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
|
||||
|
||||
return result;
|
||||
return PearlAwait( ^(void (^setResult)(id)) {
|
||||
[self resolveAnswerForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||
setResult( result_ );
|
||||
}];
|
||||
} );
|
||||
}
|
||||
|
||||
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey {
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_group_enter( group );
|
||||
__block NSString *result = nil;
|
||||
[self resolveAnswerForQuestion:question usingKey:siteKey result:^(NSString *result_) {
|
||||
result = result_;
|
||||
dispatch_group_leave( group );
|
||||
}];
|
||||
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
|
||||
|
||||
return result;
|
||||
return PearlAwait( ^(void (^setResult)(id)) {
|
||||
[self resolveAnswerForQuestion:question usingKey:siteKey result:^(NSString *result_) {
|
||||
setResult( result_ );
|
||||
}];
|
||||
} );
|
||||
}
|
||||
|
||||
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
||||
@@ -498,10 +483,12 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
else
|
||||
algorithm = site.algorithm;
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
resultBlock( loginName || !loginGenerated? loginName:
|
||||
[algorithm generateLoginForSiteNamed:name usingKey:siteKey] );
|
||||
} );
|
||||
if (!loginGenerated || [loginName length])
|
||||
resultBlock( loginName );
|
||||
else
|
||||
PearlNotMainQueue( ^{
|
||||
resultBlock( [algorithm generateLoginForSiteNamed:name usingKey:siteKey] );
|
||||
} );
|
||||
}
|
||||
|
||||
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
||||
@@ -533,9 +520,8 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
else
|
||||
algorithm = site.algorithm;
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
NSString *result = [algorithm generatePasswordForSiteNamed:name ofType:type withCounter:counter usingKey:siteKey];
|
||||
resultBlock( result );
|
||||
PearlNotMainQueue( ^{
|
||||
resultBlock( [algorithm generatePasswordForSiteNamed:name ofType:type withCounter:counter usingKey:siteKey] );
|
||||
} );
|
||||
break;
|
||||
}
|
||||
@@ -549,9 +535,8 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
|
||||
NSData *encryptedContent = ((MPStoredSiteEntity *)site).contentObject;
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
NSString *result = [self decryptContent:encryptedContent usingKey:siteKey];
|
||||
resultBlock( result );
|
||||
PearlNotMainQueue( ^{
|
||||
resultBlock( [self decryptContent:encryptedContent usingKey:siteKey] );
|
||||
} );
|
||||
break;
|
||||
}
|
||||
@@ -563,9 +548,8 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
|
||||
NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:siteQuery];
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
NSString *result = [self decryptContent:encryptedContent usingKey:siteKey];
|
||||
resultBlock( result );
|
||||
PearlNotMainQueue( ^{
|
||||
resultBlock( [self decryptContent:encryptedContent usingKey:siteKey] );
|
||||
} );
|
||||
break;
|
||||
}
|
||||
@@ -584,9 +568,8 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
else
|
||||
algorithm = site.algorithm;
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
NSString *result = [algorithm generateAnswerForSiteNamed:name onQuestion:nil usingKey:siteKey];
|
||||
resultBlock( result );
|
||||
PearlNotMainQueue( ^{
|
||||
resultBlock( [algorithm generateAnswerForSiteNamed:name onQuestion:nil usingKey:siteKey] );
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -605,9 +588,8 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
else if ([[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateAnswers])
|
||||
algorithm = question.site.algorithm;
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
NSString *result = [algorithm generateAnswerForSiteNamed:name onQuestion:keyword usingKey:siteKey];
|
||||
resultBlock( result );
|
||||
PearlNotMainQueue( ^{
|
||||
resultBlock( [algorithm generateAnswerForSiteNamed:name onQuestion:keyword usingKey:siteKey] );
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
@@ -25,17 +25,20 @@
|
||||
#define MPProductTouchID @"com.lyndir.masterpassword.products.touchid"
|
||||
#define MPProductFuel @"com.lyndir.masterpassword.products.fuel"
|
||||
|
||||
#define MP_FUEL_HOURLY_RATE 30.f /* Tier 1 purchases/h ~> USD/h */
|
||||
#define MP_FUEL_HOURLY_RATE 40.f /* payment in tier 1 purchases / h (≅ USD / h) */
|
||||
|
||||
@protocol MPInAppDelegate
|
||||
|
||||
- (void)updateWithProducts:(NSArray /* SKProduct */ *)products;
|
||||
- (void)updateWithTransaction:(SKPaymentTransaction *)transaction;
|
||||
- (void)updateWithProducts:(NSDictionary<NSString *, SKProduct *> *)products
|
||||
transactions:(NSDictionary<NSString *, SKPaymentTransaction *> *)transactions;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPAppDelegate_Shared(InApp)
|
||||
|
||||
- (NSDictionary<NSString *, SKProduct *> *)products;
|
||||
- (NSDictionary<NSString *, SKPaymentTransaction *> *)transactions;
|
||||
|
||||
- (void)registerProductsObserver:(id<MPInAppDelegate>)delegate;
|
||||
- (void)removeProductsObserver:(id<MPInAppDelegate>)delegate;
|
||||
|
||||
|
||||
@@ -23,9 +23,20 @@
|
||||
|
||||
@implementation MPAppDelegate_Shared(InApp)
|
||||
|
||||
PearlAssociatedObjectProperty( NSArray*, Products, products );
|
||||
PearlAssociatedObjectProperty( NSDictionary*, Products, products );
|
||||
|
||||
PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObservers );
|
||||
|
||||
- (NSDictionary<NSString *, SKPaymentTransaction *> *)transactions {
|
||||
|
||||
NSMutableDictionary<NSString *, SKPaymentTransaction *> *transactions =
|
||||
[NSMutableDictionary dictionaryWithCapacity:self.paymentQueue.transactions.count];
|
||||
for (SKPaymentTransaction *transaction in self.paymentQueue.transactions)
|
||||
transactions[transaction.payment.productIdentifier] = transaction;
|
||||
|
||||
return transactions;
|
||||
}
|
||||
|
||||
- (void)registerProductsObserver:(id<MPInAppDelegate>)delegate {
|
||||
|
||||
if (!self.productObservers)
|
||||
@@ -33,7 +44,7 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
[self.productObservers addObject:delegate];
|
||||
|
||||
if (self.products)
|
||||
[delegate updateWithProducts:self.products];
|
||||
[delegate updateWithProducts:self.products transactions:[self transactions]];
|
||||
else
|
||||
[self reloadProducts];
|
||||
}
|
||||
@@ -75,7 +86,7 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
// Consumable product.
|
||||
return NO;
|
||||
|
||||
#if ADHOC || DEBUG
|
||||
#if DEBUG
|
||||
// All features are unlocked for beta / debug / mac versions.
|
||||
return YES;
|
||||
#else
|
||||
@@ -101,11 +112,13 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
}
|
||||
#endif
|
||||
|
||||
for (SKProduct *product in self.products)
|
||||
for (SKProduct *product in [self.products allValues])
|
||||
if ([product.productIdentifier isEqualToString:productIdentifier]) {
|
||||
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
|
||||
payment.quantity = quantity;
|
||||
[[self paymentQueue] addPayment:payment];
|
||||
if (payment) {
|
||||
payment.quantity = quantity;
|
||||
[[self paymentQueue] addPayment:payment];
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -114,15 +127,22 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
|
||||
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
|
||||
|
||||
inf( @"products: %@, invalid: %@", response.products, response.invalidProductIdentifiers );
|
||||
self.products = response.products;
|
||||
if ([response.invalidProductIdentifiers count])
|
||||
inf( @"Invalid products: %@", response.invalidProductIdentifiers );
|
||||
|
||||
NSMutableDictionary *products = [NSMutableDictionary dictionaryWithCapacity:[response.products count]];
|
||||
for (SKProduct *product in response.products)
|
||||
products[product.productIdentifier] = product;
|
||||
self.products = products;
|
||||
|
||||
for (id<MPInAppDelegate> productObserver in self.productObservers)
|
||||
[productObserver updateWithProducts:self.products];
|
||||
[productObserver updateWithProducts:self.products transactions:[self transactions]];
|
||||
}
|
||||
|
||||
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
|
||||
|
||||
MPError( error, @"StoreKit request (%@) failed.", request );
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
[PearlAlert showAlertWithTitle:@"Purchase Failed" message:
|
||||
strf( @"%@\n\n%@", error.localizedDescription,
|
||||
@@ -131,7 +151,6 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
cancelTitle:@"OK" otherTitles:nil];
|
||||
#else
|
||||
#endif
|
||||
err( @"StoreKit request (%@) failed: %@", request, [error fullDescription] );
|
||||
}
|
||||
|
||||
- (void)requestDidFinish:(SKRequest *)request {
|
||||
@@ -145,23 +164,41 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
|
||||
for (SKPaymentTransaction *transaction in transactions) {
|
||||
dbg( @"transaction updated: %@ -> %d", transaction.payment.productIdentifier, (int)(transaction.transactionState) );
|
||||
|
||||
switch (transaction.transactionState) {
|
||||
case SKPaymentTransactionStatePurchased: {
|
||||
inf( @"purchased: %@", transaction.payment.productIdentifier );
|
||||
inf( @"Purchased: %@", transaction.payment.productIdentifier );
|
||||
NSMutableDictionary *attributes = [NSMutableDictionary new];
|
||||
|
||||
if ([transaction.payment.productIdentifier isEqualToString:MPProductFuel]) {
|
||||
float currentFuel = [[MPiOSConfig get].developmentFuelRemaining floatValue];
|
||||
float purchasedFuel = transaction.payment.quantity / MP_FUEL_HOURLY_RATE;
|
||||
[MPiOSConfig get].developmentFuelRemaining = @(currentFuel + purchasedFuel);
|
||||
if (![MPiOSConfig get].developmentFuelChecked || currentFuel < DBL_EPSILON)
|
||||
[MPiOSConfig get].developmentFuelChecked = [NSDate date];
|
||||
[attributes addEntriesFromDictionary:@{
|
||||
@"currentFuel" : @(currentFuel),
|
||||
@"purchasedFuel": @(purchasedFuel),
|
||||
}];
|
||||
}
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier
|
||||
forKey:transaction.payment.productIdentifier];
|
||||
[queue finishTransaction:transaction];
|
||||
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
#ifdef CRASHLYTICS
|
||||
SKProduct *product = self.products[transaction.payment.productIdentifier];
|
||||
for (int q = 0; q < transaction.payment.quantity; ++q)
|
||||
[Answers logPurchaseWithPrice:product.price currency:[product.priceLocale objectForKey:NSLocaleCurrencyCode]
|
||||
success:@YES itemName:product.localizedTitle itemType:@"InApp"
|
||||
itemId:product.productIdentifier customAttributes:attributes];
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SKPaymentTransactionStateRestored: {
|
||||
inf( @"restored: %@", transaction.payment.productIdentifier );
|
||||
inf( @"Restored: %@", transaction.payment.productIdentifier );
|
||||
[[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier
|
||||
forKey:transaction.payment.productIdentifier];
|
||||
[queue finishTransaction:transaction];
|
||||
@@ -171,21 +208,38 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
case SKPaymentTransactionStateDeferred:
|
||||
break;
|
||||
case SKPaymentTransactionStateFailed:
|
||||
err( @"Transaction failed: %@, reason: %@", transaction.payment.productIdentifier, [transaction.error fullDescription] );
|
||||
MPError( transaction.error, @"Transaction failed: %@.", transaction.payment.productIdentifier );
|
||||
[queue finishTransaction:transaction];
|
||||
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
#ifdef CRASHLYTICS
|
||||
SKProduct *product = self.products[transaction.payment.productIdentifier];
|
||||
[Answers logPurchaseWithPrice:product.price currency:[product.priceLocale objectForKey:NSLocaleCurrencyCode]
|
||||
success:@NO itemName:product.localizedTitle itemType:@"InApp" itemId:product.productIdentifier
|
||||
customAttributes:@{
|
||||
@"state" : @"Failed",
|
||||
@"quantity": @(transaction.payment.quantity),
|
||||
@"reason" : [transaction.error localizedFailureReason]?: [transaction.error localizedDescription],
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (id<MPInAppDelegate> productObserver in self.productObservers)
|
||||
[productObserver updateWithTransaction:transaction];
|
||||
}
|
||||
|
||||
if (![[NSUserDefaults standardUserDefaults] synchronize])
|
||||
wrn( @"Couldn't synchronize after transaction updates." );
|
||||
|
||||
NSMutableDictionary<NSString *, SKPaymentTransaction *> *allTransactions = [[self transactions] mutableCopy];
|
||||
for (SKPaymentTransaction *transaction in transactions)
|
||||
allTransactions[transaction.payment.productIdentifier] = transaction;
|
||||
for (id<MPInAppDelegate> productObserver in self.productObservers)
|
||||
[productObserver updateWithProducts:self.products transactions:allTransactions];
|
||||
}
|
||||
|
||||
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
|
||||
|
||||
err( @"StoreKit restore failed: %@", [error fullDescription] );
|
||||
MPError( error, @"StoreKit restore failed." );
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -16,18 +16,19 @@
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#import <Crashlytics/Answers.h>
|
||||
#import "MPAppDelegate_Key.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
|
||||
@interface MPAppDelegate_Shared()
|
||||
|
||||
@property(strong, nonatomic) MPKey *key;
|
||||
@property(strong, atomic) MPKey *key;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPAppDelegate_Shared(Key)
|
||||
|
||||
static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigin *keyOrigin) {
|
||||
- (NSDictionary *)createKeyQueryforUser:(MPUserEntity *)user origin:(out MPKeyOrigin *)keyOrigin {
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
if (user.touchID && kSecUseAuthenticationUI) {
|
||||
@@ -37,17 +38,17 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
|
||||
CFErrorRef acError = NULL;
|
||||
id accessControl = (__bridge_transfer id)SecAccessControlCreateWithFlags( kCFAllocatorDefault,
|
||||
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlTouchIDCurrentSet, &acError );
|
||||
if (!accessControl || acError)
|
||||
err( @"Could not use TouchID on this device: %@", acError );
|
||||
if (!accessControl)
|
||||
MPError( (__bridge_transfer NSError *)acError, @"Could not use TouchID on this device." );
|
||||
|
||||
else
|
||||
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
||||
attributes:@{
|
||||
(__bridge id)kSecAttrService : @"Saved Master Password",
|
||||
(__bridge id)kSecAttrAccount : user.name?: @"",
|
||||
(__bridge id)kSecAttrAccessControl : accessControl,
|
||||
(__bridge id)kSecUseAuthenticationUI : (__bridge id)kSecUseAuthenticationUIAllow,
|
||||
(__bridge id)kSecUseOperationPrompt :
|
||||
(__bridge id)kSecAttrService : @"Saved Master Password",
|
||||
(__bridge id)kSecAttrAccount : user.name?: @"",
|
||||
(__bridge id)kSecAttrAccessControl : accessControl,
|
||||
(__bridge id)kSecUseAuthenticationUI: (__bridge id)kSecUseAuthenticationUIAllow,
|
||||
(__bridge id)kSecUseOperationPrompt :
|
||||
strf( @"Access %@'s master password.", user.name ),
|
||||
}
|
||||
matches:nil];
|
||||
@@ -59,10 +60,10 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
|
||||
|
||||
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
||||
attributes:@{
|
||||
(__bridge id)kSecAttrService: @"Saved Master Password",
|
||||
(__bridge id)kSecAttrAccount: user.name?: @"",
|
||||
(__bridge id)kSecAttrService : @"Saved Master Password",
|
||||
(__bridge id)kSecAttrAccount : user.name?: @"",
|
||||
#if TARGET_OS_IPHONE
|
||||
(__bridge id)kSecAttrAccessible : (__bridge id)(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly?: kSecAttrAccessibleWhenUnlockedThisDeviceOnly),
|
||||
(__bridge id)kSecAttrAccessible: (__bridge id)(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly?: kSecAttrAccessibleWhenUnlockedThisDeviceOnly),
|
||||
#endif
|
||||
}
|
||||
matches:nil];
|
||||
@@ -71,15 +72,24 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
|
||||
- (MPKey *)loadSavedKeyFor:(MPUserEntity *)user {
|
||||
|
||||
MPKeyOrigin keyOrigin;
|
||||
NSDictionary *keyQuery = createKeyQuery( user, NO, &keyOrigin );
|
||||
NSData *keyData = [PearlKeyChain dataOfItemForQuery:keyQuery];
|
||||
if (!keyData) {
|
||||
NSDictionary *keyQuery = [self createKeyQueryforUser:user origin:&keyOrigin];
|
||||
id<MPAlgorithm> keyAlgorithm = user.algorithm;
|
||||
MPKey *key = [[MPKey alloc] initForFullName:user.name withKeyResolver:^NSData *(id<MPAlgorithm> algorithm) {
|
||||
return ![algorithm isEqual:keyAlgorithm]? nil:
|
||||
PearlMainQueueAwait( (id)^{
|
||||
return [PearlKeyChain dataOfItemForQuery:keyQuery];
|
||||
} );
|
||||
} keyOrigin:keyOrigin];
|
||||
|
||||
if ([key keyIDForAlgorithm:user.algorithm])
|
||||
inf( @"Found key in keychain for user: %@", user.userID );
|
||||
|
||||
else {
|
||||
inf( @"No key found in keychain for user: %@", user.userID );
|
||||
return nil;
|
||||
key = nil;
|
||||
}
|
||||
|
||||
inf( @"Found key in keychain for user: %@", user.userID );
|
||||
return [[MPKey alloc] initForFullName:user.name withKeyData:keyData forAlgorithm:user.algorithm keyOrigin:keyOrigin];
|
||||
return key;
|
||||
}
|
||||
|
||||
- (void)storeSavedKeyFor:(MPUserEntity *)user {
|
||||
@@ -90,7 +100,7 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
|
||||
[self forgetSavedKeyFor:user];
|
||||
|
||||
inf( @"Saving key in keychain for user: %@", user.userID );
|
||||
[PearlKeyChain addOrUpdateItemForQuery:createKeyQuery( user, YES, nil )
|
||||
[PearlKeyChain addOrUpdateItemForQuery:[self createKeyQueryforUser:user origin:nil]
|
||||
withAttributes:@{ (__bridge id)kSecValueData: keyData }];
|
||||
}
|
||||
}
|
||||
@@ -98,7 +108,7 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
|
||||
|
||||
- (void)forgetSavedKeyFor:(MPUserEntity *)user {
|
||||
|
||||
OSStatus result = [PearlKeyChain deleteItemForQuery:createKeyQuery( user, NO, nil )];
|
||||
OSStatus result = [PearlKeyChain deleteItemForQuery:[self createKeyQueryforUser:user origin:nil]];
|
||||
if (result == noErr) {
|
||||
inf( @"Removed key from keychain for user: %@", user.userID );
|
||||
|
||||
@@ -168,6 +178,14 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
|
||||
else
|
||||
dbg( @"Automatic login failed for user: %@", user.userID );
|
||||
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
#ifdef CRASHLYTICS
|
||||
[Answers logLoginWithMethod:password? @"Password": @"Automatic" success:@NO customAttributes:@{
|
||||
@"algorithm": @(user.algorithm.version),
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
inf( @"Logged in user: %@", user.userID );
|
||||
@@ -189,8 +207,11 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
|
||||
@try {
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
#ifdef CRASHLYTICS
|
||||
[[Crashlytics sharedInstance] setObjectValue:user.userID forKey:@"username"];
|
||||
[[Crashlytics sharedInstance] setUserName:user.userID];
|
||||
|
||||
[Answers logLoginWithMethod:password? @"Password": @"Automatic" success:@YES customAttributes:@{
|
||||
@"algorithm": @(user.algorithm.version),
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -230,28 +251,22 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
|
||||
NSString *content;
|
||||
while (!(content = [site.algorithm storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:recoverKey])) {
|
||||
// Failed to decrypt site with the current recoveryKey. Ask user for a new one to use.
|
||||
__block NSString *masterPassword = nil;
|
||||
NSString *masterPassword = nil;
|
||||
|
||||
#ifdef PEARL_UIKIT
|
||||
dispatch_group_t recoverPasswordGroup = dispatch_group_create();
|
||||
dispatch_group_enter( recoverPasswordGroup );
|
||||
[PearlAlert showAlertWithTitle:@"Enter Old Master Password"
|
||||
message:PearlString( @"Your old master password is required to migrate the stored password for %@",
|
||||
site.name )
|
||||
viewStyle:UIAlertViewStyleSecureTextInput
|
||||
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||
@try {
|
||||
masterPassword = PearlAwait( ^(void (^setResult)(id)) {
|
||||
[PearlAlert showAlertWithTitle:@"Enter Old Master Password"
|
||||
message:PearlString(
|
||||
@"Your old master password is required to migrate the stored password for %@",
|
||||
site.name )
|
||||
viewStyle:UIAlertViewStyleSecureTextInput
|
||||
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
||||
// Don't Migrate
|
||||
return;
|
||||
|
||||
masterPassword = [alert_ textFieldAtIndex:0].text;
|
||||
}
|
||||
@finally {
|
||||
dispatch_group_leave( recoverPasswordGroup );
|
||||
}
|
||||
} cancelTitle:@"Don't Migrate" otherTitles:@"Migrate", nil];
|
||||
dispatch_group_wait( recoverPasswordGroup, DISPATCH_TIME_FOREVER );
|
||||
setResult( nil );
|
||||
else
|
||||
setResult( [alert_ textFieldAtIndex:0].text );
|
||||
} cancelTitle:@"Don't Migrate" otherTitles:@"Migrate", nil];
|
||||
} );
|
||||
#endif
|
||||
if (!masterPassword)
|
||||
// Don't Migrate
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
|
||||
#endif
|
||||
|
||||
@property(strong, nonatomic, readonly) MPKey *key;
|
||||
@property(strong, nonatomic, readonly) NSManagedObjectID *activeUserOID;
|
||||
@property(strong, nonatomic, readonly) NSPersistentStoreCoordinator *storeCoordinator;
|
||||
@property(strong, atomic, readonly) MPKey *key;
|
||||
@property(strong, atomic, readonly) NSManagedObjectID *activeUserOID;
|
||||
@property(strong, atomic, readonly) NSPersistentStoreCoordinator *storeCoordinator;
|
||||
|
||||
+ (instancetype)get;
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
|
||||
@interface MPAppDelegate_Shared()
|
||||
|
||||
@property(strong, nonatomic) MPKey *key;
|
||||
@property(strong, nonatomic) NSManagedObjectID *activeUserOID;
|
||||
@property(strong, nonatomic) NSPersistentStoreCoordinator *storeCoordinator;
|
||||
@property(strong, atomic) MPKey *key;
|
||||
@property(strong, atomic) NSManagedObjectID *activeUserOID;
|
||||
@property(strong, atomic) NSPersistentStoreCoordinator *storeCoordinator;
|
||||
|
||||
@end
|
||||
|
||||
@@ -74,14 +74,11 @@
|
||||
|
||||
- (void)setActiveUser:(MPUserEntity *)activeUser {
|
||||
|
||||
NSError *error;
|
||||
if (activeUser.objectID.isTemporaryID && ![activeUser.managedObjectContext obtainPermanentIDsForObjects:@[ activeUser ] error:&error])
|
||||
err( @"Failed to obtain a permanent object ID after setting active user: %@", [error fullDescription] );
|
||||
|
||||
self.activeUserOID = activeUser.objectID;
|
||||
self.activeUserOID = activeUser.permanentObjectID;
|
||||
}
|
||||
|
||||
- (void)handleCoordinatorError:(NSError *)error {
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -35,6 +35,7 @@ typedef NS_ENUM( NSUInteger, MPImportResult ) {
|
||||
+ (BOOL)managedObjectContextForMainThreadPerformBlockAndWait:(void ( ^ )(NSManagedObjectContext *mainContext))mocBlock;
|
||||
+ (BOOL)managedObjectContextPerformBlock:(void ( ^ )(NSManagedObjectContext *context))mocBlock;
|
||||
+ (BOOL)managedObjectContextPerformBlockAndWait:(void ( ^ )(NSManagedObjectContext *context))mocBlock;
|
||||
+ (id)managedObjectContextChanged:(void ( ^ )(NSDictionary<NSManagedObjectID *, NSString *> *affectedObjects))changedBlock;
|
||||
|
||||
- (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context;
|
||||
- (void)deleteAndResetStore;
|
||||
|
||||
@@ -131,6 +131,25 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (id)managedObjectContextChanged:(void ( ^ )(NSDictionary<NSManagedObjectID *, NSString *> *affectedObjects))changedBlock {
|
||||
|
||||
NSManagedObjectContext *privateManagedObjectContextIfReady = [[self get] privateManagedObjectContextIfReady];
|
||||
if (!privateManagedObjectContextIfReady)
|
||||
return nil;
|
||||
|
||||
return PearlAddNotificationObserver( NSManagedObjectContextObjectsDidChangeNotification, privateManagedObjectContextIfReady, nil,
|
||||
^(id host, NSNotification *note) {
|
||||
NSMutableDictionary *affectedObjects = [NSMutableDictionary new];
|
||||
for (NSManagedObject *object in note.userInfo[NSInsertedObjectsKey])
|
||||
affectedObjects[object.objectID] = NSInsertedObjectsKey;
|
||||
for (NSManagedObject *object in note.userInfo[NSUpdatedObjectsKey])
|
||||
affectedObjects[object.objectID] = NSUpdatedObjectsKey;
|
||||
for (NSManagedObject *object in note.userInfo[NSDeletedObjectsKey])
|
||||
affectedObjects[object.objectID] = NSDeletedObjectsKey;
|
||||
changedBlock( affectedObjects );
|
||||
} );
|
||||
}
|
||||
|
||||
- (NSManagedObjectContext *)mainManagedObjectContextIfReady {
|
||||
|
||||
[self loadStore];
|
||||
@@ -196,19 +215,21 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
|
||||
self.mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
|
||||
self.mainManagedObjectContext.parentContext = self.privateManagedObjectContext;
|
||||
|
||||
// When privateManagedObjectContext is saved, import the changes into mainManagedObjectContext.
|
||||
PearlAddNotificationObserverTo( self.mainManagedObjectContext, NSManagedObjectContextDidSaveNotification,
|
||||
self.privateManagedObjectContext, nil, ^(NSManagedObjectContext *mainManagedObjectContext, NSNotification *note) {
|
||||
[mainManagedObjectContext performBlock:^{
|
||||
@try {
|
||||
[mainManagedObjectContext mergeChangesFromContextDidSaveNotification:note];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
err( @"While merging changes:\n%@", [exception fullDescription] );
|
||||
}
|
||||
}];
|
||||
} );
|
||||
if ([self.mainManagedObjectContext respondsToSelector:@selector( automaticallyMergesChangesFromParent )]) // iOS 10+
|
||||
self.mainManagedObjectContext.automaticallyMergesChangesFromParent = YES;
|
||||
else
|
||||
// When privateManagedObjectContext is saved, import the changes into mainManagedObjectContext.
|
||||
PearlAddNotificationObserverTo( self.mainManagedObjectContext, NSManagedObjectContextDidSaveNotification,
|
||||
self.privateManagedObjectContext, nil, ^(NSManagedObjectContext *mainContext, NSNotification *note) {
|
||||
[mainContext performBlock:^{
|
||||
@try {
|
||||
[mainContext mergeChangesFromContextDidSaveNotification:note];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
err( @"While merging changes:\n%@", [exception fullDescription] );
|
||||
}
|
||||
}];
|
||||
} );
|
||||
|
||||
|
||||
// Create a new store coordinator.
|
||||
@@ -216,7 +237,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
NSURL *localStoreURL = [self localStoreURL];
|
||||
if (![[NSFileManager defaultManager] createDirectoryAtURL:[localStoreURL URLByDeletingLastPathComponent]
|
||||
withIntermediateDirectories:YES attributes:nil error:&error]) {
|
||||
err( @"Couldn't create our application support directory: %@", [error fullDescription] );
|
||||
MPError( error, @"Couldn't create our application support directory." );
|
||||
return;
|
||||
}
|
||||
if (![self.storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self localStoreURL]
|
||||
@@ -225,7 +246,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
NSInferMappingModelAutomaticallyOption : @YES,
|
||||
STORE_OPTIONS
|
||||
} error:&error]) {
|
||||
err( @"Failed to open store: %@", [error fullDescription] );
|
||||
MPError( error, @"Failed to open store." );
|
||||
self.storeCorrupted = @YES;
|
||||
[self handleCoordinatorError:error];
|
||||
return;
|
||||
@@ -234,12 +255,15 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
PearlAddNotificationObserver( UIApplicationWillResignActiveNotification, UIApp, [NSOperationQueue mainQueue],
|
||||
^(MPAppDelegate_Shared *self, NSNotification *note) {
|
||||
[self.mainManagedObjectContext saveToStore];
|
||||
} );
|
||||
#else
|
||||
PearlAddNotificationObserver( NSApplicationWillResignActiveNotification, NSApp, [NSOperationQueue mainQueue],
|
||||
#endif
|
||||
^(MPAppDelegate_Shared *self, NSNotification *note) {
|
||||
[self.mainManagedObjectContext saveToStore];
|
||||
} );
|
||||
[self.mainManagedObjectContext saveToStore];
|
||||
} );
|
||||
#endif
|
||||
|
||||
// Perform a data sanity check on the newly loaded store to find and fix any issues.
|
||||
if ([[MPConfig get].checkInconsistency boolValue])
|
||||
@@ -265,10 +289,10 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
NSError *error = nil;
|
||||
for (NSPersistentStore *store in self.storeCoordinator.persistentStores) {
|
||||
if (![self.storeCoordinator removePersistentStore:store error:&error])
|
||||
err( @"Couldn't remove persistence store from coordinator: %@", [error fullDescription] );
|
||||
MPError( error, @"Couldn't remove persistence store from coordinator." );
|
||||
}
|
||||
if (![[NSFileManager defaultManager] removeItemAtURL:self.localStoreURL error:&error])
|
||||
err( @"Couldn't remove persistence store at URL %@: %@", self.localStoreURL, [error fullDescription] );
|
||||
MPError( error, @"Couldn't remove persistence store at URL %@.", self.localStoreURL );
|
||||
|
||||
[self loadStore];
|
||||
}
|
||||
@@ -286,7 +310,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
fetchRequest.entity = entity;
|
||||
NSArray *objects = [context executeFetchRequest:fetchRequest error:&error];
|
||||
if (!objects) {
|
||||
err( @"Failed to fetch %@ objects: %@", entity, [error fullDescription] );
|
||||
MPError( error, @"Failed to fetch %@ objects.", entity );
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -311,7 +335,8 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
|
||||
- (void)migrateStore {
|
||||
|
||||
MPStoreMigrationLevel migrationLevel = (signed)[[NSUserDefaults standardUserDefaults] integerForKey:MPMigrationLevelLocalStoreKey];
|
||||
MPStoreMigrationLevel migrationLevel = (MPStoreMigrationLevel)
|
||||
[[NSUserDefaults standardUserDefaults] integerForKey:MPMigrationLevelLocalStoreKey];
|
||||
if (migrationLevel >= MPStoreMigrationLevelCurrent)
|
||||
// Local store up-to-date.
|
||||
return;
|
||||
@@ -353,8 +378,8 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
NSError *error = nil;
|
||||
if (![NSPersistentStore migrateStore:oldLocalStoreURL withOptions:@{ STORE_OPTIONS }
|
||||
toStore:newLocalStoreURL withOptions:@{ STORE_OPTIONS }
|
||||
model:nil error:&error]) {
|
||||
err( @"Couldn't migrate the old store to the new location: %@", [error fullDescription] );
|
||||
error:&error]) {
|
||||
MPError( error, @"Couldn't migrate the old store to the new location." );
|
||||
return NO;
|
||||
}
|
||||
|
||||
@@ -400,14 +425,52 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
NSMigratePersistentStoresAutomaticallyOption: @YES,
|
||||
NSInferMappingModelAutomaticallyOption : @YES,
|
||||
STORE_OPTIONS
|
||||
} model:nil error:&error]) {
|
||||
err( @"Couldn't migrate the old store to the new location: %@", [error fullDescription] );
|
||||
} error:&error]) {
|
||||
MPError( error, @"Couldn't migrate the old store to the new location." );
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
//- (BOOL)migrateV3LocalStore {
|
||||
//
|
||||
// inf( @"Migrating V3 local store" );
|
||||
// NSURL *localStoreURL = [self localStoreURL];
|
||||
// if (![[NSFileManager defaultManager] fileExistsAtPath:localStoreURL.path isDirectory:NULL]) {
|
||||
// inf( @"No V3 local store to migrate." );
|
||||
// return YES;
|
||||
// }
|
||||
//
|
||||
// NSError *error = nil;
|
||||
// NSDictionary<NSString *, id> *metadata = [NSPersistentStore metadataForPersistentStoreWithURL:localStoreURL error:&error];
|
||||
// if (!metadata) {
|
||||
// MPError( error, @"Couldn't inspect metadata for store: %@", localStoreURL );
|
||||
// return NO;
|
||||
// }
|
||||
// NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:
|
||||
// [NSManagedObjectModel mergedModelFromBundles:nil forStoreMetadata:metadata]];
|
||||
// if (![coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil
|
||||
// URL:localStoreURL options:@{ STORE_OPTIONS }
|
||||
// error:&error]) {
|
||||
// MPError( error, @"Couldn't open V3 local store to migrate." );
|
||||
// return NO;
|
||||
// }
|
||||
//
|
||||
// NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
|
||||
// [context performBlockAndWait:^{
|
||||
// context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
|
||||
// context.persistentStoreCoordinator = coordinator;
|
||||
// for (MPStoredSiteEntity *storedSite in [[MPStoredSiteEntity fetchRequest] execute:&error]) {
|
||||
// id contentObject = [storedSite valueForKey:@"contentObject"];
|
||||
// if ([contentObject isKindOfClass:[NSData class]])
|
||||
// storedSite.contentObject = contentObject;
|
||||
// }
|
||||
// }];
|
||||
//
|
||||
// return YES;
|
||||
//}
|
||||
|
||||
#pragma mark - Utilities
|
||||
|
||||
- (void)addSiteNamed:(NSString *)siteName completion:(void ( ^ )(MPSiteEntity *site, NSManagedObjectContext *context))completion {
|
||||
@@ -436,10 +499,6 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
site.lastUsed = [NSDate date];
|
||||
site.algorithm = algorithm;
|
||||
|
||||
NSError *error = nil;
|
||||
if (site.objectID.isTemporaryID && ![context obtainPermanentIDsForObjects:@[ site ] error:&error])
|
||||
err( @"Failed to obtain a permanent object ID after creating new site: %@", [error fullDescription] );
|
||||
|
||||
[context saveToStore];
|
||||
|
||||
completion( site, context );
|
||||
@@ -468,18 +527,14 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
newSite.algorithm = site.algorithm;
|
||||
newSite.loginName = site.loginName;
|
||||
|
||||
NSError *error = nil;
|
||||
if (![context obtainPermanentIDsForObjects:@[ newSite ] error:&error])
|
||||
err( @"Failed to obtain a permanent object ID after changing object type: %@", [error fullDescription] );
|
||||
|
||||
[context deleteObject:site];
|
||||
[context saveToStore];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.objectID];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.permanentObjectID];
|
||||
site = newSite;
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.objectID];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.permanentObjectID];
|
||||
return site;
|
||||
}
|
||||
|
||||
@@ -516,7 +571,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
initWithPattern:@"^#[[:space:]]*([^:]+): (.*)"
|
||||
options:(NSRegularExpressionOptions)0 error:&error];
|
||||
if (error) {
|
||||
err( @"Error loading the header pattern: %@", [error fullDescription] );
|
||||
MPError( error, @"Error loading the header pattern." );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
}
|
||||
@@ -530,19 +585,20 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
options:(NSRegularExpressionOptions)0 error:&error]
|
||||
];
|
||||
if (error) {
|
||||
err( @"Error loading the site patterns: %@", [error fullDescription] );
|
||||
MPError( error, @"Error loading the site patterns." );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse import data.
|
||||
inf( @"Importing sites." );
|
||||
__block MPUserEntity *user = nil;
|
||||
id<MPAlgorithm> importAlgorithm = nil;
|
||||
NSUInteger importFormat = 0;
|
||||
__block MPUserEntity *user = nil;
|
||||
NSUInteger importAvatar = NSNotFound;
|
||||
NSString *importBundleVersion = nil, *importUserName = nil;
|
||||
NSData *importKeyID = nil;
|
||||
NSString *importBundleVersion = nil, *importUserName = nil;
|
||||
id<MPAlgorithm> importAlgorithm = nil;
|
||||
MPSiteType importDefaultType = (MPSiteType)0;
|
||||
BOOL headerStarted = NO, headerEnded = NO, clearText = NO;
|
||||
NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
||||
NSMutableSet *sitesToDelete = [NSMutableSet set];
|
||||
@@ -573,14 +629,22 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
range:NSMakeRange( 0, [importedSiteLine length] )] lastObject];
|
||||
NSString *headerName = [importedSiteLine substringWithRange:[headerSites rangeAtIndex:1]];
|
||||
NSString *headerValue = [importedSiteLine substringWithRange:[headerSites rangeAtIndex:2]];
|
||||
if ([headerName isEqualToString:@"User Name"]) {
|
||||
|
||||
if ([headerName isEqualToString:@"Format"]) {
|
||||
importFormat = (NSUInteger)[headerValue integerValue];
|
||||
if (importFormat >= [sitePatterns count]) {
|
||||
err( @"Unsupported import format: %lu", (unsigned long)importFormat );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
}
|
||||
if (([headerName isEqualToString:@"User Name"] || [headerName isEqualToString:@"Full Name"]) && !importUserName) {
|
||||
importUserName = headerValue;
|
||||
|
||||
NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
|
||||
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", importUserName];
|
||||
NSArray *users = [context executeFetchRequest:userFetchRequest error:&error];
|
||||
if (!users) {
|
||||
err( @"While looking for user: %@, error: %@", importUserName, [error fullDescription] );
|
||||
MPError( error, @"While looking for user: %@.", importUserName );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
if ([users count] > 1) {
|
||||
@@ -591,21 +655,18 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
user = [users lastObject];
|
||||
dbg( @"Existing user? %@", [user debugDescription] );
|
||||
}
|
||||
if ([headerName isEqualToString:@"Avatar"])
|
||||
importAvatar = (NSUInteger)[headerValue integerValue];
|
||||
if ([headerName isEqualToString:@"Key ID"])
|
||||
importKeyID = [headerValue decodeHex];
|
||||
if ([headerName isEqualToString:@"Version"]) {
|
||||
importBundleVersion = headerValue;
|
||||
importAlgorithm = MPAlgorithmDefaultForBundleVersion( importBundleVersion );
|
||||
}
|
||||
if ([headerName isEqualToString:@"Format"]) {
|
||||
importFormat = (NSUInteger)[headerValue integerValue];
|
||||
if (importFormat >= [sitePatterns count]) {
|
||||
err( @"Unsupported import format: %lu", (unsigned long)importFormat );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
}
|
||||
if ([headerName isEqualToString:@"Avatar"])
|
||||
importAvatar = (NSUInteger)[headerValue integerValue];
|
||||
if ([headerName isEqualToString:@"Algorithm"])
|
||||
importAlgorithm = MPAlgorithmForVersion( (MPAlgorithmVersion)[headerValue integerValue] );
|
||||
if ([headerName isEqualToString:@"Default Type"])
|
||||
importDefaultType = (MPSiteType)[headerValue integerValue];
|
||||
if ([headerName isEqualToString:@"Passwords"]) {
|
||||
if ([headerValue isEqualToString:@"VISIBLE"])
|
||||
clearText = YES;
|
||||
@@ -667,7 +728,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
siteFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", siteName, user];
|
||||
NSArray *existingSites = [context executeFetchRequest:siteFetchRequest error:&error];
|
||||
if (!existingSites) {
|
||||
err( @"Lookup of existing sites failed for site: %@, user: %@, error: %@", siteName, user.userID, [error fullDescription] );
|
||||
MPError( error, @"Lookup of existing sites failed for site: %@, user: %@.", siteName, user.userID );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
if ([existingSites count]) {
|
||||
@@ -709,6 +770,8 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
if (user) {
|
||||
if (importAvatar != NSNotFound)
|
||||
user.avatar = importAvatar;
|
||||
if (importDefaultType)
|
||||
user.defaultType = importDefaultType;
|
||||
dbg( @"Updating User: %@", [user debugDescription] );
|
||||
}
|
||||
else {
|
||||
@@ -716,6 +779,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
user.name = importUserName;
|
||||
user.algorithm = MPAlgorithmDefault;
|
||||
user.keyID = [userKey keyIDForAlgorithm:user.algorithm];
|
||||
user.defaultType = importDefaultType?: user.algorithm.defaultType;
|
||||
if (importAvatar != NSNotFound)
|
||||
user.avatar = importAvatar;
|
||||
dbg( @"Created User: %@", [user debugDescription] );
|
||||
@@ -785,16 +849,16 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
[export appendFormat:@"# Export of site names and stored passwords (unless device-private) encrypted with the master key.\n"];
|
||||
[export appendFormat:@"# \n"];
|
||||
[export appendFormat:@"##\n"];
|
||||
[export appendFormat:@"# Format: 1\n"];
|
||||
[export appendFormat:@"# Date: %@\n", [[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]]];
|
||||
[export appendFormat:@"# User Name: %@\n", activeUser.name];
|
||||
[export appendFormat:@"# Full Name: %@\n", activeUser.name];
|
||||
[export appendFormat:@"# Avatar: %lu\n", (unsigned long)activeUser.avatar];
|
||||
[export appendFormat:@"# Key ID: %@\n", [activeUser.keyID encodeHex]];
|
||||
[export appendFormat:@"# Date: %@\n", [[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]]];
|
||||
[export appendFormat:@"# Version: %@\n", [PearlInfoPlist get].CFBundleVersion];
|
||||
[export appendFormat:@"# Format: 1\n"];
|
||||
if (revealPasswords)
|
||||
[export appendFormat:@"# Passwords: VISIBLE\n"];
|
||||
else
|
||||
[export appendFormat:@"# Passwords: PROTECTED\n"];
|
||||
[export appendFormat:@"# Algorithm: %d\n", activeUser.algorithm.version];
|
||||
[export appendFormat:@"# Default Type: %d\n", activeUser.defaultType];
|
||||
[export appendFormat:@"# Passwords: %@\n", revealPasswords? @"VISIBLE": @"PROTECTED"];
|
||||
[export appendFormat:@"##\n"];
|
||||
[export appendFormat:@"#\n"];
|
||||
[export appendFormat:@"# Last Times Password Login\t Site\tSite\n"];
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
[self.defaults registerDefaults:@{
|
||||
NSStringFromSelector( @selector( askForReviews ) ) : @YES,
|
||||
|
||||
NSStringFromSelector( @selector( sendInfo ) ) : @NO,
|
||||
NSStringFromSelector( @selector( sendInfo ) ) : @YES,
|
||||
NSStringFromSelector( @selector( rememberLogin ) ) : @NO,
|
||||
NSStringFromSelector( @selector( hidePasswords ) ) : @NO,
|
||||
NSStringFromSelector( @selector( checkInconsistency ) ): @NO,
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MPSiteEntity.h"
|
||||
#import "MPStoredSiteEntity.h"
|
||||
#import "MPGeneratedSiteEntity.h"
|
||||
#import "MPUserEntity.h"
|
||||
#import "MPSiteEntity+CoreDataClass.h"
|
||||
#import "MPStoredSiteEntity+CoreDataClass.h"
|
||||
#import "MPGeneratedSiteEntity+CoreDataClass.h"
|
||||
#import "MPUserEntity+CoreDataClass.h"
|
||||
#import "MPAlgorithm.h"
|
||||
#import "MPFixable.h"
|
||||
|
||||
@@ -22,6 +22,12 @@
|
||||
|
||||
@end
|
||||
|
||||
@interface NSManagedObject(MP)
|
||||
|
||||
- (NSManagedObjectID *)permanentObjectID;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPSiteQuestionEntity(MP)
|
||||
|
||||
- (NSString *)resolveQuestionAnswerUsingKey:(MPKey *)key;
|
||||
|
||||
@@ -16,25 +16,41 @@
|
||||
- (BOOL)saveToStore {
|
||||
|
||||
__block BOOL success = YES;
|
||||
if ([self hasChanges]) {
|
||||
if ([self hasChanges])
|
||||
[self performBlockAndWait:^{
|
||||
@try {
|
||||
NSError *error = nil;
|
||||
if (!(success = [self save:&error]))
|
||||
err( @"While saving: %@", [error fullDescription] );
|
||||
MPError( error, @"While saving." );
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
success = NO;
|
||||
err( @"While saving: %@", [exception fullDescription] );
|
||||
err( @"While saving.\n%@", [exception fullDescription] );
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
return success && (!self.parentContext || [self.parentContext saveToStore]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSManagedObject(MP)
|
||||
|
||||
- (NSManagedObjectID *)permanentObjectID {
|
||||
|
||||
NSManagedObjectID *objectID = self.objectID;
|
||||
if ([objectID isTemporaryID]) {
|
||||
NSError *error = nil;
|
||||
if (![self.managedObjectContext obtainPermanentIDsForObjects:@[ self ] error:&error])
|
||||
MPError( error, @"Failed to obtain permanent object ID." );
|
||||
objectID = self.objectID;
|
||||
}
|
||||
|
||||
return objectID.isTemporaryID? nil: objectID;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPSiteQuestionEntity(MP)
|
||||
|
||||
- (NSString *)resolveQuestionAnswerUsingKey:(MPKey *)key {
|
||||
@@ -237,8 +253,8 @@
|
||||
// Invalid self.user.defaultType
|
||||
result = MPApplyFix( result, ^MPFixableResult {
|
||||
wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.",
|
||||
self.name, self.user.name, (long)self.type, (long)MPSiteTypeGeneratedLong );
|
||||
self.type = MPSiteTypeGeneratedLong;
|
||||
self.name, self.user.name, (long)self.type, (long)[self.algorithm defaultType] );
|
||||
self.type = [self.algorithm defaultType];
|
||||
return MPFixableResultProblemsFixed;
|
||||
} );
|
||||
if (![self isKindOfClass:[self.algorithm classOfType:self.type]])
|
||||
@@ -330,7 +346,7 @@
|
||||
|
||||
- (MPSiteType)defaultType {
|
||||
|
||||
return (MPSiteType)[self.defaultType_ unsignedIntegerValue]?: MPSiteTypeGeneratedLong;
|
||||
return (MPSiteType)[self.defaultType_ unsignedIntegerValue]?: self.algorithm.defaultType;
|
||||
}
|
||||
|
||||
- (void)setDefaultType:(MPSiteType)aDefaultType {
|
||||
|
||||
20
platform-darwin/Source/MPGeneratedSiteEntity+CoreDataClass.h
Normal file
20
platform-darwin/Source/MPGeneratedSiteEntity+CoreDataClass.h
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// MPGeneratedSiteEntity+CoreDataClass.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MPSiteEntity+CoreDataClass.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPGeneratedSiteEntity : MPSiteEntity
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#import "MPGeneratedSiteEntity+CoreDataProperties.h"
|
||||
13
platform-darwin/Source/MPGeneratedSiteEntity+CoreDataClass.m
Normal file
13
platform-darwin/Source/MPGeneratedSiteEntity+CoreDataClass.m
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// MPGeneratedSiteEntity+CoreDataClass.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPGeneratedSiteEntity+CoreDataClass.h"
|
||||
|
||||
@implementation MPGeneratedSiteEntity
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// MPGeneratedSiteEntity+CoreDataProperties.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPGeneratedSiteEntity+CoreDataClass.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPGeneratedSiteEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPGeneratedSiteEntity *> *)fetchRequest;
|
||||
|
||||
@property (nullable, nonatomic, copy) NSNumber *counter_;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// MPGeneratedSiteEntity+CoreDataProperties.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPGeneratedSiteEntity+CoreDataProperties.h"
|
||||
|
||||
@implementation MPGeneratedSiteEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPGeneratedSiteEntity *> *)fetchRequest {
|
||||
return [[NSFetchRequest alloc] initWithEntityName:@"MPGeneratedSiteEntity"];
|
||||
}
|
||||
|
||||
@dynamic counter_;
|
||||
|
||||
@end
|
||||
@@ -1,17 +0,0 @@
|
||||
//
|
||||
// MPGeneratedSiteEntity.h
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
#import "MPSiteEntity.h"
|
||||
|
||||
@interface MPGeneratedSiteEntity : MPSiteEntity
|
||||
|
||||
@property(nonatomic, retain) NSNumber *counter_;
|
||||
|
||||
@end
|
||||
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// MPGeneratedSiteEntity.m
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPGeneratedSiteEntity.h"
|
||||
|
||||
@implementation MPGeneratedSiteEntity
|
||||
|
||||
@dynamic counter_;
|
||||
|
||||
@end
|
||||
@@ -28,12 +28,12 @@ typedef NS_ENUM( NSUInteger, MPKeyOrigin ) {
|
||||
|
||||
@interface MPKey : NSObject
|
||||
|
||||
@property(nonatomic, readonly) NSString *fullName;
|
||||
@property(nonatomic, readonly) MPKeyOrigin origin;
|
||||
@property(nonatomic, readonly, copy) NSString *fullName;
|
||||
|
||||
- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword;
|
||||
- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData
|
||||
forAlgorithm:(id<MPAlgorithm>)algorithm keyOrigin:(MPKeyOrigin)origin;
|
||||
- (instancetype)initForFullName:(NSString *)fullName withKeyResolver:(NSData *( ^ )(id<MPAlgorithm>))keyResolver
|
||||
keyOrigin:(MPKeyOrigin)origin;
|
||||
|
||||
- (NSData *)keyIDForAlgorithm:(id<MPAlgorithm>)algorithm;
|
||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm;
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
|
||||
@interface MPKey()
|
||||
|
||||
@property(nonatomic) NSString *fullName;
|
||||
@property(nonatomic) MPKeyOrigin origin;
|
||||
@property(nonatomic) NSString *masterPassword;
|
||||
@property(nonatomic, copy) NSString *fullName;
|
||||
@property(nonatomic, copy) NSData *( ^keyResolver )(id<MPAlgorithm>);
|
||||
|
||||
@end
|
||||
|
||||
@@ -31,25 +31,22 @@
|
||||
|
||||
- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword {
|
||||
|
||||
return [self initForFullName:fullName withKeyResolver:^NSData *(id<MPAlgorithm> algorithm) {
|
||||
return [algorithm keyDataForFullName:self.fullName withMasterPassword:masterPassword];
|
||||
} keyOrigin:MPKeyOriginMasterPassword];
|
||||
}
|
||||
|
||||
- (instancetype)initForFullName:(NSString *)fullName withKeyResolver:(NSData *( ^ )(id<MPAlgorithm>))keyResolver
|
||||
keyOrigin:(MPKeyOrigin)origin {
|
||||
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
_keyCache = [NSCache new];
|
||||
self.fullName = fullName;
|
||||
self.origin = MPKeyOriginMasterPassword;
|
||||
self.masterPassword = masterPassword;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData
|
||||
forAlgorithm:(id<MPAlgorithm>)algorithm keyOrigin:(MPKeyOrigin)origin {
|
||||
|
||||
if (!(self = [self initForFullName:fullName withMasterPassword:nil]))
|
||||
return nil;
|
||||
|
||||
self.origin = origin;
|
||||
[_keyCache setObject:keyData forKey:algorithm];
|
||||
self.fullName = fullName;
|
||||
self.keyResolver = keyResolver;
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -61,15 +58,17 @@
|
||||
|
||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm {
|
||||
|
||||
NSData *keyData = [_keyCache objectForKey:algorithm];
|
||||
if (keyData)
|
||||
@synchronized (self) {
|
||||
NSData *keyData = [_keyCache objectForKey:algorithm];
|
||||
if (keyData)
|
||||
return keyData;
|
||||
|
||||
keyData = self.keyResolver( algorithm );
|
||||
if (keyData)
|
||||
[_keyCache setObject:keyData forKey:algorithm];
|
||||
|
||||
return keyData;
|
||||
|
||||
keyData = [algorithm keyDataForFullName:self.fullName withMasterPassword:self.masterPassword];
|
||||
if (keyData)
|
||||
[_keyCache setObject:keyData forKey:algorithm];
|
||||
|
||||
return keyData;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm trimmedLength:(NSUInteger)subKeyLength {
|
||||
@@ -80,7 +79,7 @@
|
||||
|
||||
- (BOOL)isEqualToKey:(MPKey *)key {
|
||||
|
||||
return [self.fullName isEqualToString:key.fullName] && [self.masterPassword isEqualToString:self.masterPassword];
|
||||
return [[self keyIDForAlgorithm:MPAlgorithmDefault] isEqualToData:[key keyIDForAlgorithm:MPAlgorithmDefault]];
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
|
||||
22
platform-darwin/Source/MPSiteEntity+CoreDataClass.h
Normal file
22
platform-darwin/Source/MPSiteEntity+CoreDataClass.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// MPSiteEntity+CoreDataClass.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class MPSiteQuestionEntity, MPUserEntity, NSObject;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPSiteEntity : NSManagedObject
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#import "MPSiteEntity+CoreDataProperties.h"
|
||||
16
platform-darwin/Source/MPSiteEntity+CoreDataClass.m
Normal file
16
platform-darwin/Source/MPSiteEntity+CoreDataClass.m
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// MPSiteEntity+CoreDataClass.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteEntity+CoreDataClass.h"
|
||||
#import "MPSiteQuestionEntity+CoreDataClass.h"
|
||||
|
||||
#import "MPUserEntity+CoreDataClass.h"
|
||||
|
||||
@implementation MPSiteEntity
|
||||
|
||||
@end
|
||||
48
platform-darwin/Source/MPSiteEntity+CoreDataProperties.h
Normal file
48
platform-darwin/Source/MPSiteEntity+CoreDataProperties.h
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// MPSiteEntity+CoreDataProperties.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteEntity+CoreDataClass.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPSiteEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPSiteEntity *> *)fetchRequest;
|
||||
|
||||
@property (nullable, nonatomic, retain) NSObject *content;
|
||||
@property (nullable, nonatomic, copy) NSDate *lastUsed;
|
||||
@property (nullable, nonatomic, copy) NSNumber *loginGenerated_;
|
||||
@property (nullable, nonatomic, copy) NSString *loginName;
|
||||
@property (nullable, nonatomic, copy) NSString *name;
|
||||
@property (nullable, nonatomic, copy) NSNumber *requiresExplicitMigration_;
|
||||
@property (nullable, nonatomic, copy) NSNumber *type_;
|
||||
@property (nullable, nonatomic, copy) NSNumber *uses_;
|
||||
@property (nullable, nonatomic, copy) NSNumber *version_;
|
||||
@property (nullable, nonatomic, copy) NSString *url;
|
||||
@property (nullable, nonatomic, retain) NSOrderedSet<MPSiteQuestionEntity *> *questions;
|
||||
@property (nullable, nonatomic, retain) MPUserEntity *user;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPSiteEntity (CoreDataGeneratedAccessors)
|
||||
|
||||
- (void)insertObject:(MPSiteQuestionEntity *)value inQuestionsAtIndex:(NSUInteger)idx;
|
||||
- (void)removeObjectFromQuestionsAtIndex:(NSUInteger)idx;
|
||||
- (void)insertQuestions:(NSArray<MPSiteQuestionEntity *> *)value atIndexes:(NSIndexSet *)indexes;
|
||||
- (void)removeQuestionsAtIndexes:(NSIndexSet *)indexes;
|
||||
- (void)replaceObjectInQuestionsAtIndex:(NSUInteger)idx withObject:(MPSiteQuestionEntity *)value;
|
||||
- (void)replaceQuestionsAtIndexes:(NSIndexSet *)indexes withQuestions:(NSArray<MPSiteQuestionEntity *> *)values;
|
||||
- (void)addQuestionsObject:(MPSiteQuestionEntity *)value;
|
||||
- (void)removeQuestionsObject:(MPSiteQuestionEntity *)value;
|
||||
- (void)addQuestions:(NSOrderedSet<MPSiteQuestionEntity *> *)values;
|
||||
- (void)removeQuestions:(NSOrderedSet<MPSiteQuestionEntity *> *)values;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
30
platform-darwin/Source/MPSiteEntity+CoreDataProperties.m
Normal file
30
platform-darwin/Source/MPSiteEntity+CoreDataProperties.m
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// MPSiteEntity+CoreDataProperties.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteEntity+CoreDataProperties.h"
|
||||
|
||||
@implementation MPSiteEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPSiteEntity *> *)fetchRequest {
|
||||
return [[NSFetchRequest alloc] initWithEntityName:@"MPSiteEntity"];
|
||||
}
|
||||
|
||||
@dynamic content;
|
||||
@dynamic lastUsed;
|
||||
@dynamic loginGenerated_;
|
||||
@dynamic loginName;
|
||||
@dynamic name;
|
||||
@dynamic requiresExplicitMigration_;
|
||||
@dynamic type_;
|
||||
@dynamic uses_;
|
||||
@dynamic version_;
|
||||
@dynamic url;
|
||||
@dynamic questions;
|
||||
@dynamic user;
|
||||
|
||||
@end
|
||||
@@ -1,41 +0,0 @@
|
||||
//
|
||||
// MPSiteEntity.h
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class MPSiteQuestionEntity, MPUserEntity;
|
||||
|
||||
@interface MPSiteEntity : NSManagedObject
|
||||
|
||||
//@property (nonatomic, retain) id content; // Hide here, reveal in MPStoredSiteEntity
|
||||
@property(nonatomic, retain) NSDate *lastUsed;
|
||||
@property(nonatomic, retain) NSNumber *loginGenerated_;
|
||||
@property(nonatomic, retain) NSString *loginName;
|
||||
@property(nonatomic, retain) NSString *name;
|
||||
@property(nonatomic, retain) NSNumber *requiresExplicitMigration_;
|
||||
@property(nonatomic, retain) NSNumber *type_;
|
||||
@property(nonatomic, retain) NSNumber *uses_;
|
||||
@property(nonatomic, retain) NSNumber *version_;
|
||||
@property(nonatomic, retain) NSOrderedSet *questions;
|
||||
@property(nonatomic, retain) MPUserEntity *user;
|
||||
@end
|
||||
|
||||
@interface MPSiteEntity(CoreDataGeneratedAccessors)
|
||||
|
||||
- (void)insertObject:(MPSiteQuestionEntity *)value inQuestionsAtIndex:(NSUInteger)idx;
|
||||
- (void)removeObjectFromQuestionsAtIndex:(NSUInteger)idx;
|
||||
- (void)insertQuestions:(NSArray *)value atIndexes:(NSIndexSet *)indexes;
|
||||
- (void)removeQuestionsAtIndexes:(NSIndexSet *)indexes;
|
||||
- (void)replaceObjectInQuestionsAtIndex:(NSUInteger)idx withObject:(MPSiteQuestionEntity *)value;
|
||||
- (void)replaceQuestionsAtIndexes:(NSIndexSet *)indexes withQuestions:(NSArray *)values;
|
||||
- (void)addQuestionsObject:(MPSiteQuestionEntity *)value;
|
||||
- (void)removeQuestionsObject:(MPSiteQuestionEntity *)value;
|
||||
- (void)addQuestions:(NSOrderedSet *)values;
|
||||
- (void)removeQuestions:(NSOrderedSet *)values;
|
||||
@end
|
||||
@@ -1,27 +0,0 @@
|
||||
//
|
||||
// MPSiteEntity.m
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteEntity.h"
|
||||
#import "MPSiteQuestionEntity.h"
|
||||
#import "MPUserEntity.h"
|
||||
|
||||
@implementation MPSiteEntity
|
||||
|
||||
//@dynamic content;
|
||||
@dynamic lastUsed;
|
||||
@dynamic loginGenerated_;
|
||||
@dynamic loginName;
|
||||
@dynamic name;
|
||||
@dynamic requiresExplicitMigration_;
|
||||
@dynamic type_;
|
||||
@dynamic uses_;
|
||||
@dynamic version_;
|
||||
@dynamic questions;
|
||||
@dynamic user;
|
||||
|
||||
@end
|
||||
22
platform-darwin/Source/MPSiteQuestionEntity+CoreDataClass.h
Normal file
22
platform-darwin/Source/MPSiteQuestionEntity+CoreDataClass.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// MPSiteQuestionEntity+CoreDataClass.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class MPSiteEntity;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPSiteQuestionEntity : NSManagedObject
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#import "MPSiteQuestionEntity+CoreDataProperties.h"
|
||||
14
platform-darwin/Source/MPSiteQuestionEntity+CoreDataClass.m
Normal file
14
platform-darwin/Source/MPSiteQuestionEntity+CoreDataClass.m
Normal file
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// MPSiteQuestionEntity+CoreDataClass.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteQuestionEntity+CoreDataClass.h"
|
||||
#import "MPSiteEntity+CoreDataClass.h"
|
||||
|
||||
@implementation MPSiteQuestionEntity
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// MPSiteQuestionEntity+CoreDataProperties.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteQuestionEntity+CoreDataClass.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPSiteQuestionEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPSiteQuestionEntity *> *)fetchRequest;
|
||||
|
||||
@property (nullable, nonatomic, copy) NSString *keyword;
|
||||
@property (nullable, nonatomic, retain) MPSiteEntity *site;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// MPSiteQuestionEntity+CoreDataProperties.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteQuestionEntity+CoreDataProperties.h"
|
||||
|
||||
@implementation MPSiteQuestionEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPSiteQuestionEntity *> *)fetchRequest {
|
||||
return [[NSFetchRequest alloc] initWithEntityName:@"MPSiteQuestionEntity"];
|
||||
}
|
||||
|
||||
@dynamic keyword;
|
||||
@dynamic site;
|
||||
|
||||
@end
|
||||
@@ -1,19 +0,0 @@
|
||||
//
|
||||
// MPSiteQuestionEntity.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-27.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class MPSiteEntity;
|
||||
|
||||
@interface MPSiteQuestionEntity : NSManagedObject
|
||||
|
||||
@property(nonatomic, retain) NSString *keyword;
|
||||
@property(nonatomic, retain) MPSiteEntity *site;
|
||||
|
||||
@end
|
||||
@@ -1,17 +0,0 @@
|
||||
//
|
||||
// MPSiteQuestionEntity.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-27.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteQuestionEntity.h"
|
||||
#import "MPSiteEntity.h"
|
||||
|
||||
@implementation MPSiteQuestionEntity
|
||||
|
||||
@dynamic keyword;
|
||||
@dynamic site;
|
||||
|
||||
@end
|
||||
22
platform-darwin/Source/MPStoredSiteEntity+CoreDataClass.h
Normal file
22
platform-darwin/Source/MPStoredSiteEntity+CoreDataClass.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// MPStoredSiteEntity+CoreDataClass.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MPSiteEntity+CoreDataClass.h"
|
||||
|
||||
@class NSObject;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPStoredSiteEntity : MPSiteEntity
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#import "MPStoredSiteEntity+CoreDataProperties.h"
|
||||
13
platform-darwin/Source/MPStoredSiteEntity+CoreDataClass.m
Normal file
13
platform-darwin/Source/MPStoredSiteEntity+CoreDataClass.m
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// MPStoredSiteEntity+CoreDataClass.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPStoredSiteEntity+CoreDataClass.h"
|
||||
|
||||
@implementation MPStoredSiteEntity
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// MPStoredSiteEntity+CoreDataProperties.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-05-01.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPStoredSiteEntity+CoreDataClass.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPStoredSiteEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPStoredSiteEntity *> *)fetchRequest;
|
||||
|
||||
@property (nullable, nonatomic, retain) NSData *contentObject;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// MPStoredSiteEntity+CoreDataProperties.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-05-01.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPStoredSiteEntity+CoreDataProperties.h"
|
||||
|
||||
@implementation MPStoredSiteEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPStoredSiteEntity *> *)fetchRequest {
|
||||
return [[NSFetchRequest alloc] initWithEntityName:@"MPStoredSiteEntity"];
|
||||
}
|
||||
|
||||
@dynamic contentObject;
|
||||
|
||||
@end
|
||||
@@ -1,17 +0,0 @@
|
||||
//
|
||||
// MPStoredSiteEntity.h
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
#import "MPSiteEntity.h"
|
||||
|
||||
@interface MPStoredSiteEntity : MPSiteEntity
|
||||
|
||||
@property(nonatomic, retain) NSData *contentObject;
|
||||
|
||||
@end
|
||||
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// MPStoredSiteEntity.m
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPStoredSiteEntity.h"
|
||||
|
||||
@implementation MPStoredSiteEntity
|
||||
|
||||
@dynamic contentObject;
|
||||
|
||||
@end
|
||||
@@ -6,8 +6,12 @@
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Crashlytics/Crashlytics.h>
|
||||
#import <Crashlytics/Answers.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
extern NSString *const MPErrorDomain;
|
||||
extern NSInteger const MPErrorHangCode;
|
||||
|
||||
extern NSString *const MPSignedInNotification;
|
||||
extern NSString *const MPSignedOutNotification;
|
||||
@@ -19,4 +23,21 @@ extern NSString *const MPFoundInconsistenciesNotification;
|
||||
|
||||
extern NSString *const MPSitesImportedNotificationUserKey;
|
||||
extern NSString *const MPInconsistenciesFixResultUserKey;
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#ifdef CRASHLYTICS
|
||||
#define MPError(error_, message, ...) ({ \
|
||||
err( message @"%@%@", ##__VA_ARGS__, error_? @"\n": @"", [error_ fullDescription]?: @"" ); \
|
||||
\
|
||||
if ([[MPConfig get].sendInfo boolValue]) { \
|
||||
[[Crashlytics sharedInstance] recordError:error_ withAdditionalUserInfo:@{ \
|
||||
@"location": strf( @"%@:%d %@", @(basename((char *)__FILE__)), __LINE__, NSStringFromSelector(_cmd) ), \
|
||||
}]; \
|
||||
} \
|
||||
})
|
||||
#else
|
||||
#define MPError(error_, ...) ({ \
|
||||
err( message @"%@%@", ##__VA_ARGS__, error_? @"\n": @"", [error_ fullDescription]?: @"" ); \
|
||||
})
|
||||
#endif
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#import "MPTypes.h"
|
||||
|
||||
NSString *const MPErrorDomain = @"MPErrorDomain";
|
||||
NSInteger const MPErrorHangCode = 1;
|
||||
|
||||
NSString *const MPSignedInNotification = @"MPSignedInNotification";
|
||||
NSString *const MPSignedOutNotification = @"MPSignedOutNotification";
|
||||
|
||||
22
platform-darwin/Source/MPUserEntity+CoreDataClass.h
Normal file
22
platform-darwin/Source/MPUserEntity+CoreDataClass.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// MPUserEntity+CoreDataClass.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class MPSiteEntity;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPUserEntity : NSManagedObject
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#import "MPUserEntity+CoreDataProperties.h"
|
||||
14
platform-darwin/Source/MPUserEntity+CoreDataClass.m
Normal file
14
platform-darwin/Source/MPUserEntity+CoreDataClass.m
Normal file
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// MPUserEntity+CoreDataClass.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPUserEntity+CoreDataClass.h"
|
||||
#import "MPSiteEntity+CoreDataClass.h"
|
||||
|
||||
@implementation MPUserEntity
|
||||
|
||||
@end
|
||||
39
platform-darwin/Source/MPUserEntity+CoreDataProperties.h
Normal file
39
platform-darwin/Source/MPUserEntity+CoreDataProperties.h
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// MPUserEntity+CoreDataProperties.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPUserEntity+CoreDataClass.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPUserEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPUserEntity *> *)fetchRequest;
|
||||
|
||||
@property (nullable, nonatomic, copy) NSNumber *avatar_;
|
||||
@property (nullable, nonatomic, copy) NSNumber *defaultType_;
|
||||
@property (nullable, nonatomic, retain) NSData *keyID;
|
||||
@property (nullable, nonatomic, copy) NSDate *lastUsed;
|
||||
@property (nullable, nonatomic, copy) NSString *name;
|
||||
@property (nullable, nonatomic, copy) NSNumber *saveKey_;
|
||||
@property (nullable, nonatomic, copy) NSNumber *touchID_;
|
||||
@property (nullable, nonatomic, copy) NSNumber *version_;
|
||||
@property (nullable, nonatomic, retain) NSSet<MPSiteEntity *> *sites;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPUserEntity (CoreDataGeneratedAccessors)
|
||||
|
||||
- (void)addSitesObject:(MPSiteEntity *)value;
|
||||
- (void)removeSitesObject:(MPSiteEntity *)value;
|
||||
- (void)addSites:(NSSet<MPSiteEntity *> *)values;
|
||||
- (void)removeSites:(NSSet<MPSiteEntity *> *)values;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
27
platform-darwin/Source/MPUserEntity+CoreDataProperties.m
Normal file
27
platform-darwin/Source/MPUserEntity+CoreDataProperties.m
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// MPUserEntity+CoreDataProperties.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPUserEntity+CoreDataProperties.h"
|
||||
|
||||
@implementation MPUserEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPUserEntity *> *)fetchRequest {
|
||||
return [[NSFetchRequest alloc] initWithEntityName:@"MPUserEntity"];
|
||||
}
|
||||
|
||||
@dynamic avatar_;
|
||||
@dynamic defaultType_;
|
||||
@dynamic keyID;
|
||||
@dynamic lastUsed;
|
||||
@dynamic name;
|
||||
@dynamic saveKey_;
|
||||
@dynamic touchID_;
|
||||
@dynamic version_;
|
||||
@dynamic sites;
|
||||
|
||||
@end
|
||||
@@ -1,34 +0,0 @@
|
||||
//
|
||||
// MPUserEntity.h
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class MPSiteEntity;
|
||||
|
||||
@interface MPUserEntity : NSManagedObject
|
||||
|
||||
@property(nonatomic, retain) NSNumber *avatar_;
|
||||
@property(nonatomic, retain) NSNumber *defaultType_;
|
||||
@property(nonatomic, retain) NSData *keyID;
|
||||
@property(nonatomic, retain) NSDate *lastUsed;
|
||||
@property(nonatomic, retain) NSString *name;
|
||||
@property(nonatomic, retain) NSNumber *saveKey_;
|
||||
@property(nonatomic, retain) NSNumber *touchID_;
|
||||
@property(nonatomic, retain) NSNumber *version_;
|
||||
@property(nonatomic, retain) NSSet *sites;
|
||||
@end
|
||||
|
||||
@interface MPUserEntity(CoreDataGeneratedAccessors)
|
||||
|
||||
- (void)addSitesObject:(MPSiteEntity *)value;
|
||||
- (void)removeSitesObject:(MPSiteEntity *)value;
|
||||
- (void)addSites:(NSSet *)values;
|
||||
- (void)removeSites:(NSSet *)values;
|
||||
|
||||
@end
|
||||
@@ -1,24 +0,0 @@
|
||||
//
|
||||
// MPUserEntity.m
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPUserEntity.h"
|
||||
#import "MPSiteEntity.h"
|
||||
|
||||
@implementation MPUserEntity
|
||||
|
||||
@dynamic avatar_;
|
||||
@dynamic defaultType_;
|
||||
@dynamic keyID;
|
||||
@dynamic lastUsed;
|
||||
@dynamic name;
|
||||
@dynamic saveKey_;
|
||||
@dynamic touchID_;
|
||||
@dynamic version_;
|
||||
@dynamic sites;
|
||||
|
||||
@end
|
||||
@@ -28,10 +28,10 @@
|
||||
|
||||
[super windowDidLoad];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window
|
||||
queue:nil usingBlock:^(NSNotification *note) {
|
||||
[MPMacAppDelegate get].initialWindowController = nil;
|
||||
}];
|
||||
PearlAddNotificationObserver( NSWindowWillCloseNotification, self.window, nil, ^(id host, NSNotification *note) {
|
||||
PearlRemoveNotificationObserversFrom( host );
|
||||
[MPMacAppDelegate get].initialWindowController = nil;
|
||||
} );
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
@@ -72,13 +72,12 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
NSString *crashlyticsAPIKey = [self crashlyticsAPIKey];
|
||||
if ([crashlyticsAPIKey length]) {
|
||||
inf(@"Initializing Crashlytics");
|
||||
#if defined (DEBUG) || defined (ADHOC)
|
||||
#if DEBUG
|
||||
[Crashlytics sharedInstance].debugMode = YES;
|
||||
#endif
|
||||
[[Crashlytics sharedInstance] setUserIdentifier:[PearlKeyChain deviceIdentifier]];
|
||||
[[Crashlytics sharedInstance] setObjectValue:[PearlKeyChain deviceIdentifier] forKey:@"deviceIdentifier"];
|
||||
[[Crashlytics sharedInstance] setUserName:@"Anonymous"];
|
||||
[[Crashlytics sharedInstance] setObjectValue:@"Anonymous" forKey:@"username"];
|
||||
[Crashlytics startWithAPIKey:crashlyticsAPIKey];
|
||||
[[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
|
||||
PearlLogLevel level = PearlLogLevelInfo;
|
||||
@@ -172,17 +171,17 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
|
||||
// Save changes in the application's managed object context before the application terminates.
|
||||
|
||||
NSManagedObjectContext *context = [MPMacAppDelegate managedObjectContextForMainThreadIfReady];
|
||||
if (!context)
|
||||
NSManagedObjectContext *mainContext = [MPMacAppDelegate managedObjectContextForMainThreadIfReady];
|
||||
if (!mainContext)
|
||||
return NSTerminateNow;
|
||||
|
||||
if (![context commitEditing])
|
||||
if (![mainContext commitEditing])
|
||||
return NSTerminateCancel;
|
||||
|
||||
if (![context hasChanges])
|
||||
if (![mainContext hasChanges])
|
||||
return NSTerminateNow;
|
||||
|
||||
[context saveToStore];
|
||||
[mainContext saveToStore];
|
||||
return NSTerminateNow;
|
||||
}
|
||||
|
||||
@@ -272,7 +271,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
[[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:
|
||||
^(NSData *importedSitesData, NSURLResponse *response, NSError *error) {
|
||||
if (error)
|
||||
err( @"While reading imported sites from %@: %@", url, [error fullDescription] );
|
||||
MPError( error, @"While reading imported sites from %@.", url );
|
||||
if (!importedSitesData)
|
||||
return;
|
||||
|
||||
@@ -389,20 +388,16 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
return;
|
||||
|
||||
NSString *name = [(NSSecureTextField *)alert.accessoryView stringValue];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPUserEntity *newUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass( [MPUserEntity class] )
|
||||
inManagedObjectContext:moc];
|
||||
inManagedObjectContext:context];
|
||||
newUser.name = name;
|
||||
[moc saveToStore];
|
||||
NSError *error = nil;
|
||||
if (![moc obtainPermanentIDsForObjects:@[ newUser ] error:&error])
|
||||
err( @"Failed to obtain permanent object ID for new user: %@", [error fullDescription] );
|
||||
[context saveToStore];
|
||||
[self setActiveUser:newUser];
|
||||
|
||||
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
|
||||
[self updateUsers];
|
||||
[self setActiveUser:newUser];
|
||||
PearlMainQueue( ^{
|
||||
[self showPasswordWindow:nil];
|
||||
}];
|
||||
} );
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -416,10 +411,10 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
if ([alert runModal] != NSAlertFirstButtonReturn)
|
||||
return;
|
||||
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||
[moc deleteObject:[self activeUserInContext:moc]];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
[context deleteObject:[self activeUserInContext:context]];
|
||||
[self setActiveUser:nil];
|
||||
[moc saveToStore];
|
||||
[context saveToStore];
|
||||
|
||||
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
|
||||
[self updateUsers];
|
||||
@@ -516,20 +511,23 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
|
||||
NSError *coordinateError = nil;
|
||||
NSString *exportedSites = [self exportSitesRevealPasswords:revealPasswords];
|
||||
[[[NSFileCoordinator alloc] initWithFilePresenter:nil] coordinateWritingItemAtURL:savePanel.URL options:0 error:&coordinateError
|
||||
byAccessor:^(NSURL *newURL) {
|
||||
NSError *writeError = nil;
|
||||
if (![exportedSites writeToURL:newURL atomically:NO
|
||||
encoding:NSUTF8StringEncoding
|
||||
error:&writeError])
|
||||
PearlMainQueue( ^{
|
||||
[[NSAlert alertWithError:writeError] runModal];
|
||||
} );
|
||||
}];
|
||||
if (coordinateError)
|
||||
[[[NSFileCoordinator alloc] initWithFilePresenter:nil] coordinateWritingItemAtURL:savePanel.URL options:0
|
||||
error:&coordinateError byAccessor:
|
||||
^(NSURL *newURL) {
|
||||
NSError *writeError = nil;
|
||||
if (![exportedSites writeToURL:newURL atomically:NO encoding:NSUTF8StringEncoding error:&writeError])
|
||||
MPError( writeError, @"Could not write to the export file." );
|
||||
|
||||
PearlMainQueue( ^{
|
||||
[[NSAlert alertWithError:writeError] runModal];
|
||||
} );
|
||||
}];
|
||||
if (coordinateError) {
|
||||
MPError( coordinateError, @"Write access to the export file could not be obtained." );
|
||||
PearlMainQueue( ^{
|
||||
[[NSAlert alertWithError:coordinateError] runModal];
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateUsers {
|
||||
@@ -567,7 +565,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
|
||||
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
|
||||
if (!users)
|
||||
err( @"Failed to load users: %@", [error fullDescription] );
|
||||
MPError( error, @"Failed to load users." );
|
||||
|
||||
if (![users count]) {
|
||||
NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""];
|
||||
@@ -579,7 +577,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
for (MPUserEntity *user in users) {
|
||||
NSMenuItem *userItem = [[NSMenuItem alloc] initWithTitle:user.name action:@selector( selectUser: ) keyEquivalent:@""];
|
||||
[userItem setTarget:self];
|
||||
[userItem setRepresentedObject:[user objectID]];
|
||||
[userItem setRepresentedObject:user.permanentObjectID];
|
||||
[[self.usersItem submenu] addItem:userItem];
|
||||
|
||||
if (!mainActiveUser && [user.name isEqualToString:[MPMacConfig get].usedUserName])
|
||||
|
||||
@@ -41,40 +41,38 @@
|
||||
[self replaceFonts:self.window.contentView];
|
||||
prof_rewind( @"replaceFonts" );
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidBecomeKeyNotification object:self.window
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
PearlAddNotificationObserver( NSWindowDidBecomeKeyNotification, self.window, [NSOperationQueue mainQueue],
|
||||
^(id host, NSNotification *note) {
|
||||
prof_new( @"didBecomeKey" );
|
||||
[self.window makeKeyAndOrderFront:nil];
|
||||
prof_rewind( @"fadeIn" );
|
||||
[self updateUser];
|
||||
prof_finish( @"updateUser" );
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
} );
|
||||
PearlAddNotificationObserver( NSWindowWillCloseNotification, self.window, [NSOperationQueue mainQueue],
|
||||
^(id host, NSNotification *note) {
|
||||
PearlRemoveNotificationObservers();
|
||||
|
||||
NSWindow *sheet = [self.window attachedSheet];
|
||||
if (sheet)
|
||||
[self.window endSheet:sheet];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillResignActiveNotification object:nil
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
} );
|
||||
PearlAddNotificationObserver( NSApplicationWillResignActiveNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(id host, NSNotification *note) {
|
||||
[self.window close];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:MPSignedInNotification object:nil
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
[self updateUser];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
[self updateUser];
|
||||
}];
|
||||
} );
|
||||
PearlAddNotificationObserver( MPSignedInNotification, nil, [NSOperationQueue mainQueue], ^(id host, NSNotification *note) {
|
||||
[self updateUser];
|
||||
} );
|
||||
PearlAddNotificationObserver( MPSignedOutNotification, nil, [NSOperationQueue mainQueue], ^(id host, NSNotification *note) {
|
||||
[self updateUser];
|
||||
} );
|
||||
[self observeKeyPath:@"sitesController.selection" withBlock:^(id from, id to, NSKeyValueChange cause, id _self) {
|
||||
prof_new( @"sitesController.selection" );
|
||||
[_self updateSelection];
|
||||
prof_finish( @"updateSelection" );
|
||||
}];
|
||||
prof_rewind( @"observers" );
|
||||
|
||||
NSSearchFieldCell *siteFieldCell = (NSSearchFieldCell *)self.siteField.cell;
|
||||
NSSearchFieldCell *siteFieldCell = self.siteField.cell;
|
||||
siteFieldCell.searchButtonCell = nil;
|
||||
siteFieldCell.cancelButtonCell = nil;
|
||||
|
||||
@@ -162,10 +160,10 @@
|
||||
- (IBAction)doUnlockUser:(id)sender {
|
||||
|
||||
[self.progressView startAnimation:self];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:context];
|
||||
NSString *userName = activeUser.name;
|
||||
BOOL success = [[MPMacAppDelegate get] signInAsUser:activeUser saveInContext:moc usingMasterPassword:self.masterPassword];
|
||||
BOOL success = [[MPMacAppDelegate get] signInAsUser:activeUser saveInContext:context usingMasterPassword:self.masterPassword];
|
||||
|
||||
PearlMainQueue( ^{
|
||||
self.masterPassword = nil;
|
||||
@@ -549,15 +547,15 @@
|
||||
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO] ];
|
||||
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name LIKE[cd] %@) AND user == %@",
|
||||
queryPattern, queryPattern, [MPMacAppDelegate get].activeUserOID];
|
||||
fetchRequest.predicate =
|
||||
[NSPredicate predicateWithFormat:@"name LIKE[cd] %@ AND user == %@", queryPattern, [MPMacAppDelegate get].activeUserOID];
|
||||
prof_rewind( @"fetchRequest" );
|
||||
|
||||
NSError *error = nil;
|
||||
NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error];
|
||||
if (!siteResults) {
|
||||
prof_finish( @"executeFetchRequest: %@ // %@", fetchRequest.predicate, [error fullDescription] );
|
||||
err( @"While fetching sites for completion: %@", [error fullDescription] );
|
||||
MPError( error, @"While fetching sites for completion." );
|
||||
return;
|
||||
}
|
||||
prof_rewind( @"executeFetchRequest: %@", fetchRequest.predicate );
|
||||
@@ -635,9 +633,9 @@
|
||||
return;
|
||||
}
|
||||
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||
[[self.selectedSite entityInContext:moc] use];
|
||||
[moc saveToStore];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
[[self.selectedSite entityInContext:context] use];
|
||||
[context saveToStore];
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@@ -52,9 +52,9 @@
|
||||
|
||||
- (void)setEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups {
|
||||
|
||||
if ([_entityOID isEqual:entity.objectID])
|
||||
if ([_entityOID isEqual:entity.permanentObjectID])
|
||||
return;
|
||||
_entityOID = entity.objectID;
|
||||
_entityOID = entity.permanentObjectID;
|
||||
|
||||
NSString *siteName = entity.name;
|
||||
NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName];
|
||||
@@ -115,7 +115,7 @@
|
||||
NSError *error;
|
||||
MPSiteEntity *entity = (MPSiteEntity *)[moc existingObjectWithID:_entityOID error:&error];
|
||||
if (!entity)
|
||||
err( @"Couldn't retrieve active site: %@", [error fullDescription] );
|
||||
MPError( error, @"Couldn't retrieve active site." );
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>MasterPassword 8.xcdatamodel</string>
|
||||
<string>MasterPassword 9.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="12141" systemVersion="16E195" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
|
||||
<entity name="MPGeneratedSiteEntity" representedClassName="MPGeneratedSiteEntity" parentEntity="MPSiteEntity" elementID="789304EA-070D-4982-8C20-54EECFC20CB6" syncable="YES">
|
||||
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="MPSiteEntity" representedClassName="MPSiteEntity" isAbstract="YES" elementID="58EE245C-6827-4C11-BB7E-5722F2426EC2" syncable="YES">
|
||||
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
|
||||
<attribute name="lastUsed" attributeType="Date" usesScalarValueType="NO" indexed="YES" syncable="YES"/>
|
||||
<attribute name="loginGenerated_" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="loginName" optional="YES" attributeType="String" elementID="A1B9F981-D33C-4BFE-9F94-C9D3E1F78E51" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/>
|
||||
<attribute name="requiresExplicitMigration_" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="NO">
|
||||
<userInfo/>
|
||||
</attribute>
|
||||
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="url" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" indexed="YES" syncable="YES"/>
|
||||
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<relationship name="questions" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="MPSiteQuestionEntity" inverseName="site" inverseEntity="MPSiteQuestionEntity" syncable="YES"/>
|
||||
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="sites" inverseEntity="MPUserEntity" elementID="FC8AE32E-7BE3-4FA6-8611-B7DC0DB063EF" syncable="YES"/>
|
||||
<compoundIndexes>
|
||||
<compoundIndex>
|
||||
<index value="name"/>
|
||||
<index value="user"/>
|
||||
</compoundIndex>
|
||||
</compoundIndexes>
|
||||
</entity>
|
||||
<entity name="MPSiteQuestionEntity" representedClassName="MPSiteQuestionEntity" syncable="YES">
|
||||
<attribute name="keyword" attributeType="String" syncable="YES"/>
|
||||
<relationship name="site" maxCount="1" deletionRule="Nullify" destinationEntity="MPSiteEntity" inverseName="questions" inverseEntity="MPSiteEntity" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="MPStoredSiteEntity" representedClassName="MPStoredSiteEntity" parentEntity="MPSiteEntity" elementID="BEEF1688-0CCD-4699-A86A-4D860FE2CEB8" syncable="YES">
|
||||
<attribute name="contentObject" optional="YES" attributeType="Transformable" customClassName="NSData" storedInTruthFile="YES" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES">
|
||||
<attribute name="avatar_" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="defaultType_" attributeType="Integer 16" defaultValueString="17" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="keyID" optional="YES" attributeType="Binary" syncable="YES"/>
|
||||
<attribute name="lastUsed" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="NO">
|
||||
<userInfo/>
|
||||
</attribute>
|
||||
<attribute name="touchID_" attributeType="Boolean" defaultValueString="0" usesScalarValueType="NO">
|
||||
<userInfo/>
|
||||
</attribute>
|
||||
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="2" usesScalarValueType="NO" syncable="YES"/>
|
||||
<relationship name="sites" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPSiteEntity" inverseName="user" inverseEntity="MPSiteEntity" elementID="D18D6772-040E-4CFE-8F32-A34B08E9E9BC" syncable="YES"/>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="MPGeneratedSiteEntity" positionX="216" positionY="-288" width="128" height="58"/>
|
||||
<element name="MPSiteEntity" positionX="-0" positionY="-286" width="128" height="225"/>
|
||||
<element name="MPSiteQuestionEntity" positionX="-2" positionY="-9" width="128" height="73"/>
|
||||
<element name="MPStoredSiteEntity" positionX="214" positionY="-171" width="128" height="60"/>
|
||||
<element name="MPUserEntity" positionX="-218" positionY="-288" width="128" height="178"/>
|
||||
</elements>
|
||||
</model>
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "MPTypeViewController.h"
|
||||
#import "MPSiteQuestionEntity.h"
|
||||
#import "MPSiteQuestionEntity+CoreDataClass.h"
|
||||
|
||||
@interface MPAnswersViewController : UIViewController
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
|
||||
- (void)setSite:(MPSiteEntity *)site {
|
||||
|
||||
_siteOID = [site objectID];
|
||||
_siteOID = site.permanentObjectID;
|
||||
_multiple = [site.questions count] > 0;
|
||||
[self.tableView reloadData];
|
||||
[self updateAnimated:NO];
|
||||
@@ -161,10 +161,9 @@
|
||||
MPSiteEntity *site = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
|
||||
|
||||
if ([cell isKindOfClass:[MPGlobalAnswersCell class]]) {
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Answer Copied" ) dismissAfter:2];
|
||||
[UIPasteboard generalPasteboard].string = ((MPGlobalAnswersCell *)cell).answerField.text;
|
||||
}
|
||||
if ([cell isKindOfClass:[MPGlobalAnswersCell class]])
|
||||
[self copyAnswer:((MPGlobalAnswersCell *)cell).answerField.text];
|
||||
|
||||
else if ([cell isKindOfClass:[MPMultipleAnswersCell class]]) {
|
||||
if (!_multiple)
|
||||
[self setMultiple:YES animated:YES];
|
||||
@@ -192,6 +191,7 @@
|
||||
} cancelTitle:@"Cancel" otherTitles:@"Remove Questions", nil];
|
||||
}
|
||||
}
|
||||
|
||||
else if ([cell isKindOfClass:[MPSendAnswersCell class]]) {
|
||||
NSString *body;
|
||||
if (!_multiple) {
|
||||
@@ -215,14 +215,30 @@
|
||||
|
||||
[PearlEMail sendEMailTo:nil fromVC:self subject:strf( @"Master Password security answers for %@", site.name ) body:body];
|
||||
}
|
||||
else if ([cell isKindOfClass:[MPAnswersQuestionCell class]]) {
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Answer Copied" ) dismissAfter:2];
|
||||
[UIPasteboard generalPasteboard].string = ((MPAnswersQuestionCell *)cell).answerField.text;
|
||||
}
|
||||
|
||||
else if ([cell isKindOfClass:[MPAnswersQuestionCell class]])
|
||||
[self copyAnswer:((MPAnswersQuestionCell *)cell).answerField.text];
|
||||
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
}
|
||||
|
||||
- (void)copyAnswer:(NSString *)answer {
|
||||
|
||||
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
|
||||
if ([pasteboard respondsToSelector:@selector( setItems:options: )]) {
|
||||
[pasteboard setItems:@[ @{ UIPasteboardTypeAutomatic: answer } ]
|
||||
options:@{
|
||||
UIPasteboardOptionLocalOnly : @NO,
|
||||
UIPasteboardOptionExpirationDate: [NSDate dateWithTimeIntervalSinceNow:3 * 60]
|
||||
}];
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Answer Copied (3 min)" ) dismissAfter:2];
|
||||
}
|
||||
else {
|
||||
pasteboard.string = answer;
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Answer Copied" ) dismissAfter:2];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)updateAnimated:(BOOL)animated {
|
||||
@@ -285,8 +301,8 @@
|
||||
|
||||
- (void)setQuestion:(MPSiteQuestionEntity *)question forSite:(MPSiteEntity *)site inVC:(MPAnswersViewController *)answersVC {
|
||||
|
||||
_siteOID = site.objectID;
|
||||
_questionOID = question.objectID;
|
||||
_siteOID = site.permanentObjectID;
|
||||
_questionOID = question.permanentObjectID;
|
||||
_answersVC = answersVC;
|
||||
|
||||
[self updateAnswerForQuestion:question ofSite:site];
|
||||
@@ -317,14 +333,7 @@
|
||||
question.keyword = keyword;
|
||||
|
||||
if ([context saveToStore]) {
|
||||
if ([question.objectID isTemporaryID]) {
|
||||
NSError *error = nil;
|
||||
[context obtainPermanentIDsForObjects:@[ question ] error:&error];
|
||||
if (error)
|
||||
err( @"Failed to obtain permanent object ID: %@", [error fullDescription] );
|
||||
}
|
||||
|
||||
_questionOID = question.objectID;
|
||||
_questionOID = question.permanentObjectID;
|
||||
[self updateAnswerForQuestion:question ofSite:site];
|
||||
|
||||
if (didAddQuestionObject)
|
||||
|
||||
@@ -49,11 +49,10 @@ const long MPAvatarAdd = 10000;
|
||||
|
||||
[super awakeFromNib];
|
||||
|
||||
self.alpha = 0;
|
||||
self.visible = NO;
|
||||
|
||||
self.nameContainer.layer.cornerRadius = 5;
|
||||
|
||||
self.avatarImageView.hidden = NO;
|
||||
self.avatarImageView.layer.cornerRadius = self.avatarImageView.bounds.size.height / 2;
|
||||
self.avatarImageView.layer.masksToBounds = NO;
|
||||
self.avatarImageView.backgroundColor = [UIColor clearColor];
|
||||
@@ -211,7 +210,7 @@ const long MPAvatarAdd = 10000;
|
||||
[UIView animateWithDuration:animated? 0.5f: 0 delay:0
|
||||
options:UIViewAnimationOptionOverrideInheritedDuration | UIViewAnimationOptionBeginFromCurrentState
|
||||
animations:^{
|
||||
self.alpha = 1;
|
||||
self.visible = YES;
|
||||
|
||||
if (self.newUser) {
|
||||
if (self.mode == MPAvatarModeLowered)
|
||||
@@ -220,6 +219,10 @@ const long MPAvatarAdd = 10000;
|
||||
self.avatar = arc4random() % MPAvatarCount;
|
||||
}
|
||||
|
||||
self.nameContainer.alpha = self.visibility;
|
||||
self.avatarImageView.alpha = self.visibility * 0.7f + 0.3f;
|
||||
self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
|
||||
|
||||
switch (self.mode) {
|
||||
case MPAvatarModeLowered: {
|
||||
[self.avatarSizeConstraint updateConstant:
|
||||
@@ -227,10 +230,9 @@ const long MPAvatarAdd = 10000;
|
||||
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||
self.nameContainer.alpha = self.visibility;
|
||||
self.nameContainer.visible = YES;
|
||||
self.nameContainer.backgroundColor = [UIColor clearColor];
|
||||
self.avatarImageView.alpha = self.visibility * 0.7f + 0.3f;
|
||||
self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
|
||||
self.avatarImageView.visible = YES;
|
||||
break;
|
||||
}
|
||||
case MPAvatarModeRaisedButInactive: {
|
||||
@@ -239,10 +241,9 @@ const long MPAvatarAdd = 10000;
|
||||
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||
self.nameContainer.alpha = self.visibility;
|
||||
self.nameContainer.visible = YES;
|
||||
self.nameContainer.backgroundColor = [UIColor clearColor];
|
||||
self.avatarImageView.alpha = 0;
|
||||
self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
|
||||
self.avatarImageView.visible = NO;
|
||||
break;
|
||||
}
|
||||
case MPAvatarModeRaisedAndActive: {
|
||||
@@ -251,10 +252,9 @@ const long MPAvatarAdd = 10000;
|
||||
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||
self.nameContainer.alpha = self.visibility;
|
||||
self.nameContainer.visible = YES;
|
||||
self.nameContainer.backgroundColor = [UIColor blackColor];
|
||||
self.avatarImageView.alpha = 1;
|
||||
self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
|
||||
self.avatarImageView.visible = YES;
|
||||
break;
|
||||
}
|
||||
case MPAvatarModeRaisedAndHidden: {
|
||||
@@ -263,10 +263,9 @@ const long MPAvatarAdd = 10000;
|
||||
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||
self.nameContainer.alpha = 0;
|
||||
self.nameContainer.visible = NO;
|
||||
self.nameContainer.backgroundColor = [UIColor blackColor];
|
||||
self.avatarImageView.alpha = 0;
|
||||
self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
|
||||
self.avatarImageView.visible = NO;
|
||||
break;
|
||||
}
|
||||
case MPAvatarModeRaisedAndMinimized: {
|
||||
@@ -274,9 +273,9 @@ const long MPAvatarAdd = 10000;
|
||||
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultHigh + 2];
|
||||
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||
self.nameContainer.alpha = 0;
|
||||
self.nameContainer.visible = NO;
|
||||
self.nameContainer.backgroundColor = [UIColor blackColor];
|
||||
self.avatarImageView.alpha = 1;
|
||||
self.avatarImageView.visible = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -293,7 +292,7 @@ const long MPAvatarAdd = 10000;
|
||||
else
|
||||
self.avatarImageView.backgroundColor = [UIColor clearColor];
|
||||
self.avatarImageView.layer.cornerRadius = self.avatarImageView.bounds.size.height / 2;
|
||||
self.spinner.alpha = self.spinnerActive? 1: 0;
|
||||
self.spinner.visible = self.spinnerActive;
|
||||
|
||||
[self.contentView layoutIfNeeded];
|
||||
} completion:nil];
|
||||
|
||||
@@ -28,17 +28,16 @@
|
||||
|
||||
[super viewDidLoad];
|
||||
|
||||
_views = [NSArray arrayWithObjects:
|
||||
self.view0, self.view1, self.view2, self.view3, self.view4, self.view5, self.view6, self.view7, self.view8, self.view9, nil];
|
||||
_views = @[ self.view0, self.view1, self.view2, self.view3, self.view4, self.view5, self.view6, self.view7, self.view8, self.view9 ];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
self.viewProgress.hidden = NO;
|
||||
self.viewProgress.visible = YES;
|
||||
self.viewProgress.progress = 0;
|
||||
[_views makeObjectsPerformSelector:@selector( setAlpha: ) withObject:@0];
|
||||
[_views makeObjectsPerformSelector:@selector( setVisible: ) withObject:@NO];
|
||||
_nextView = 0;
|
||||
}
|
||||
|
||||
@@ -47,7 +46,7 @@
|
||||
[super viewDidAppear:animated];
|
||||
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
[_views[_nextView++] setAlpha:1];
|
||||
[_views[_nextView++] setVisible:YES];
|
||||
}];
|
||||
|
||||
_viewTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 block:^(NSTimer *timer) {
|
||||
@@ -56,11 +55,11 @@
|
||||
if (self.viewProgress.progress == 1)
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.viewProgress.progress = 0;
|
||||
[_views[_nextView++] setAlpha:1];
|
||||
[_views[_nextView++] setVisible:YES];
|
||||
|
||||
if (_nextView >= [_views count]) {
|
||||
[_viewTimer invalidate];
|
||||
self.viewProgress.hidden = YES;
|
||||
self.viewProgress.visible = NO;
|
||||
}
|
||||
}];
|
||||
} repeats:YES];
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
@property(weak, nonatomic) IBOutlet UISegmentedControl *typeControl;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *counterLabel;
|
||||
@property(weak, nonatomic) IBOutlet UIActivityIndicatorView *activity;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *passwordLabel;
|
||||
@property(weak, nonatomic) IBOutlet UIButton *passwordButton;
|
||||
@property(weak, nonatomic) IBOutlet UIView *tipContainer;
|
||||
|
||||
- (IBAction)controlChanged:(UIControl *)control;
|
||||
|
||||
@@ -81,22 +81,31 @@
|
||||
[self updatePassword];
|
||||
}
|
||||
|
||||
- (IBAction)copyPassword:(UITapGestureRecognizer *)recognizer {
|
||||
- (IBAction)copyPassword:(id)sender {
|
||||
|
||||
if (recognizer.state == UIGestureRecognizerStateEnded) {
|
||||
NSString *sitePassword = self.passwordLabel.text;
|
||||
if ([sitePassword length]) {
|
||||
[UIPasteboard generalPasteboard].string = sitePassword;
|
||||
NSString *sitePassword = [self.passwordButton titleForState:UIControlStateNormal];
|
||||
if (![sitePassword length])
|
||||
return;
|
||||
|
||||
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
|
||||
if ([pasteboard respondsToSelector:@selector( setItems:options: )])
|
||||
[pasteboard setItems:@[ @{ UIPasteboardTypeAutomatic: sitePassword } ]
|
||||
options:@{
|
||||
UIPasteboardOptionLocalOnly : @NO,
|
||||
UIPasteboardOptionExpirationDate: [NSDate dateWithTimeIntervalSinceNow:3 * 60]
|
||||
}];
|
||||
else
|
||||
pasteboard.string = sitePassword;
|
||||
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.tipContainer.visible = YES;
|
||||
} completion:^(BOOL finished) {
|
||||
PearlMainQueueAfter( 3, ^{
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.tipContainer.alpha = 1;
|
||||
} completion:^(BOOL finished) {
|
||||
if (finished)
|
||||
PearlMainQueueAfter( 3, ^{
|
||||
self.tipContainer.alpha = 0;
|
||||
} );
|
||||
self.tipContainer.visible = NO;
|
||||
}];
|
||||
}
|
||||
}
|
||||
} );
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
@@ -106,7 +115,7 @@
|
||||
NSString *fullName = self.fullNameField.text;
|
||||
NSString *masterPassword = self.masterPasswordField.text;
|
||||
|
||||
self.passwordLabel.text = nil;
|
||||
[self.passwordButton setTitle:nil forState:UIControlStateNormal];
|
||||
[self.activity startAnimating];
|
||||
[_emergencyKeyQueue cancelAllOperations];
|
||||
[_emergencyKeyQueue addOperationWithBlock:^{
|
||||
@@ -128,7 +137,7 @@
|
||||
NSUInteger siteCounter = (NSUInteger)self.counterStepper.value;
|
||||
self.counterLabel.text = strf( @"%lu", (unsigned long)siteCounter );
|
||||
|
||||
self.passwordLabel.text = nil;
|
||||
[self.passwordButton setTitle:nil forState:UIControlStateNormal];
|
||||
[self.activity startAnimating];
|
||||
[_emergencyPasswordQueue cancelAllOperations];
|
||||
[_emergencyPasswordQueue addOperationWithBlock:^{
|
||||
@@ -138,7 +147,7 @@
|
||||
|
||||
PearlMainQueue( ^{
|
||||
[self.activity stopAnimating];
|
||||
self.passwordLabel.text = sitePassword;
|
||||
[self.passwordButton setTitle:sitePassword forState:UIControlStateNormal];
|
||||
} );
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
UIButton *dismissButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[dismissButton addTarget:self action:@selector( dismissOverlay: ) forControlEvents:UIControlEventTouchUpInside];
|
||||
dismissButton.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5f];
|
||||
dismissButton.alpha = 0;
|
||||
dismissButton.visible = NO;
|
||||
dismissButton.frame = self.view.bounds;
|
||||
dismissButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
_dismissSegueByButton[[NSValue valueWithNonretainedObject:dismissButton]] =
|
||||
@@ -89,7 +89,7 @@
|
||||
[_dismissSegueByButton removeObjectForKey:dismissSegueKey];
|
||||
|
||||
[UIView animateWithDuration:0.1f animations:^{
|
||||
dismissButton.alpha = 0;
|
||||
dismissButton.visible = NO;
|
||||
} completion:^(BOOL finished) {
|
||||
[dismissButton removeFromSuperview];
|
||||
}];
|
||||
@@ -127,14 +127,14 @@
|
||||
|
||||
CGRectSetY( destinationViewController.view.frame, 100 );
|
||||
destinationViewController.view.transform = CGAffineTransformMakeScale( 1.2f, 1.2f );
|
||||
destinationViewController.view.alpha = 0;
|
||||
destinationViewController.view.visible = NO;
|
||||
|
||||
[UIView transitionWithView:containerViewController.view duration:0.3f
|
||||
options:UIViewAnimationOptionAllowAnimatedContent animations:^{
|
||||
destinationViewController.view.transform = CGAffineTransformIdentity;
|
||||
CGRectSetY( destinationViewController.view.frame, 0 );
|
||||
destinationViewController.view.alpha = 1;
|
||||
dismissButton.alpha = 1;
|
||||
destinationViewController.view.visible = YES;
|
||||
dismissButton.visible = YES;
|
||||
} completion:^(BOOL finished) {
|
||||
[destinationViewController didMoveToParentViewController:containerViewController];
|
||||
[containerViewController setNeedsStatusBarAppearanceUpdate];
|
||||
@@ -147,7 +147,7 @@
|
||||
options:UIViewAnimationOptionAllowAnimatedContent animations:^{
|
||||
CGRectSetY( sourceViewController.view.frame, 100 );
|
||||
sourceViewController.view.transform = CGAffineTransformMakeScale( 0.8f, 0.8f );
|
||||
sourceViewController.view.alpha = 0;
|
||||
sourceViewController.view.visible = NO;
|
||||
[containerViewController removeDismissButtonForViewController:sourceViewController];
|
||||
} completion:^(BOOL finished) {
|
||||
if (finished) {
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
@property(nonatomic, strong) IBOutlet UITextField *passwordField;
|
||||
@property(nonatomic, strong) IBOutlet UIView *loginNameContainer;
|
||||
@property(nonatomic, strong) IBOutlet UITextField *loginNameField;
|
||||
@property(nonatomic, strong) IBOutlet UILabel *loginNameGenerated;
|
||||
@property(nonatomic, strong) IBOutlet UILabel *strengthLabel;
|
||||
@property(nonatomic, strong) IBOutlet UILabel *counterLabel;
|
||||
@property(nonatomic, strong) IBOutlet UIButton *counterButton;
|
||||
@@ -170,7 +171,7 @@
|
||||
|
||||
- (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated {
|
||||
|
||||
_siteOID = [site objectID];
|
||||
_siteOID = site.permanentObjectID;
|
||||
[self updateAnimated:animated];
|
||||
}
|
||||
|
||||
@@ -199,7 +200,7 @@
|
||||
atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES];
|
||||
|
||||
if (textField == self.loginNameField)
|
||||
self.loginNameButton.titleLabel.alpha = [self.loginNameField.text length] || self.loginNameField.enabled? 0: 1;
|
||||
self.loginNameButton.hidden = [self.loginNameField.attributedText length] || self.loginNameField.enabled;
|
||||
}
|
||||
|
||||
- (IBAction)textFieldDidChange:(UITextField *)textField {
|
||||
@@ -224,7 +225,7 @@
|
||||
|
||||
if (textField == self.passwordField || textField == self.loginNameField) {
|
||||
textField.enabled = NO;
|
||||
NSString *text = textField.text;
|
||||
NSString *text = [textField.attributedText string]?: textField.text;
|
||||
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPSiteEntity *site = [self siteInContext:context];
|
||||
@@ -235,10 +236,8 @@
|
||||
if ([site.algorithm savePassword:text toSite:site usingKey:[MPiOSAppDelegate get].key])
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:@"Password Updated" dismissAfter:2];
|
||||
}
|
||||
else if (textField == self.loginNameField &&
|
||||
((site.loginGenerated && ![text length]) ||
|
||||
(!site.loginGenerated && ![text isEqualToString:site.loginName]))) {
|
||||
if (site.loginGenerated || !([site.loginName isEqualToString:text] || (!text && !site.loginName))) {
|
||||
else if (textField == self.loginNameField) {
|
||||
if (![text isEqualToString:[site.algorithm resolveLoginForSite:site usingKey:[MPiOSAppDelegate get].key]]) {
|
||||
site.loginGenerated = NO;
|
||||
site.loginName = text;
|
||||
|
||||
@@ -282,12 +281,12 @@
|
||||
|
||||
[self setMode:MPPasswordCellModePassword animated:YES];
|
||||
|
||||
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||
[PearlSheet showSheetWithTitle:@"Change Password Type" viewStyle:UIActionSheetStyleAutomatic
|
||||
initSheet:^(UIActionSheet *sheet) {
|
||||
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||
for (NSNumber *typeNumber in [MPAlgorithmDefault allTypes]) {
|
||||
for (NSNumber *typeNumber in [mainSite.algorithm allTypes]) {
|
||||
MPSiteType type = (MPSiteType)[typeNumber unsignedIntegerValue];
|
||||
NSString *typeName = [MPAlgorithmDefault nameOfType:type];
|
||||
NSString *typeName = [mainSite.algorithm nameOfType:type];
|
||||
if (type == mainSite.type)
|
||||
[sheet addButtonWithTitle:strf( @"● %@", typeName )];
|
||||
else
|
||||
@@ -297,7 +296,8 @@
|
||||
if (buttonIndex == [sheet cancelButtonIndex])
|
||||
return;
|
||||
|
||||
MPSiteType type = (MPSiteType)[[MPAlgorithmDefault allTypes][buttonIndex] unsignedIntegerValue]?: MPSiteTypeGeneratedLong;
|
||||
MPSiteType type = (MPSiteType)[[mainSite.algorithm allTypes][buttonIndex] unsignedIntegerValue]?:
|
||||
mainSite.user.defaultType?: mainSite.algorithm.defaultType;
|
||||
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPSiteEntity *site = [self siteInContext:context];
|
||||
@@ -367,6 +367,31 @@
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)doAction:(UIButton *)sender {
|
||||
|
||||
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlock:^(NSManagedObjectContext *mainContext) {
|
||||
MPSiteEntity *mainSite = [self siteInContext:mainContext];
|
||||
[PearlAlert showAlertWithTitle:@"Login Page" message:nil
|
||||
viewStyle:UIAlertViewStylePlainTextInput
|
||||
initAlert:^(UIAlertView *alert, UITextField *firstField) {
|
||||
firstField.placeholder = strf( @"Login URL for %@", mainSite.name );
|
||||
firstField.text = mainSite.url;
|
||||
}
|
||||
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
|
||||
if (buttonIndex == alert.cancelButtonIndex)
|
||||
return;
|
||||
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPSiteEntity *site = [self siteInContext:context];
|
||||
NSURL *url = [NSURL URLWithString:[alert textFieldAtIndex:0].text];
|
||||
site.url = [url.host? url: nil absoluteString];
|
||||
[context saveToStore];
|
||||
}];
|
||||
}
|
||||
cancelTitle:@"Cancel" otherTitles:@"Save", nil];
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)doIncrementCounter:(UIButton *)sender {
|
||||
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
@@ -493,10 +518,12 @@
|
||||
|
||||
- (void)updateAnimated:(BOOL)animated {
|
||||
|
||||
Weakify( self );
|
||||
if (![NSThread isMainThread]) {
|
||||
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
|
||||
PearlMainQueueOperation( ^{
|
||||
Strongify( self );
|
||||
[self updateAnimated:animated];
|
||||
}];
|
||||
} );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -504,13 +531,14 @@
|
||||
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||
|
||||
// UI
|
||||
self.backgroundColor = mainSite.url? [UIColor greenColor]: [UIColor redColor];
|
||||
self.upgradeButton.gone = !mainSite.requiresExplicitMigration && ![[MPiOSConfig get].allowDowngrade boolValue];
|
||||
self.answersButton.gone = ![[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateAnswers];
|
||||
BOOL settingsMode = self.mode == MPPasswordCellModeSettings;
|
||||
self.loginNameContainer.alpha = settingsMode || mainSite.loginGenerated || [mainSite.loginName length]? 0.7f: 0;
|
||||
self.loginNameField.textColor = [UIColor colorWithHexString:mainSite.loginGenerated? @"5E636D": @"6D5E63"];
|
||||
self.modeButton.alpha = self.transientSite? 0: settingsMode? 0.5f: 0.1f;
|
||||
self.counterLabel.alpha = self.counterButton.alpha = mainSite.type & MPSiteTypeClassGenerated? 0.5f: 0;
|
||||
self.loginNameContainer.visible = settingsMode || mainSite.loginGenerated || [mainSite.loginName length];
|
||||
self.modeButton.visible = !self.transientSite;
|
||||
self.modeButton.alpha = settingsMode? 0.5f: 0.1f;
|
||||
self.counterLabel.visible = self.counterButton.visible = mainSite.type & MPSiteTypeClassGenerated;
|
||||
self.modeButton.selected = settingsMode;
|
||||
self.strengthLabel.gone = !settingsMode;
|
||||
self.modeScrollView.scrollEnabled = !self.transientSite;
|
||||
@@ -520,13 +548,21 @@
|
||||
[self.passwordField resignFirstResponder];
|
||||
}
|
||||
if ([[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateLogins])
|
||||
[self.loginNameButton setTitle:@"Tap to generate username or use pencil to save one" forState:UIControlStateNormal];
|
||||
[self.loginNameButton setTitle:@"Tap here to ⚙ generate username or the pencil to type one" forState:UIControlStateNormal];
|
||||
else
|
||||
[self.loginNameButton setTitle:@"Tap the pencil to save a username" forState:UIControlStateNormal];
|
||||
[self.loginNameButton setTitle:@"Tap the pencil to type a username" forState:UIControlStateNormal];
|
||||
|
||||
// Site Name
|
||||
[self updateSiteName:mainSite];
|
||||
|
||||
// Site Counter
|
||||
if ([mainSite isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||
self.counterLabel.text = strf( @"%lu", (unsigned long)((MPGeneratedSiteEntity *)mainSite).counter );
|
||||
|
||||
// Site Login Name
|
||||
self.loginNameField.enabled = self.passwordField.enabled = //
|
||||
[self.loginNameField isFirstResponder] || [self.passwordField isFirstResponder];
|
||||
|
||||
// Site Password
|
||||
self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue];
|
||||
self.passwordField.attributedPlaceholder = stra(
|
||||
@@ -534,14 +570,23 @@
|
||||
mainSite.type & MPSiteTypeClassGenerated? strl( @"..." ): @"", @{
|
||||
NSForegroundColorAttributeName: [UIColor whiteColor]
|
||||
} );
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
|
||||
// Calculate Fields
|
||||
if (![MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPSiteEntity *site = [self siteInContext:context];
|
||||
MPKey *key = [MPiOSAppDelegate get].key;
|
||||
if (!key)
|
||||
if (!key) {
|
||||
wrn( @"Could not load cell content: key unavailable." );
|
||||
PearlMainQueueOperation( ^{
|
||||
Strongify( self );
|
||||
[self updateAnimated:YES];
|
||||
} );
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL loginGenerated = site.loginGenerated;
|
||||
NSString *password = nil, *loginName = [site resolveLoginUsingKey:key];
|
||||
MPSiteType transientType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPSiteTypeGeneratedLong;
|
||||
MPSiteType transientType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPAlgorithmDefault.defaultType;
|
||||
if (self.transientSite && transientType & MPSiteTypeClassGenerated)
|
||||
password = [MPAlgorithmDefault generatePasswordForSiteNamed:self.transientSite ofType:transientType
|
||||
withCounter:1 usingKey:key];
|
||||
@@ -559,13 +604,15 @@
|
||||
BOOL requiresExplicitMigration = site.requiresExplicitMigration;
|
||||
|
||||
PearlMainQueue( ^{
|
||||
self.loginNameField.text = loginName;
|
||||
self.passwordField.text = password;
|
||||
self.strengthLabel.text = timeToCrackString;
|
||||
self.loginNameButton.titleLabel.alpha = [loginName length] || self.loginNameField.enabled? 0: 1;
|
||||
self.loginNameGenerated.hidden = !loginGenerated;
|
||||
self.loginNameField.attributedText =
|
||||
strarm( stra( loginName?: @"", self.siteNameLabel.textAttributes ), NSParagraphStyleAttributeName, nil );
|
||||
self.loginNameButton.hidden = [loginName length] || self.loginNameField.enabled;
|
||||
|
||||
if (![password length]) {
|
||||
self.indicatorView.alpha = 1;
|
||||
self.indicatorView.hidden = NO;
|
||||
[self.indicatorView removeFromSuperview];
|
||||
[self.modeScrollView addSubview:self.indicatorView];
|
||||
[self.contentView addConstraintsWithVisualFormat:@"V:[indicator][target]" options:NSLayoutFormatAlignAllCenterX
|
||||
@@ -575,7 +622,7 @@
|
||||
}];
|
||||
}
|
||||
else if (requiresExplicitMigration) {
|
||||
self.indicatorView.alpha = 1;
|
||||
self.indicatorView.hidden = NO;
|
||||
[self.indicatorView removeFromSuperview];
|
||||
[self.modeScrollView addSubview:self.indicatorView];
|
||||
[self.contentView addConstraintsWithVisualFormat:@"V:[indicator][target]" options:NSLayoutFormatAlignAllCenterX
|
||||
@@ -585,17 +632,15 @@
|
||||
}];
|
||||
}
|
||||
else
|
||||
self.indicatorView.alpha = 0;
|
||||
self.indicatorView.hidden = YES;
|
||||
} );
|
||||
}];
|
||||
|
||||
// Site Counter
|
||||
if ([mainSite isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||
self.counterLabel.text = strf( @"%lu", (unsigned long)((MPGeneratedSiteEntity *)mainSite).counter );
|
||||
|
||||
// Site Login Name
|
||||
self.loginNameField.enabled = self.passwordField.enabled = //
|
||||
[self.loginNameField isFirstResponder] || [self.passwordField isFirstResponder];
|
||||
}]) {
|
||||
wrn( @"Could not load cell content: store unavailable." );
|
||||
PearlMainQueueOperation( ^{
|
||||
Strongify( self );
|
||||
[self updateAnimated:YES];
|
||||
} );
|
||||
}
|
||||
|
||||
[self.contentView layoutIfNeeded];
|
||||
}];
|
||||
@@ -616,8 +661,9 @@
|
||||
range:NSMakeRange( s, [self.fuzzyGroups[f] length] )];
|
||||
}
|
||||
|
||||
[attributedSiteName appendAttributedString:stra(
|
||||
strf( @" - %@", self.transientSite? @"Tap to create": [site.algorithm shortNameOfType:site.type] ), @{} )];
|
||||
if (self.transientSite)
|
||||
[attributedSiteName appendAttributedString:stra( @" – Tap to create", @{} )];
|
||||
|
||||
self.siteNameLabel.attributedText = attributedSiteName;
|
||||
}
|
||||
|
||||
@@ -629,8 +675,21 @@
|
||||
return NO;
|
||||
|
||||
PearlMainQueue( ^{
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Password Copied" ) dismissAfter:2];
|
||||
[UIPasteboard generalPasteboard].string = password;
|
||||
[self.window endEditing:YES];
|
||||
|
||||
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
|
||||
if ([pasteboard respondsToSelector:@selector(setItems:options:)]) {
|
||||
[pasteboard setItems:@[ @{ UIPasteboardTypeAutomatic: password } ]
|
||||
options:@{
|
||||
UIPasteboardOptionLocalOnly : @NO,
|
||||
UIPasteboardOptionExpirationDate: [NSDate dateWithTimeIntervalSinceNow:3 * 60]
|
||||
}];
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Password Copied (3 min)" ) dismissAfter:2];
|
||||
}
|
||||
else {
|
||||
pasteboard.string = password;
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Password Copied" ) dismissAfter:2];
|
||||
}
|
||||
} );
|
||||
|
||||
[site use];
|
||||
@@ -646,8 +705,10 @@
|
||||
return NO;
|
||||
|
||||
PearlMainQueue( ^{
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Login Name Copied" ) dismissAfter:2];
|
||||
[self.window endEditing:YES];
|
||||
|
||||
[UIPasteboard generalPasteboard].string = loginName;
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Login Name Copied" ) dismissAfter:2];
|
||||
} );
|
||||
|
||||
[site use];
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
@property(assign, nonatomic) BOOL active;
|
||||
|
||||
- (void)setActive:(BOOL)active animated:(BOOL)animated completion:(void ( ^ )(BOOL finished))completion;
|
||||
- (void)updatePasswords;
|
||||
- (void)reloadPasswords;
|
||||
|
||||
- (IBAction)dismissPopdown:(id)sender;
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#import "MPAnswersViewController.h"
|
||||
#import "MPMessageViewController.h"
|
||||
|
||||
static const NSString *MPTransientPasswordItem = @"MPTransientPasswordItem";
|
||||
|
||||
typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
MPPasswordsBadNameTip = 1 << 0,
|
||||
};
|
||||
@@ -32,7 +34,6 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
@interface MPPasswordsViewController()<NSFetchedResultsControllerDelegate>
|
||||
|
||||
@property(nonatomic, strong) IBOutlet UINavigationBar *navigationBar;
|
||||
@property(nonatomic, readonly) NSString *query;
|
||||
|
||||
@end
|
||||
|
||||
@@ -42,10 +43,9 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
UIColor *_backgroundColor;
|
||||
UIColor *_darkenedBackgroundColor;
|
||||
__weak UIViewController *_popdownVC;
|
||||
BOOL _showTransientItem;
|
||||
NSUInteger _transientItem;
|
||||
NSCharacterSet *_siteNameAcceptableCharactersSet;
|
||||
NSArray *_fuzzyGroups;
|
||||
NSMutableArray<NSMutableArray *> *_passwordCollectionSections;
|
||||
}
|
||||
|
||||
#pragma mark - Life
|
||||
@@ -61,7 +61,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
_backgroundColor = self.passwordCollectionView.backgroundColor;
|
||||
_darkenedBackgroundColor = [_backgroundColor colorWithAlphaComponent:0.6f];
|
||||
_transientItem = NSNotFound;
|
||||
_passwordCollectionSections = [NSMutableArray new];
|
||||
|
||||
self.view.backgroundColor = [UIColor clearColor];
|
||||
[self.passwordCollectionView automaticallyAdjustInsetsForKeyboard];
|
||||
@@ -81,7 +81,24 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
[self registerObservers];
|
||||
[self updateConfigKey:nil];
|
||||
[self updatePasswords];
|
||||
|
||||
static NSRegularExpression *bareHostRE = nil;
|
||||
static dispatch_once_t once = 0;
|
||||
dispatch_once( &once, ^{
|
||||
bareHostRE = [NSRegularExpression regularExpressionWithPattern:@"([^\\.]+\\.[^\\.]+)$" options:0 error:nil];
|
||||
} );
|
||||
|
||||
NSURL *pasteboardURL = nil;
|
||||
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
|
||||
if ([pasteboard respondsToSelector:@selector( hasURLs )])
|
||||
pasteboardURL = pasteboard.hasURLs? pasteboard.URL: nil;
|
||||
else
|
||||
pasteboardURL = [NSURL URLWithString:pasteboard.string];
|
||||
|
||||
if (pasteboardURL.host)
|
||||
self.query = NSNullToNil( [pasteboardURL.host firstMatchGroupsOfExpression:bareHostRE][0] );
|
||||
else
|
||||
[self reloadPasswords];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
@@ -138,30 +155,43 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
return CGSizeMake( itemWidth, 100 );
|
||||
}
|
||||
|
||||
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout
|
||||
insetForSectionAtIndex:(NSInteger)section {
|
||||
|
||||
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)collectionViewLayout;
|
||||
UIEdgeInsets occludedInsets = [self.passwordCollectionView occludedInsets];
|
||||
UIEdgeInsets insets = layout.sectionInset;
|
||||
insets.top = insets.bottom; // Undo storyboard hack for manual top-occluded insets.
|
||||
|
||||
if (section == 0)
|
||||
insets.top += occludedInsets.top;
|
||||
|
||||
if (section == collectionView.numberOfSections - 1)
|
||||
insets.bottom += occludedInsets.bottom;
|
||||
|
||||
return insets;
|
||||
}
|
||||
|
||||
#pragma mark - UICollectionViewDataSource
|
||||
|
||||
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
|
||||
|
||||
return [self.fetchedResultsController.sections count];
|
||||
return [_passwordCollectionSections count];
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
||||
|
||||
if (![MPiOSAppDelegate get].activeUserOID || !_fetchedResultsController)
|
||||
return 0;
|
||||
|
||||
NSUInteger objects = ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[section]).numberOfObjects;
|
||||
_transientItem = _showTransientItem? objects: NSNotFound;
|
||||
return objects + (_showTransientItem? 1: 0);
|
||||
return [_passwordCollectionSections[(NSUInteger)section] count];
|
||||
}
|
||||
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
MPPasswordCell *cell = [MPPasswordCell dequeueCellFromCollectionView:collectionView indexPath:indexPath];
|
||||
[cell setFuzzyGroups:_fuzzyGroups];
|
||||
if (indexPath.item < ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects)
|
||||
[cell setSite:[self.fetchedResultsController objectAtIndexPath:indexPath] animated:NO];
|
||||
else
|
||||
id item = _passwordCollectionSections[(NSUInteger)indexPath.section][(NSUInteger)indexPath.item];
|
||||
if ([item isKindOfClass:[MPSiteEntity class]])
|
||||
[cell setSite:item animated:NO];
|
||||
else // item == MPTransientPasswordItem
|
||||
[cell setTransientSite:self.query animated:NO];
|
||||
|
||||
return cell;
|
||||
@@ -178,41 +208,14 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
#pragma mark - NSFetchedResultsControllerDelegate
|
||||
|
||||
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath
|
||||
forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
|
||||
|
||||
if (controller == _fetchedResultsController) {
|
||||
@try {
|
||||
[self.passwordCollectionView performBatchUpdates:^{
|
||||
[self fetchedItemsDidUpdate];
|
||||
switch (type) {
|
||||
case NSFetchedResultsChangeInsert:
|
||||
[self.passwordCollectionView insertItemsAtIndexPaths:@[ newIndexPath ]];
|
||||
break;
|
||||
case NSFetchedResultsChangeDelete:
|
||||
[self.passwordCollectionView deleteItemsAtIndexPaths:@[ indexPath ]];
|
||||
break;
|
||||
case NSFetchedResultsChangeMove:
|
||||
[self.passwordCollectionView moveItemAtIndexPath:indexPath toIndexPath:newIndexPath];
|
||||
break;
|
||||
case NSFetchedResultsChangeUpdate:
|
||||
[self.passwordCollectionView reloadItemsAtIndexPaths:@[ indexPath ]];
|
||||
break;
|
||||
}
|
||||
} completion:nil];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
wrn( @"While updating password cells: %@", [exception fullDescription] );
|
||||
[self.passwordCollectionView reloadData];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo
|
||||
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
|
||||
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
|
||||
|
||||
if (controller == _fetchedResultsController)
|
||||
[self.passwordCollectionView reloadData];
|
||||
PearlMainQueue( ^{
|
||||
[self.passwordCollectionView updateDataSource:_passwordCollectionSections
|
||||
toSections:[self createPasswordCollectionSections]
|
||||
reloadItems:nil completion:nil];
|
||||
} );
|
||||
}
|
||||
|
||||
#pragma mark - UISearchBarDelegate
|
||||
@@ -244,7 +247,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
if (_passwordsDismissRecognizer)
|
||||
[self.view removeGestureRecognizer:_passwordsDismissRecognizer];
|
||||
|
||||
[self updatePasswords];
|
||||
[self reloadPasswords];
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.passwordCollectionView.backgroundColor = _backgroundColor;
|
||||
}];
|
||||
@@ -268,7 +271,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
if ([[self.query stringByTrimmingCharactersInSet:_siteNameAcceptableCharactersSet] length])
|
||||
[self showTips:MPPasswordsBadNameTip];
|
||||
|
||||
[self updatePasswords];
|
||||
[self reloadPasswords];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,62 +281,65 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
if (showTips & MPPasswordsBadNameTip)
|
||||
self.badNameTipContainer.alpha = 1;
|
||||
self.badNameTipContainer.visible = YES;
|
||||
} completion:^(BOOL finished) {
|
||||
if (finished)
|
||||
PearlMainQueueAfter( 5, ^{
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
if (showTips & MPPasswordsBadNameTip)
|
||||
self.badNameTipContainer.alpha = 0;
|
||||
}];
|
||||
} );
|
||||
PearlMainQueueAfter( 5, ^{
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
if (showTips & MPPasswordsBadNameTip)
|
||||
self.badNameTipContainer.visible = NO;
|
||||
}];
|
||||
} );
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)fetchedItemsDidUpdate {
|
||||
- (NSMutableArray<NSMutableArray *> *)createPasswordCollectionSections {
|
||||
|
||||
NSString *query = self.query;
|
||||
_showTransientItem = [query length] > 0;
|
||||
NSUInteger objects = ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[0]).numberOfObjects;
|
||||
if (_showTransientItem && objects == 1 &&
|
||||
[[[self.fetchedResultsController.fetchedObjects firstObject] name] isEqualToString:query])
|
||||
_showTransientItem = NO;
|
||||
if ([self.passwordCollectionView numberOfSections] > 0) {
|
||||
if (!_showTransientItem && _transientItem != NSNotFound)
|
||||
[self.passwordCollectionView deleteItemsAtIndexPaths:
|
||||
@[ [NSIndexPath indexPathForItem:_transientItem inSection:0] ]];
|
||||
else if (_showTransientItem && _transientItem == NSNotFound)
|
||||
[self.passwordCollectionView insertItemsAtIndexPaths:
|
||||
@[ [NSIndexPath indexPathForItem:objects inSection:0] ]];
|
||||
else if (_transientItem != NSNotFound)
|
||||
[self.passwordCollectionView reloadItemsAtIndexPaths:
|
||||
@[ [NSIndexPath indexPathForItem:_transientItem inSection:0] ]];
|
||||
BOOL needTransientItem = [query length] > 0;
|
||||
|
||||
NSArray<id<NSFetchedResultsSectionInfo>> *sectionInfos = [self.fetchedResultsController sections];
|
||||
NSMutableArray *sections = [[NSMutableArray alloc] initWithCapacity:[sectionInfos count]];
|
||||
for (id<NSFetchedResultsSectionInfo> sectionInfo in sectionInfos) {
|
||||
NSArray<MPSiteEntity *> *sites = [sectionInfo.objects copy];
|
||||
[sections addObject:sites];
|
||||
|
||||
if (needTransientItem)
|
||||
for (MPSiteEntity *site in sites)
|
||||
if ([site.name isEqualToString:query]) {
|
||||
needTransientItem = NO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (needTransientItem)
|
||||
[sections addObject:@[ MPTransientPasswordItem ]];
|
||||
|
||||
return sections;
|
||||
}
|
||||
|
||||
- (void)registerObservers {
|
||||
|
||||
PearlRemoveNotificationObservers();
|
||||
PearlAddNotificationObserver( UIApplicationDidEnterBackgroundNotification, nil, [NSOperationQueue mainQueue],
|
||||
PearlAddNotificationObserver( UIApplicationWillResignActiveNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
self.passwordSelectionContainer.alpha = 0;
|
||||
} );
|
||||
PearlAddNotificationObserver( UIApplicationWillEnterForegroundNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
[self updatePasswords];
|
||||
[self.view endEditing:YES];
|
||||
self.passwordSelectionContainer.visible = NO;
|
||||
} );
|
||||
PearlAddNotificationObserver( UIApplicationDidBecomeActiveNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
[UIView animateWithDuration:0.7f animations:^{
|
||||
self.passwordSelectionContainer.alpha = 1;
|
||||
self.passwordSelectionContainer.visible = YES;
|
||||
}];
|
||||
} );
|
||||
PearlAddNotificationObserver( UIApplicationWillEnterForegroundNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
[self viewWillAppear:YES];
|
||||
} );
|
||||
PearlAddNotificationObserver( MPSignedOutNotification, nil, nil,
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
PearlMainQueue( ^{
|
||||
_fetchedResultsController = nil;
|
||||
self.passwordsSearchBar.text = nil;
|
||||
[self.passwordCollectionView reloadData];
|
||||
self->_fetchedResultsController = nil;
|
||||
self.query = nil;
|
||||
} );
|
||||
} );
|
||||
PearlAddNotificationObserver( MPCheckConfigNotification, nil, nil,
|
||||
@@ -345,25 +351,23 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresWillChangeNotification, nil, nil,
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
self->_fetchedResultsController = nil;
|
||||
PearlMainQueue( ^{
|
||||
[self.passwordCollectionView reloadData];
|
||||
} );
|
||||
[self reloadPasswords];
|
||||
} );
|
||||
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresDidChangeNotification, nil, nil,
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
PearlMainQueue( ^{
|
||||
[self updatePasswords];
|
||||
[self reloadPasswords];
|
||||
[self registerObservers];
|
||||
} );
|
||||
} );
|
||||
|
||||
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
|
||||
if (mainContext)
|
||||
PearlAddNotificationObserver( NSManagedObjectContextDidSaveNotification, mainContext, nil,
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
if (![[MPiOSAppDelegate get] activeUserInContext:note.object])
|
||||
[[MPiOSAppDelegate get] signOutAnimated:YES];
|
||||
} );
|
||||
[MPiOSAppDelegate managedObjectContextChanged:^(NSDictionary<NSManagedObjectID *, NSString *> *affectedObjects) {
|
||||
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlock:^(NSManagedObjectContext *mainContext) {
|
||||
// TODO: either move this into the app delegate or remove the duplicate signOutAnimated: call from the app delegate.
|
||||
if (![[MPiOSAppDelegate get] activeUserInContext:mainContext])
|
||||
[[MPiOSAppDelegate get] signOutAnimated:YES];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateConfigKey:(NSString *)key {
|
||||
@@ -374,81 +378,38 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
[self.passwordCollectionView reloadData];
|
||||
}
|
||||
|
||||
- (void)updatePasswords {
|
||||
- (void)reloadPasswords {
|
||||
|
||||
NSManagedObjectID *activeUserOID = [MPiOSAppDelegate get].activeUserOID;
|
||||
if (!activeUserOID) {
|
||||
PearlMainQueue( ^{
|
||||
self.passwordsSearchBar.text = nil;
|
||||
[self.passwordCollectionView reloadData];
|
||||
[self.passwordCollectionView setContentOffset:CGPointMake( 0, -self.passwordCollectionView.contentInset.top ) animated:YES];
|
||||
} );
|
||||
return;
|
||||
}
|
||||
|
||||
static NSRegularExpression *fuzzyRE;
|
||||
static dispatch_once_t once = 0;
|
||||
dispatch_once( &once, ^{
|
||||
fuzzyRE = [NSRegularExpression regularExpressionWithPattern:@"(.)" options:0 error:nil];
|
||||
} );
|
||||
|
||||
NSString *queryString = self.query;
|
||||
NSString *queryPattern;
|
||||
if ([queryString length] < 13)
|
||||
queryPattern = [queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1*"];
|
||||
else
|
||||
// If query is too long, a wildcard per character makes the CoreData fetch take excessively long.
|
||||
queryPattern = strf( @"*%@*", queryString );
|
||||
NSMutableArray *fuzzyGroups = [NSMutableArray arrayWithCapacity:[queryString length]];
|
||||
[fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length )
|
||||
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
|
||||
[fuzzyGroups addObject:[queryString substringWithRange:result.range]];
|
||||
}];
|
||||
_fuzzyGroups = fuzzyGroups;
|
||||
[self.fetchedResultsController.managedObjectContext performBlock:^{
|
||||
NSArray *oldSectionInfos = [self.fetchedResultsController sections];
|
||||
NSMutableArray *oldSections = [[NSMutableArray alloc] initWithCapacity:[oldSectionInfos count]];
|
||||
for (id<NSFetchedResultsSectionInfo> sectionInfo in oldSectionInfos)
|
||||
[oldSections addObject:[sectionInfo.objects copy]];
|
||||
static NSRegularExpression *fuzzyRE;
|
||||
static dispatch_once_t once = 0;
|
||||
dispatch_once( &once, ^{
|
||||
fuzzyRE = [NSRegularExpression regularExpressionWithPattern:@"(.)" options:0 error:nil];
|
||||
} );
|
||||
|
||||
NSString *queryString = self.query;
|
||||
NSString *queryPattern = [[queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1"]
|
||||
stringByAppendingString:@"*"];
|
||||
NSMutableArray *fuzzyGroups = [NSMutableArray new];
|
||||
[fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length )
|
||||
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
|
||||
[fuzzyGroups addObject:[queryString substringWithRange:result.range]];
|
||||
}];
|
||||
_fuzzyGroups = fuzzyGroups;
|
||||
|
||||
NSError *error = nil;
|
||||
self.fetchedResultsController.fetchRequest.predicate =
|
||||
[NSPredicate predicateWithFormat:@"(%@ == '' OR name LIKE[cd] %@) AND user == %@",
|
||||
queryPattern, queryPattern, activeUserOID];
|
||||
[NSPredicate predicateWithFormat:@"name LIKE[cd] %@ AND user == %@", queryPattern, [MPiOSAppDelegate get].activeUserOID];
|
||||
if (![self.fetchedResultsController performFetch:&error])
|
||||
err( @"Couldn't fetch sites: %@", [error fullDescription] );
|
||||
MPError( error, @"Couldn't fetch sites." );
|
||||
|
||||
PearlMainQueue( ^{
|
||||
@try {
|
||||
[self.passwordCollectionView performBatchUpdates:^{
|
||||
[self fetchedItemsDidUpdate];
|
||||
|
||||
NSInteger fromSections = self.passwordCollectionView.numberOfSections;
|
||||
NSInteger toSections = [self numberOfSectionsInCollectionView:self.passwordCollectionView];
|
||||
for (NSInteger section = 0; section < MAX( toSections, fromSections ); ++section) {
|
||||
if (section >= fromSections)
|
||||
[self.passwordCollectionView insertSections:[NSIndexSet indexSetWithIndex:section]];
|
||||
else if (section >= toSections)
|
||||
[self.passwordCollectionView deleteSections:[NSIndexSet indexSetWithIndex:section]];
|
||||
else if (section < [oldSections count])
|
||||
[self.passwordCollectionView reloadItemsFromArray:oldSections[section]
|
||||
toArray:[[self.fetchedResultsController sections][section] objects]
|
||||
inSection:section];
|
||||
else
|
||||
[self.passwordCollectionView reloadSections:[NSIndexSet indexSetWithIndex:section]];
|
||||
}
|
||||
} completion:^(BOOL finished) {
|
||||
if (finished)
|
||||
[self.passwordCollectionView setContentOffset:CGPointMake( 0, -self.passwordCollectionView.contentInset.top )
|
||||
animated:YES];
|
||||
for (MPPasswordCell *cell in self.passwordCollectionView.visibleCells)
|
||||
[cell setFuzzyGroups:_fuzzyGroups];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
wrn( @"While updating password cells: %@", [exception fullDescription] );
|
||||
[self.passwordCollectionView reloadData];
|
||||
}
|
||||
[self.passwordCollectionView updateDataSource:_passwordCollectionSections
|
||||
toSections:[self createPasswordCollectionSections]
|
||||
reloadItems:@[ MPTransientPasswordItem ] completion:^(BOOL finished) {
|
||||
for (MPPasswordCell *cell in self.passwordCollectionView.visibleCells)
|
||||
[cell setFuzzyGroups:_fuzzyGroups];
|
||||
}];
|
||||
} );
|
||||
}];
|
||||
}
|
||||
@@ -460,18 +421,24 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
return [self.passwordsSearchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
}
|
||||
|
||||
- (void)setQuery:(NSString *)query {
|
||||
|
||||
self.passwordsSearchBar.text = [query stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
[self reloadPasswords];
|
||||
}
|
||||
|
||||
- (NSFetchedResultsController *)fetchedResultsController {
|
||||
|
||||
if (!_fetchedResultsController) {
|
||||
_showTransientItem = NO;
|
||||
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||
fetchRequest.sortDescriptors = @[
|
||||
[[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO]
|
||||
];
|
||||
fetchRequest.fetchBatchSize = 10;
|
||||
_fetchedResultsController = [[NSFetchedResultsController alloc]
|
||||
initWithFetchRequest:fetchRequest managedObjectContext:mainContext sectionNameKeyPath:nil cacheName:nil];
|
||||
_fetchedResultsController =
|
||||
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:mainContext
|
||||
sectionNameKeyPath:nil cacheName:nil];
|
||||
_fetchedResultsController.delegate = self;
|
||||
}];
|
||||
[self registerObservers];
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
@implementation MPPopdownSegue {
|
||||
}
|
||||
|
||||
static char UnwindingObserverKey;
|
||||
|
||||
- (void)perform {
|
||||
|
||||
MPPasswordsViewController *passwordsVC;
|
||||
@@ -39,19 +37,21 @@ static char UnwindingObserverKey;
|
||||
[passwordsVC.popdownContainer addConstraintsWithVisualFormats:@[ @"H:|[popdownView]|", @"V:|[popdownView]|" ] options:0
|
||||
metrics:nil views:NSDictionaryOfVariableBindings( popdownView )];
|
||||
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
[[passwordsVC.popdownToTopConstraint updatePriority:1] layoutIfNeeded];
|
||||
} completion:^(BOOL finished) {
|
||||
[popdownVC didMoveToParentViewController:passwordsVC];
|
||||
[passwordsVC.popdownToTopConstraint layoutIfNeeded];
|
||||
[passwordsVC.view endEditing:YES];
|
||||
|
||||
id<NSObject> observer = [[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:
|
||||
^(NSNotification *note) {
|
||||
[[[MPPopdownSegue alloc] initWithIdentifier:@"unwind-popdown" source:popdownVC
|
||||
destination:passwordsVC] perform];
|
||||
}];
|
||||
objc_setAssociatedObject( popdownVC, &UnwindingObserverKey, observer, OBJC_ASSOCIATION_RETAIN );
|
||||
}];
|
||||
[UIView animateWithDuration:0.6f delay:0 usingSpringWithDamping:0.75f initialSpringVelocity:1
|
||||
options:UIViewAnimationOptionCurveEaseOut animations:^{
|
||||
[[passwordsVC.popdownToTopConstraint updatePriority:1] layoutIfNeeded];
|
||||
} completion:^(BOOL finished) {
|
||||
[popdownVC didMoveToParentViewController:passwordsVC];
|
||||
|
||||
PearlAddNotificationObserverTo( popdownVC, MPSignedOutNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(id host, NSNotification *note) {
|
||||
[[[MPPopdownSegue alloc] initWithIdentifier:@"unwind-popdown" source:popdownVC destination:passwordsVC]
|
||||
perform];
|
||||
} );
|
||||
}];
|
||||
}
|
||||
else {
|
||||
popdownVC = self.sourceViewController;
|
||||
@@ -59,16 +59,16 @@ static char UnwindingObserverKey;
|
||||
passwordsVC = (id)passwordsVC.parentViewController);
|
||||
NSAssert( passwordsVC, @"Couldn't find passwords VC to pop back to." );
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:objc_getAssociatedObject( popdownVC, &UnwindingObserverKey )];
|
||||
objc_setAssociatedObject( popdownVC, &UnwindingObserverKey, nil, OBJC_ASSOCIATION_RETAIN );
|
||||
PearlRemoveNotificationObserversFrom( popdownVC );
|
||||
|
||||
[popdownVC willMoveToParentViewController:nil];
|
||||
[UIView animateWithDuration:0.3f delay:0 options:UIViewAnimationOptionOverrideInheritedDuration animations:^{
|
||||
[[passwordsVC.popdownToTopConstraint updatePriority:UILayoutPriorityDefaultHigh] layoutIfNeeded];
|
||||
} completion:^(BOOL finished) {
|
||||
[popdownVC.view removeFromSuperview];
|
||||
[popdownVC removeFromParentViewController];
|
||||
}];
|
||||
[UIView animateWithDuration:0.4f delay:0 options:UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionOverrideInheritedDuration
|
||||
animations:^{
|
||||
[[passwordsVC.popdownToTopConstraint updatePriority:UILayoutPriorityDefaultHigh] layoutIfNeeded];
|
||||
} completion:^(BOOL finished) {
|
||||
[popdownVC.view removeFromSuperview];
|
||||
[popdownVC removeFromParentViewController];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,10 @@
|
||||
@property(weak, nonatomic) IBOutlet UITableViewCell *exportCell;
|
||||
@property(weak, nonatomic) IBOutlet UITableViewCell *checkInconsistencies;
|
||||
@property(weak, nonatomic) IBOutlet UIImageView *avatarImage;
|
||||
@property(weak, nonatomic) IBOutlet UISegmentedControl *generatedTypeControl;
|
||||
@property(weak, nonatomic) IBOutlet UISegmentedControl *generated1TypeControl;
|
||||
@property(weak, nonatomic) IBOutlet UISegmentedControl *generated2TypeControl;
|
||||
@property(weak, nonatomic) IBOutlet UISegmentedControl *storedTypeControl;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *passwordTypeExample;
|
||||
|
||||
- (IBAction)previousAvatar:(id)sender;
|
||||
- (IBAction)nextAvatar:(id)sender;
|
||||
|
||||
@@ -55,12 +55,24 @@
|
||||
- (void)reload {
|
||||
|
||||
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserForMainThread];
|
||||
self.generatedTypeControl.selectedSegmentIndex = [self generatedSegmentIndexForType:activeUser.defaultType];
|
||||
self.storedTypeControl.selectedSegmentIndex = [self storedSegmentIndexForType:activeUser.defaultType];
|
||||
self.avatarImage.image = [UIImage imageNamed:strf( @"avatar-%lu", (unsigned long)activeUser.avatar )];
|
||||
self.savePasswordSwitch.on = activeUser.saveKey;
|
||||
self.touchIDSwitch.on = activeUser.touchID;
|
||||
self.touchIDSwitch.enabled = self.savePasswordSwitch.on && [[MPiOSAppDelegate get] isFeatureUnlocked:MPProductTouchID];
|
||||
|
||||
MPSiteType defaultType = activeUser.defaultType;
|
||||
self.generated1TypeControl.selectedSegmentIndex = [self generated1SegmentIndexForType:defaultType];
|
||||
self.generated2TypeControl.selectedSegmentIndex = [self generated2SegmentIndexForType:defaultType];
|
||||
self.storedTypeControl.selectedSegmentIndex = [self storedSegmentIndexForType:defaultType];
|
||||
PearlNotMainQueue( ^{
|
||||
NSString *examplePassword = nil;
|
||||
if (defaultType & MPSiteTypeClassGenerated)
|
||||
examplePassword = [MPAlgorithmDefault generatePasswordForSiteNamed:@"test" ofType:defaultType
|
||||
withCounter:1 usingKey:[MPiOSAppDelegate get].key];
|
||||
PearlMainQueue( ^{
|
||||
self.passwordTypeExample.text = [examplePassword length]? [NSString stringWithFormat:@"eg. %@", examplePassword]: nil;
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDelegate
|
||||
@@ -88,14 +100,18 @@
|
||||
[self dismissPopup];
|
||||
[[MPiOSAppDelegate get] signOutAnimated:YES];
|
||||
}
|
||||
|
||||
if (cell == self.feedbackCell)
|
||||
[[MPiOSAppDelegate get] showFeedbackWithLogs:YES forVC:self];
|
||||
|
||||
if (cell == self.exportCell)
|
||||
[[MPiOSAppDelegate get] showExportForVC:self];
|
||||
|
||||
if (cell == self.showHelpCell) {
|
||||
MPPasswordsViewController *passwordsVC = [self dismissPopup];
|
||||
[passwordsVC performSegueWithIdentifier:@"guide" sender:self];
|
||||
}
|
||||
|
||||
if (cell == self.checkInconsistencies)
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
if ([[MPiOSAppDelegate get] findAndFixInconsistenciesSaveInContext:context] == MPFixableResultNoProblems)
|
||||
@@ -140,11 +156,13 @@
|
||||
} );
|
||||
}];
|
||||
|
||||
if (sender == self.generatedTypeControl || sender == self.storedTypeControl) {
|
||||
if (sender == self.generatedTypeControl)
|
||||
if (sender == self.generated1TypeControl || sender == self.generated2TypeControl || sender == self.storedTypeControl) {
|
||||
if (sender != self.generated1TypeControl)
|
||||
self.generated1TypeControl.selectedSegmentIndex = -1;
|
||||
if (sender != self.generated2TypeControl)
|
||||
self.generated2TypeControl.selectedSegmentIndex = -1;
|
||||
if (sender != self.storedTypeControl)
|
||||
self.storedTypeControl.selectedSegmentIndex = -1;
|
||||
else if (sender == self.storedTypeControl)
|
||||
self.generatedTypeControl.selectedSegmentIndex = -1;
|
||||
|
||||
MPSiteType defaultType = [self typeForSelectedSegment];
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
@@ -189,13 +207,13 @@
|
||||
- (IBAction)homePageButton:(id)sender {
|
||||
|
||||
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender:
|
||||
[NSURL URLWithString:@"http://masterpasswordapp.com"]];
|
||||
[NSURL URLWithString:@"https://ssl.masterpasswordapp.com"]];
|
||||
}
|
||||
|
||||
- (IBAction)securityButton:(id)sender {
|
||||
|
||||
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender:
|
||||
[NSURL URLWithString:@"http://masterpasswordapp.com/security.html"]];
|
||||
[NSURL URLWithString:@"https://ssl.masterpasswordapp.com/security.html"]];
|
||||
}
|
||||
|
||||
- (IBAction)sourceButton:(id)sender {
|
||||
@@ -207,7 +225,7 @@
|
||||
- (IBAction)thanksButton:(id)sender {
|
||||
|
||||
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender:
|
||||
[NSURL URLWithString:@"http://thanks.lhunath.com"]];
|
||||
[NSURL URLWithString:@"https://thanks.lhunath.com"]];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
@@ -226,10 +244,17 @@
|
||||
|
||||
- (MPSiteType)typeForSelectedSegment {
|
||||
|
||||
NSInteger selectedGeneratedIndex = self.generatedTypeControl.selectedSegmentIndex;
|
||||
NSInteger selectedGenerated1Index = self.generated1TypeControl.selectedSegmentIndex;
|
||||
NSInteger selectedGenerated2Index = self.generated2TypeControl.selectedSegmentIndex;
|
||||
NSInteger selectedStoredIndex = self.storedTypeControl.selectedSegmentIndex;
|
||||
|
||||
switch (selectedGeneratedIndex) {
|
||||
switch (selectedGenerated1Index) {
|
||||
case 0:
|
||||
return MPSiteTypeGeneratedPhrase;
|
||||
case 1:
|
||||
return MPSiteTypeGeneratedName;
|
||||
default:
|
||||
switch (selectedGenerated2Index) {
|
||||
case 0:
|
||||
return MPSiteTypeGeneratedMaximum;
|
||||
case 1:
|
||||
@@ -250,13 +275,26 @@
|
||||
case 1:
|
||||
return MPSiteTypeStoredDevicePrivate;
|
||||
default:
|
||||
Throw( @"unsupported selected type index: generated=%ld, stored=%ld", (long)selectedGeneratedIndex,
|
||||
(long)selectedStoredIndex );
|
||||
Throw( @"unsupported selected type index: generated1=%ld, generated2=%ld, stored=%ld",
|
||||
(long)selectedGenerated1Index, (long)selectedGenerated2Index, (long)selectedStoredIndex );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)generatedSegmentIndexForType:(MPSiteType)type {
|
||||
- (NSInteger)generated1SegmentIndexForType:(MPSiteType)type {
|
||||
|
||||
switch (type) {
|
||||
case MPSiteTypeGeneratedPhrase:
|
||||
return 0;
|
||||
case MPSiteTypeGeneratedName:
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)generated2SegmentIndexForType:(MPSiteType)type {
|
||||
|
||||
switch (type) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
|
||||
@@ -17,20 +17,11 @@
|
||||
//==============================================================================
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <StoreKit/StoreKit.h>
|
||||
|
||||
@class MPStoreProductCell;
|
||||
|
||||
@interface MPStoreViewController : PearlMutableStaticTableViewController
|
||||
|
||||
@property(weak, nonatomic) IBOutlet MPStoreProductCell *generateLoginCell;
|
||||
@property(weak, nonatomic) IBOutlet MPStoreProductCell *generateAnswersCell;
|
||||
@property(weak, nonatomic) IBOutlet MPStoreProductCell *iOSIntegrationCell;
|
||||
@property(weak, nonatomic) IBOutlet MPStoreProductCell *touchIDCell;
|
||||
@property(weak, nonatomic) IBOutlet MPStoreProductCell *fuelCell;
|
||||
@property(weak, nonatomic) IBOutlet UITableViewCell *loadingCell;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *fuelMeterConstraint;
|
||||
@property(weak, nonatomic) IBOutlet UIButton *fuelSpeedButton;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *fuelStatusLabel;
|
||||
@interface MPStoreViewController : UITableViewController
|
||||
|
||||
+ (NSString *)latestStoreFeatures;
|
||||
|
||||
@@ -39,7 +30,23 @@
|
||||
@interface MPStoreProductCell : UITableViewCell
|
||||
|
||||
@property(nonatomic) IBOutlet UILabel *priceLabel;
|
||||
@property(nonatomic) IBOutlet UILabel *titleLabel;
|
||||
@property(nonatomic) IBOutlet UILabel *descriptionLabel;
|
||||
@property(nonatomic) IBOutlet UIImageView *thumbnailView;
|
||||
@property(nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
|
||||
@property(nonatomic) IBOutlet UIView *purchasedIndicator;
|
||||
|
||||
@property(nonatomic, readonly) SKProduct *product;
|
||||
@property(nonatomic, readonly) NSInteger quantity;
|
||||
|
||||
- (void)updateWithProduct:(SKProduct *)product transaction:(SKPaymentTransaction *)transaction;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPStoreFuelProductCell : MPStoreProductCell
|
||||
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *fuelMeterConstraint;
|
||||
@property(weak, nonatomic) IBOutlet UIButton *fuelSpeedButton;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *fuelStatusLabel;
|
||||
|
||||
@end
|
||||
|
||||
@@ -27,8 +27,9 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
|
||||
@interface MPStoreViewController()<MPInAppDelegate>
|
||||
|
||||
@property(nonatomic, strong) NSNumberFormatter *currencyFormatter;
|
||||
@property(nonatomic, strong) NSArray *products;
|
||||
@property(nonatomic, strong) NSDictionary<NSString *, SKProduct *> *products;
|
||||
@property(nonatomic, strong) NSDictionary<NSString *, SKPaymentTransaction *> *transactions;
|
||||
@property(nonatomic, strong) NSMutableArray<NSArray *> *dataSource;
|
||||
|
||||
@end
|
||||
|
||||
@@ -43,7 +44,7 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
];
|
||||
NSInteger storeVersion = [[NSUserDefaults standardUserDefaults] integerForKey:@"storeVersion"];
|
||||
for (; storeVersion < [storeVersions count]; ++storeVersion)
|
||||
[features appendFormat:@"%@\n", storeVersions[storeVersion]];
|
||||
[features appendFormat:@"%@\n", storeVersions[(NSUInteger)storeVersion]];
|
||||
if (![features length])
|
||||
return nil;
|
||||
|
||||
@@ -57,14 +58,13 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
|
||||
[super viewDidLoad];
|
||||
|
||||
self.currencyFormatter = [NSNumberFormatter new];
|
||||
self.currencyFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
|
||||
|
||||
self.tableView.tableHeaderView = [UIView new];
|
||||
self.tableView.tableFooterView = [UIView new];
|
||||
self.tableView.rowHeight = UITableViewAutomaticDimension;
|
||||
self.tableView.estimatedRowHeight = 400;
|
||||
self.view.backgroundColor = [UIColor clearColor];
|
||||
|
||||
self.dataSource = [@[ @[], @[ @"MPStoreCellSpinner", @"MPStoreCellFooter" ] ] mutableCopy];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
@@ -73,50 +73,44 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
|
||||
self.tableView.contentInset = UIEdgeInsetsMake( 64, 0, 49, 0 );
|
||||
|
||||
[self reloadCellsHiding:self.allCellsBySection[0] showing:@[ self.loadingCell ]];
|
||||
[self.allCellsBySection[0] enumerateObjectsUsingBlock:^(MPStoreProductCell *cell, NSUInteger idx, BOOL *stop) {
|
||||
if ([cell isKindOfClass:[MPStoreProductCell class]]) {
|
||||
cell.purchasedIndicator.alpha = 0;
|
||||
[cell.activityIndicator stopAnimating];
|
||||
}
|
||||
}];
|
||||
|
||||
PearlAddNotificationObserver( NSUserDefaultsDidChangeNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPStoreViewController *self, NSNotification *note) {
|
||||
[self updateProducts];
|
||||
[self updateFuel];
|
||||
} );
|
||||
[[MPiOSAppDelegate get] registerProductsObserver:self];
|
||||
[self updateFuel];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
PearlRemoveNotificationObservers();
|
||||
[[MPiOSAppDelegate get] removeProductsObserver:self];
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
|
||||
- (MPStoreProductCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
|
||||
MPStoreProductCell *cell = (MPStoreProductCell *)[super tableView:tableView cellForRowAtIndexPath:indexPath];
|
||||
if (indexPath.section == 0)
|
||||
cell.selectionStyle = [[MPiOSAppDelegate get] isFeatureUnlocked:[self productForCell:cell].productIdentifier]?
|
||||
UITableViewCellSelectionStyleNone: UITableViewCellSelectionStyleDefault;
|
||||
|
||||
if (cell.selectionStyle != UITableViewCellSelectionStyleNone) {
|
||||
cell.selectedBackgroundView = [[UIView alloc] initWithFrame:cell.bounds];
|
||||
cell.selectedBackgroundView.backgroundColor = [UIColor colorWithRGBAHex:0x78DDFB33];
|
||||
}
|
||||
|
||||
return cell;
|
||||
return [self.dataSource count];
|
||||
}
|
||||
|
||||
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
|
||||
return NO;
|
||||
return [self.dataSource[(NSUInteger)section] count];
|
||||
}
|
||||
|
||||
- (MPStoreProductCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
id content = self.dataSource[(NSUInteger)indexPath.section][(NSUInteger)indexPath.row];
|
||||
if ([content isKindOfClass:[SKProduct class]]) {
|
||||
SKProduct *product = content;
|
||||
MPStoreProductCell *cell;
|
||||
if ([product.productIdentifier isEqualToString:MPProductFuel])
|
||||
cell = [MPStoreFuelProductCell dequeueCellFromTableView:tableView indexPath:indexPath];
|
||||
else
|
||||
cell = [MPStoreProductCell dequeueCellFromTableView:tableView indexPath:indexPath];
|
||||
[cell updateWithProduct:product transaction:self.transactions[product.productIdentifier]];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
return [tableView dequeueReusableCellWithIdentifier:content forIndexPath:indexPath];
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDelegate
|
||||
@@ -128,14 +122,13 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
MPStoreProductCell *cell = (MPStoreProductCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath];
|
||||
if (cell.selectionStyle == UITableViewCellSelectionStyleNone)
|
||||
return;
|
||||
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
|
||||
if (cell.selectionStyle != UITableViewCellSelectionStyleNone && [cell isKindOfClass:[MPStoreProductCell class]]) {
|
||||
MPStoreProductCell *productCell = (MPStoreProductCell *)cell;
|
||||
|
||||
SKProduct *product = [self productForCell:cell];
|
||||
if (product && ![[MPAppDelegate_Shared get] isFeatureUnlocked:product.productIdentifier])
|
||||
[[MPAppDelegate_Shared get] purchaseProductWithIdentifier:product.productIdentifier
|
||||
quantity:[self quantityForProductIdentifier:product.productIdentifier]];
|
||||
if (productCell.product && ![[MPAppDelegate_Shared get] isFeatureUnlocked:productCell.product.productIdentifier])
|
||||
[[MPAppDelegate_Shared get] purchaseProductWithIdentifier:productCell.product.productIdentifier quantity:productCell.quantity];
|
||||
}
|
||||
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
}
|
||||
@@ -146,7 +139,9 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
|
||||
NSUInteger fuelConsumption = [[MPiOSConfig get].developmentFuelConsumption unsignedIntegerValue];
|
||||
[MPiOSConfig get].developmentFuelConsumption = @((fuelConsumption + 1) % MPDevelopmentFuelConsumptionCount);
|
||||
[self updateProducts];
|
||||
|
||||
[self.tableView updateDataSource:self.dataSource toSections:nil reloadItems:@[ self.products[MPProductFuel] ]
|
||||
withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
}
|
||||
|
||||
- (IBAction)restorePurchases:(id)sender {
|
||||
@@ -165,41 +160,33 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
- (IBAction)sendThanks:(id)sender {
|
||||
|
||||
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender:
|
||||
[NSURL URLWithString:@"http://thanks.lhunath.com"]];
|
||||
[NSURL URLWithString:@"https://thanks.lhunath.com"]];
|
||||
}
|
||||
|
||||
#pragma mark - MPInAppDelegate
|
||||
|
||||
- (void)updateWithProducts:(NSArray *)products {
|
||||
- (void)updateWithProducts:(NSDictionary<NSString *, SKProduct *> *)products
|
||||
transactions:(NSDictionary<NSString *, SKPaymentTransaction *> *)transactions {
|
||||
|
||||
self.products = products;
|
||||
self.transactions = transactions;
|
||||
NSMutableArray *newDataSource = [NSMutableArray arrayWithCapacity:2];
|
||||
|
||||
[self updateProducts];
|
||||
}
|
||||
// Section 0: products
|
||||
[newDataSource addObject:[[products allValues] sortedArrayUsingComparator:
|
||||
^NSComparisonResult(SKProduct *p1, SKProduct *p2) {
|
||||
return [p1.productIdentifier compare:p2.productIdentifier];
|
||||
}]];
|
||||
NSArray *reloadProducts = [newDataSource[0] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:
|
||||
^BOOL(SKProduct *product, NSDictionary *bindings) {
|
||||
return self.transactions[product.productIdentifier] != nil;
|
||||
}]];
|
||||
|
||||
- (void)updateWithTransaction:(SKPaymentTransaction *)transaction {
|
||||
// Section 1: information cells
|
||||
[newDataSource addObject:@[ @"MPStoreCellFooter" ]];
|
||||
|
||||
MPStoreProductCell *cell = [self cellForProductIdentifier:transaction.payment.productIdentifier];
|
||||
if (!cell)
|
||||
return;
|
||||
|
||||
switch (transaction.transactionState) {
|
||||
case SKPaymentTransactionStatePurchasing:
|
||||
[cell.activityIndicator startAnimating];
|
||||
break;
|
||||
case SKPaymentTransactionStatePurchased:
|
||||
[cell.activityIndicator stopAnimating];
|
||||
break;
|
||||
case SKPaymentTransactionStateFailed:
|
||||
[cell.activityIndicator stopAnimating];
|
||||
break;
|
||||
case SKPaymentTransactionStateRestored:
|
||||
[cell.activityIndicator stopAnimating];
|
||||
break;
|
||||
case SKPaymentTransactionStateDeferred:
|
||||
[cell.activityIndicator startAnimating];
|
||||
break;
|
||||
}
|
||||
[self.tableView updateDataSource:self.dataSource toSections:newDataSource
|
||||
reloadItems:reloadProducts withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
@@ -216,60 +203,80 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (SKProduct *)productForCell:(MPStoreProductCell *)cell {
|
||||
@end
|
||||
|
||||
for (SKProduct *product in self.products)
|
||||
if ([self cellForProductIdentifier:product.productIdentifier] == cell)
|
||||
return product;
|
||||
@implementation MPStoreProductCell
|
||||
|
||||
- (void)updateWithProduct:(SKProduct *)product transaction:(SKPaymentTransaction *)transaction {
|
||||
|
||||
_product = product;
|
||||
|
||||
BOOL purchased = [[MPiOSAppDelegate get] isFeatureUnlocked:self.product.productIdentifier];
|
||||
self.selectionStyle = purchased? UITableViewCellSelectionStyleNone: UITableViewCellSelectionStyleDefault;
|
||||
self.selectedBackgroundView = self.selectionStyle == UITableViewCellSelectionStyleNone? nil: [[UIView alloc] initWithFrame:self.bounds];
|
||||
self.selectedBackgroundView.backgroundColor = [UIColor colorWithRGBAHex:0x78DDFB33];
|
||||
|
||||
self.purchasedIndicator.visible = purchased;
|
||||
self.priceLabel.text = purchased? @"": [self price];
|
||||
self.titleLabel.text = product.localizedTitle;
|
||||
self.descriptionLabel.text = product.localizedDescription;
|
||||
self.thumbnailView.image = [self productImage];
|
||||
|
||||
if (transaction && (transaction.transactionState == SKPaymentTransactionStateDeferred ||
|
||||
transaction.transactionState == SKPaymentTransactionStatePurchasing))
|
||||
[self.activityIndicator startAnimating];
|
||||
else
|
||||
[self.activityIndicator stopAnimating];
|
||||
}
|
||||
|
||||
- (UIImage *)productImage {
|
||||
|
||||
if ([MPProductGenerateLogins isEqualToString:self.product.productIdentifier])
|
||||
return [UIImage imageNamed:@"thumb_generated_login"];
|
||||
if ([MPProductGenerateAnswers isEqualToString:self.product.productIdentifier])
|
||||
return [UIImage imageNamed:@"thumb_generated_answers"];
|
||||
if ([MPProductOSIntegration isEqualToString:self.product.productIdentifier])
|
||||
return [UIImage imageNamed:@"thumb_ios_integration"];
|
||||
if ([MPProductTouchID isEqualToString:self.product.productIdentifier])
|
||||
return [UIImage imageNamed:@"thumb_touch_id"];
|
||||
if ([MPProductFuel isEqualToString:self.product.productIdentifier])
|
||||
return [UIImage imageNamed:@"thumb_fuel"];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (MPStoreProductCell *)cellForProductIdentifier:(NSString *)productIdentifier {
|
||||
- (NSString *)price {
|
||||
|
||||
if ([productIdentifier isEqualToString:MPProductGenerateLogins])
|
||||
return self.generateLoginCell;
|
||||
if ([productIdentifier isEqualToString:MPProductGenerateAnswers])
|
||||
return self.generateAnswersCell;
|
||||
if ([productIdentifier isEqualToString:MPProductOSIntegration])
|
||||
return self.iOSIntegrationCell;
|
||||
if ([productIdentifier isEqualToString:MPProductTouchID])
|
||||
return self.touchIDCell;
|
||||
if ([productIdentifier isEqualToString:MPProductFuel])
|
||||
return self.fuelCell;
|
||||
NSNumberFormatter *currencyFormatter = [NSNumberFormatter new];
|
||||
currencyFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
|
||||
currencyFormatter.locale = self.product.priceLocale;
|
||||
|
||||
return nil;
|
||||
return [currencyFormatter stringFromNumber:@([self.product.price floatValue] * self.quantity)];
|
||||
}
|
||||
|
||||
- (void)updateProducts {
|
||||
- (NSInteger)quantity {
|
||||
|
||||
NSMutableArray *showCells = [NSMutableArray array];
|
||||
NSMutableArray *hideCells = [NSMutableArray array];
|
||||
[hideCells addObjectsFromArray:[self.allCellsBySection[0] array]];
|
||||
[hideCells addObject:self.loadingCell];
|
||||
|
||||
for (SKProduct *product in self.products) {
|
||||
[self showCellForProductWithIdentifier:MPProductGenerateLogins ifProduct:product showingCells:showCells];
|
||||
[self showCellForProductWithIdentifier:MPProductGenerateAnswers ifProduct:product showingCells:showCells];
|
||||
[self showCellForProductWithIdentifier:MPProductOSIntegration ifProduct:product showingCells:showCells];
|
||||
[self showCellForProductWithIdentifier:MPProductTouchID ifProduct:product showingCells:showCells];
|
||||
[self showCellForProductWithIdentifier:MPProductFuel ifProduct:product showingCells:showCells];
|
||||
}
|
||||
|
||||
[hideCells removeObjectsInArray:showCells];
|
||||
[self reloadCellsHiding:hideCells showing:showCells];
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (void)updateFuel {
|
||||
@end
|
||||
|
||||
@implementation MPStoreFuelProductCell
|
||||
|
||||
- (void)updateWithProduct:(SKProduct *)product transaction:(SKPaymentTransaction *)transaction {
|
||||
|
||||
[super updateWithProduct:product transaction:transaction];
|
||||
|
||||
CGFloat weeklyFuelConsumption = [self weeklyFuelConsumption]; /* consume x fuel / week */
|
||||
[self.fuelSpeedButton setTitle:[self weeklyFuelConsumptionTitle] forState:UIControlStateNormal];
|
||||
|
||||
NSTimeInterval fuelSecondsElapsed = 0;
|
||||
CGFloat fuelRemaining = [[MPiOSConfig get].developmentFuelRemaining floatValue]; /* x fuel left */
|
||||
CGFloat fuelInvested = [[MPiOSConfig get].developmentFuelInvested floatValue]; /* x fuel left */
|
||||
NSDate *now = [NSDate date];
|
||||
NSTimeInterval fuelSecondsElapsed = -[[MPiOSConfig get].developmentFuelChecked timeIntervalSinceDate:now];
|
||||
if (fuelSecondsElapsed > 3600 || ![MPiOSConfig get].developmentFuelChecked) {
|
||||
NSDate *now = [NSDate date], *checked = [MPiOSConfig get].developmentFuelChecked;
|
||||
if (!checked || 3600 < (fuelSecondsElapsed = [now timeIntervalSinceDate:checked])) {
|
||||
NSTimeInterval weeksElapsed = fuelSecondsElapsed / (3600 * 24 * 7 /* 1 week */); /* x weeks elapsed */
|
||||
NSTimeInterval fuelConsumed = weeklyFuelConsumption * weeksElapsed;
|
||||
NSTimeInterval fuelConsumed = MIN( fuelRemaining, weeklyFuelConsumption * weeksElapsed );
|
||||
fuelRemaining -= fuelConsumed;
|
||||
fuelInvested += fuelConsumed;
|
||||
[MPiOSConfig get].developmentFuelChecked = now;
|
||||
@@ -277,54 +284,43 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
[MPiOSConfig get].developmentFuelInvested = @(fuelInvested);
|
||||
}
|
||||
|
||||
CGFloat fuelRatio = weeklyFuelConsumption == 0? 0: fuelRemaining / weeklyFuelConsumption; /* x weeks worth of fuel left */
|
||||
[self.fuelMeterConstraint updateConstant:MIN( 0.5f, fuelRatio - 0.5f ) * 160]; /* -80pt = 0 weeks left, 80pt = >=1 week left */
|
||||
self.fuelStatusLabel.text = strf( @"fuel left: %0.1f work hours\ninvested: %0.1f work hours", fuelRemaining, fuelInvested );
|
||||
CGFloat fuelRatio = weeklyFuelConsumption? fuelRemaining / weeklyFuelConsumption: 0; /* x weeks worth of fuel left */
|
||||
[self.fuelMeterConstraint updateConstant:MIN( 0.5f, fuelRatio - 0.5f ) * 160]; /* -80pt = 0 weeks left, +80pt = >=1 week left */
|
||||
self.fuelStatusLabel.text = strf( @"Fuel left: %0.1f work hours\nFunded: %0.1f work hours", fuelRemaining, fuelInvested );
|
||||
self.fuelStatusLabel.hidden = (fuelRemaining + fuelInvested) == 0;
|
||||
}
|
||||
|
||||
- (NSInteger)quantity {
|
||||
|
||||
return MAX( 1, (NSInteger)ceil( MP_FUEL_HOURLY_RATE * [self weeklyFuelConsumption] ) );
|
||||
}
|
||||
|
||||
- (CGFloat)weeklyFuelConsumption {
|
||||
|
||||
switch ((MPDevelopmentFuelConsumption)[[MPiOSConfig get].developmentFuelConsumption unsignedIntegerValue]) {
|
||||
case MPDevelopmentFuelConsumptionQuarterly:
|
||||
[self.fuelSpeedButton setTitle:@"1h / quarter" forState:UIControlStateNormal];
|
||||
return 1.f / 12 /* 12 weeks */;
|
||||
case MPDevelopmentFuelConsumptionMonthly:
|
||||
[self.fuelSpeedButton setTitle:@"1h / month" forState:UIControlStateNormal];
|
||||
return 1.f / 4 /* 4 weeks */;
|
||||
case MPDevelopmentFuelWeekly:
|
||||
[self.fuelSpeedButton setTitle:@"1h / week" forState:UIControlStateNormal];
|
||||
return 1.f;
|
||||
return 1.f /* 1 week */;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (void)showCellForProductWithIdentifier:(NSString *)productIdentifier ifProduct:(SKProduct *)product
|
||||
showingCells:(NSMutableArray *)showCells {
|
||||
- (NSString *)weeklyFuelConsumptionTitle {
|
||||
|
||||
if (![product.productIdentifier isEqualToString:productIdentifier])
|
||||
return;
|
||||
switch ((MPDevelopmentFuelConsumption)[[MPiOSConfig get].developmentFuelConsumption unsignedIntegerValue]) {
|
||||
case MPDevelopmentFuelConsumptionQuarterly:
|
||||
return @"1h / quarter";
|
||||
case MPDevelopmentFuelConsumptionMonthly:
|
||||
return @"1h / month";
|
||||
case MPDevelopmentFuelWeekly:
|
||||
return @"1h / week";
|
||||
}
|
||||
|
||||
MPStoreProductCell *cell = [self cellForProductIdentifier:productIdentifier];
|
||||
[showCells addObject:cell];
|
||||
|
||||
self.currencyFormatter.locale = product.priceLocale;
|
||||
BOOL purchased = [[MPiOSAppDelegate get] isFeatureUnlocked:productIdentifier];
|
||||
NSInteger quantity = [self quantityForProductIdentifier:productIdentifier];
|
||||
cell.priceLabel.text = purchased? @"": [self.currencyFormatter stringFromNumber:@([product.price floatValue] * quantity)];
|
||||
cell.purchasedIndicator.alpha = purchased? 1: 0;
|
||||
}
|
||||
|
||||
- (NSInteger)quantityForProductIdentifier:(NSString *)productIdentifier {
|
||||
|
||||
if ([productIdentifier isEqualToString:MPProductFuel])
|
||||
return (NSInteger)(MP_FUEL_HOURLY_RATE * [self weeklyFuelConsumption] + .5f);
|
||||
|
||||
return 1;
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPStoreProductCell
|
||||
@end
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
|
||||
inf( @"Type selection will appear" );
|
||||
self.recommendedTipContainer.alpha = 0;
|
||||
self.recommendedTipContainer.visible = NO;
|
||||
|
||||
[super viewWillAppear:animated];
|
||||
}
|
||||
@@ -42,15 +42,13 @@
|
||||
|
||||
if ([[MPiOSConfig get].firstRun boolValue])
|
||||
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{
|
||||
self.recommendedTipContainer.alpha = 1;
|
||||
self.recommendedTipContainer.visible = YES;
|
||||
} completion:^(BOOL finished) {
|
||||
if (finished) {
|
||||
dispatch_after( dispatch_time( DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC) ), dispatch_get_main_queue(), ^{
|
||||
[UIView animateWithDuration:0.2f animations:^{
|
||||
self.recommendedTipContainer.alpha = 0;
|
||||
}];
|
||||
} );
|
||||
}
|
||||
PearlMainQueueAfter( 5, ^{
|
||||
[UIView animateWithDuration:0.2f animations:^{
|
||||
self.recommendedTipContainer.visible = NO;
|
||||
}];
|
||||
} );
|
||||
}];
|
||||
|
||||
[super viewDidAppear:animated];
|
||||
@@ -89,11 +87,11 @@
|
||||
if ([selectedSite isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||
counter = ((MPGeneratedSiteEntity *)selectedSite).counter;
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{
|
||||
PearlNotMainQueue( ^{
|
||||
NSString *typeContent = [MPAlgorithmDefault generatePasswordForSiteNamed:name ofType:cellType
|
||||
withCounter:counter usingKey:[MPiOSAppDelegate get].key];
|
||||
|
||||
dispatch_async( dispatch_get_main_queue(), ^{
|
||||
PearlMainQueue( ^{
|
||||
[(UITextField *)[[tableView cellForRowAtIndexPath:indexPath] viewWithTag:2] setText:typeContent];
|
||||
} );
|
||||
} );
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#import <Crashlytics/Answers.h>
|
||||
#import "MPUsersViewController.h"
|
||||
#import "MPEntities.h"
|
||||
#import "MPAvatarCell.h"
|
||||
@@ -58,6 +59,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||
@implementation MPUsersViewController {
|
||||
NSString *_masterPasswordChoice;
|
||||
NSOperationQueue *_afterUpdates;
|
||||
__weak id _contextChangedObserver;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
@@ -76,7 +78,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||
self.avatarCollectionView.allowsMultipleSelection = YES;
|
||||
[self.entryField addTarget:self action:@selector( textFieldEditingChanged: ) forControlEvents:UIControlEventEditingChanged];
|
||||
|
||||
self.preferencesTipContainer.alpha = 0;
|
||||
self.preferencesTipContainer.visible = NO;
|
||||
|
||||
[self setActive:YES animated:NO];
|
||||
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"tipped.thanks"])
|
||||
@@ -87,16 +89,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
self.userSelectionContainer.alpha = 0;
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
PearlRemoveNotificationObservers();
|
||||
|
||||
[self.marqueeTipTimer invalidate];
|
||||
self.userSelectionContainer.visible = NO;
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
@@ -126,8 +119,18 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
|
||||
if ([segue.identifier isEqualToString:@"web"])
|
||||
((MPWebViewController *)segue.destinationViewController).initialURL = [NSURL URLWithString:@"http://thanks.lhunath.com"];
|
||||
if ([segue.identifier isEqualToString:@"thanks"])
|
||||
((MPWebViewController *)segue.destinationViewController).initialURL =
|
||||
[NSURL URLWithString:@"https://thanks.lhunath.com"];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
[self removeObservers];
|
||||
|
||||
[self.marqueeTipTimer invalidate];
|
||||
}
|
||||
|
||||
#pragma mark - UITextFieldDelegate
|
||||
@@ -220,8 +223,20 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||
if (isNew) {
|
||||
user = [MPUserEntity insertNewObjectInContext:context];
|
||||
user.algorithm = MPAlgorithmDefault;
|
||||
user.defaultType = user.algorithm.defaultType;
|
||||
user.avatar = newUserAvatar;
|
||||
user.name = newUserName;
|
||||
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
#ifdef CRASHLYTICS
|
||||
[Answers logSignUpWithMethod:@"Manual"
|
||||
success:@YES
|
||||
customAttributes:@{
|
||||
@"algorithm": @(user.algorithm.version),
|
||||
@"avatar" : @(user.avatar),
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context
|
||||
@@ -258,6 +273,9 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||
// This isn't really in UITextFieldDelegate. We fake it from UITextFieldTextDidChangeNotification.
|
||||
- (void)textFieldEditingChanged:(UITextField *)textField {
|
||||
|
||||
if ([[textField.text lowercaseString] isEqualToString:@"hangtest"])
|
||||
[NSThread sleepForTimeInterval:10];
|
||||
|
||||
if (textField == self.entryField) {
|
||||
switch (self.activeUserState) {
|
||||
case MPActiveUserStateNone:
|
||||
@@ -367,7 +385,7 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
MPAvatarCell *userAvatar = [self selectedAvatar];
|
||||
userAvatar.spinnerActive = YES;
|
||||
if (!isNew && mainUser && [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPUserEntity *user = [MPUserEntity existingObjectWithID:mainUser.objectID inContext:context];
|
||||
MPUserEntity *user = [MPUserEntity existingObjectWithID:mainUser.permanentObjectID inContext:context];
|
||||
BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context usingMasterPassword:nil];
|
||||
|
||||
PearlMainQueue( ^{
|
||||
@@ -410,10 +428,10 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
|
||||
BOOL isNew = NO;
|
||||
MPUserEntity *user = [self userForAvatar:avatarCell inContext:mainContext isNew:&isNew];
|
||||
NSManagedObjectID *userID = user.objectID;
|
||||
if (isNew || !user)
|
||||
return;
|
||||
|
||||
NSManagedObjectID *userID = user.permanentObjectID;
|
||||
[PearlSheet showSheetWithTitle:user.name
|
||||
viewStyle:UIActionSheetStyleBlackTranslucent
|
||||
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
|
||||
@@ -464,25 +482,23 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
- (void)deleteUser:(NSManagedObjectID *)userID {
|
||||
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPUserEntity
|
||||
*user_ = [MPUserEntity existingObjectWithID:userID inContext:context];
|
||||
if (!user_)
|
||||
MPUserEntity *user = [MPUserEntity existingObjectWithID:userID inContext:context];
|
||||
if (!user)
|
||||
return;
|
||||
|
||||
[context deleteObject:user_];
|
||||
[context deleteObject:user];
|
||||
[context saveToStore];
|
||||
[self reloadUsers]; // I do NOT understand why our ObjectsDidChangeNotification isn't firing on saveToStore.
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)resetUser:(NSManagedObjectID *)userID avatar:(MPAvatarCell *)avatarCell {
|
||||
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPUserEntity *user_ = [MPUserEntity existingObjectWithID:userID inContext:context];
|
||||
if (!user_)
|
||||
MPUserEntity *user = [MPUserEntity existingObjectWithID:userID inContext:context];
|
||||
if (!user)
|
||||
return;
|
||||
|
||||
[[MPiOSAppDelegate get] changeMasterPasswordFor:user_ saveInContext:context didResetBlock:^{
|
||||
[[MPiOSAppDelegate get] changeMasterPasswordFor:user saveInContext:context didResetBlock:^{
|
||||
PearlMainQueue( ^{
|
||||
NSIndexPath *avatarIndexPath = [self.avatarCollectionView indexPathForCell:avatarCell];
|
||||
[self.avatarCollectionView selectItemAtIndexPath:avatarIndexPath animated:NO
|
||||
@@ -497,25 +513,25 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
if (showTips & MPUsersThanksTip)
|
||||
self.thanksTipContainer.alpha = 1;
|
||||
self.thanksTipContainer.visible = YES;
|
||||
if (showTips & MPUsersAvatarTip)
|
||||
self.avatarTipContainer.alpha = 1;
|
||||
self.avatarTipContainer.visible = YES;
|
||||
if (showTips & MPUsersMasterPasswordTip)
|
||||
self.entryTipContainer.alpha = 1;
|
||||
self.entryTipContainer.visible = YES;
|
||||
if (showTips & MPUsersPreferencesTip)
|
||||
self.preferencesTipContainer.alpha = 1;
|
||||
self.preferencesTipContainer.visible = YES;
|
||||
} completion:^(BOOL finished) {
|
||||
if (finished)
|
||||
PearlMainQueueAfter( 5, ^{
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
if (showTips & MPUsersThanksTip)
|
||||
self.thanksTipContainer.alpha = 0;
|
||||
self.thanksTipContainer.visible = NO;
|
||||
if (showTips & MPUsersAvatarTip)
|
||||
self.avatarTipContainer.alpha = 0;
|
||||
self.avatarTipContainer.visible = NO;
|
||||
if (showTips & MPUsersMasterPasswordTip)
|
||||
self.entryTipContainer.alpha = 0;
|
||||
self.entryTipContainer.visible = NO;
|
||||
if (showTips & MPUsersPreferencesTip)
|
||||
self.preferencesTipContainer.alpha = 0;
|
||||
self.preferencesTipContainer.visible = NO;
|
||||
}];
|
||||
} );
|
||||
}];
|
||||
@@ -538,14 +554,14 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
return;
|
||||
|
||||
[UIView animateWithDuration:timer? 0.5f: 0 animations:^{
|
||||
self.marqueeButton.alpha = 0;
|
||||
self.marqueeButton.visible = NO;
|
||||
} completion:^(BOOL finished) {
|
||||
if (!finished)
|
||||
return;
|
||||
|
||||
[self.marqueeButton setTitle:nextMarqueeString forState:UIControlStateNormal];
|
||||
[UIView animateWithDuration:timer? 0.5f: 0 animations:^{
|
||||
self.marqueeButton.alpha = 0.5f;
|
||||
self.marqueeButton.visible = YES;
|
||||
}];
|
||||
}];
|
||||
}
|
||||
@@ -588,8 +604,6 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
|
||||
- (void)updateAvatarVisibility {
|
||||
|
||||
self.previousAvatarButton.alpha = 0;
|
||||
self.nextAvatarButton.alpha = 0;
|
||||
for (NSIndexPath *indexPath in self.avatarCollectionView.indexPathsForVisibleItems) {
|
||||
MPAvatarCell *cell = (MPAvatarCell *)[self.avatarCollectionView cellForItemAtIndexPath:indexPath];
|
||||
[self updateVisibilityForAvatar:cell atIndexPath:indexPath animated:NO];
|
||||
@@ -633,10 +647,10 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
CGFloat visibility = MAX( 0, MIN( 1, 1 - ABS( current / (max / 2) - 1 ) ) );
|
||||
[cell setVisibility:visibility animated:animated];
|
||||
|
||||
if (cell.newUser) {
|
||||
self.previousAvatarButton.alpha = cell.mode == MPAvatarModeRaisedAndActive? visibility * 0.7f: 0;
|
||||
self.nextAvatarButton.alpha = cell.mode == MPAvatarModeRaisedAndActive? visibility * 0.7f: 0;
|
||||
}
|
||||
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{
|
||||
self.nextAvatarButton.visible = self.previousAvatarButton.visible = cell.newUser && cell.mode == MPAvatarModeRaisedAndActive;
|
||||
self.nextAvatarButton.alpha = self.previousAvatarButton.alpha = visibility * 0.7f;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)afterUpdatesMainQueue:(void ( ^ )(void))block {
|
||||
@@ -646,18 +660,24 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)registerObservers {
|
||||
- (void)removeObservers {
|
||||
|
||||
[self removeKeyPathObservers];
|
||||
PearlRemoveNotificationObservers();
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:_contextChangedObserver];
|
||||
}
|
||||
|
||||
- (void)registerObservers {
|
||||
|
||||
[self removeObservers];
|
||||
[self observeKeyPath:@"avatarCollectionView.contentOffset" withBlock:
|
||||
^(id from, id to, NSKeyValueChange cause, MPUsersViewController *_self) {
|
||||
[_self updateAvatarVisibility];
|
||||
}];
|
||||
|
||||
PearlRemoveNotificationObservers();
|
||||
PearlAddNotificationObserver( UIApplicationDidEnterBackgroundNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPUsersViewController *self, NSNotification *note) {
|
||||
self.userSelectionContainer.alpha = 0;
|
||||
self.userSelectionContainer.visible = NO;
|
||||
} );
|
||||
PearlAddNotificationObserver( UIApplicationWillEnterForegroundNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPUsersViewController *self, NSNotification *note) {
|
||||
@@ -666,7 +686,7 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
PearlAddNotificationObserver( UIApplicationDidBecomeActiveNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPUsersViewController *self, NSNotification *note) {
|
||||
[UIView animateWithDuration:0.5f animations:^{
|
||||
self.userSelectionContainer.alpha = 1;
|
||||
self.userSelectionContainer.visible = YES;
|
||||
}];
|
||||
} );
|
||||
PearlAddNotificationObserver( UIKeyboardWillShowNotification, nil, [NSOperationQueue mainQueue],
|
||||
@@ -676,36 +696,31 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
[self.keyboardHeightConstraint updateConstant:keyboardHeight];
|
||||
} );
|
||||
|
||||
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.avatarCollectionView.alpha = mainContext? 1: 0;
|
||||
}];
|
||||
if (mainContext && self.storeLoadingActivity.isAnimating)
|
||||
[self.storeLoadingActivity stopAnimating];
|
||||
if (!mainContext && !self.storeLoadingActivity.isAnimating)
|
||||
[self.storeLoadingActivity startAnimating];
|
||||
if ((_contextChangedObserver = [MPiOSAppDelegate managedObjectContextChanged:^(NSDictionary<NSManagedObjectID *, NSString *> *affectedObjects) {
|
||||
if ([[[affectedObjects allKeys] filteredArrayUsingPredicate:
|
||||
[NSPredicate predicateWithBlock:^BOOL(NSManagedObjectID *objectID, NSDictionary *bindings) {
|
||||
return [objectID.entity.name isEqualToString:NSStringFromClass( [MPUserEntity class] )];
|
||||
}]] count])
|
||||
[self reloadUsers];
|
||||
}]))
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.avatarCollectionView.visible = YES;
|
||||
[self.storeLoadingActivity stopAnimating];
|
||||
}];
|
||||
else
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.avatarCollectionView.visible = NO;
|
||||
[self.storeLoadingActivity startAnimating];
|
||||
}];
|
||||
|
||||
if (mainContext)
|
||||
PearlAddNotificationObserver( NSManagedObjectContextObjectsDidChangeNotification, mainContext, nil,
|
||||
^(MPUsersViewController *self, NSNotification *note) {
|
||||
NSSet *insertedObjects = note.userInfo[NSInsertedObjectsKey];
|
||||
NSSet *deletedObjects = note.userInfo[NSDeletedObjectsKey];
|
||||
if ([[NSSetUnion( insertedObjects, deletedObjects )
|
||||
filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
|
||||
return [evaluatedObject isKindOfClass:[MPUserEntity class]];
|
||||
}]] count])
|
||||
[self reloadUsers];
|
||||
} );
|
||||
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresWillChangeNotification, [MPiOSAppDelegate get].storeCoordinator, nil,
|
||||
^(MPUsersViewController *self, NSNotification *note) {
|
||||
self.userIDs = nil;
|
||||
} );
|
||||
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresDidChangeNotification, [MPiOSAppDelegate get].storeCoordinator, nil,
|
||||
^(MPUsersViewController *self, NSNotification *note) {
|
||||
PearlMainQueue( ^{
|
||||
[self registerObservers];
|
||||
[self reloadUsers];
|
||||
} );
|
||||
[self registerObservers];
|
||||
[self reloadUsers];
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -720,13 +735,13 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
];
|
||||
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
|
||||
if (!users) {
|
||||
err( @"Failed to load users: %@", [error fullDescription] );
|
||||
MPError( error, @"Failed to load users." );
|
||||
self.userIDs = nil;
|
||||
}
|
||||
|
||||
NSMutableArray *userIDs = [NSMutableArray arrayWithCapacity:[users count]];
|
||||
for (MPUserEntity *user in users)
|
||||
[userIDs addObject:user.objectID];
|
||||
[userIDs addObject:user.permanentObjectID];
|
||||
self.userIDs = userIDs;
|
||||
}])
|
||||
self.userIDs = nil;
|
||||
@@ -754,7 +769,7 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
NSManagedObjectID *selectUserID = [MPiOSAppDelegate get].activeUserOID;
|
||||
if (!selectUserID)
|
||||
selectUserID = [self selectedUserInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]
|
||||
isNew:&isNew].objectID;
|
||||
isNew:&isNew].permanentObjectID;
|
||||
[self.avatarCollectionView reloadData];
|
||||
|
||||
NSUInteger selectedAvatarItem = isNew? [_userIDs count]: selectUserID? [_userIDs indexOfObject:selectUserID]: NSNotFound;
|
||||
@@ -763,7 +778,7 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
|
||||
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.userSelectionContainer.alpha = 1;
|
||||
self.userSelectionContainer.visible = YES;
|
||||
}];
|
||||
} );
|
||||
}
|
||||
@@ -828,8 +843,8 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
switch (activeUserState) {
|
||||
case MPActiveUserStateNone: {
|
||||
self.avatarCollectionView.scrollEnabled = YES;
|
||||
self.entryContainer.alpha = 0;
|
||||
self.footerContainer.alpha = 1;
|
||||
self.entryContainer.visible = NO;
|
||||
self.footerContainer.visible = YES;
|
||||
break;
|
||||
}
|
||||
case MPActiveUserStateLogin:
|
||||
@@ -837,15 +852,15 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
case MPActiveUserStateMasterPasswordChoice:
|
||||
case MPActiveUserStateMasterPasswordConfirmation: {
|
||||
self.avatarCollectionView.scrollEnabled = NO;
|
||||
self.entryContainer.alpha = 1;
|
||||
self.footerContainer.alpha = 1;
|
||||
self.entryContainer.visible = YES;
|
||||
self.footerContainer.visible = YES;
|
||||
requestFirstResponder = YES;
|
||||
break;
|
||||
}
|
||||
case MPActiveUserStateMinimized: {
|
||||
self.avatarCollectionView.scrollEnabled = NO;
|
||||
self.entryContainer.alpha = 0;
|
||||
self.footerContainer.alpha = 0;
|
||||
self.entryContainer.visible = NO;
|
||||
self.footerContainer.visible = NO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,11 +32,11 @@
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
if (!self.initialURL)
|
||||
self.initialURL = [NSURL URLWithString:@"http://masterpasswordapp.com"];
|
||||
self.initialURL = [NSURL URLWithString:@"https://ssl.masterpasswordapp.com"];
|
||||
|
||||
self.webNavigationItem.title = self.initialURL.host;
|
||||
|
||||
self.webView.alpha = 0;
|
||||
self.webView.visible = NO;
|
||||
[self.webView loadRequest:[[NSURLRequest alloc] initWithURL:self.initialURL]];
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
- (void)webViewDidFinishLoad:(UIWebView *)webView {
|
||||
|
||||
[UIView animateWithDuration:0.3 animations:^{
|
||||
self.webView.alpha = 1;
|
||||
self.webView.visible = YES;
|
||||
}];
|
||||
|
||||
[self.webNavigationItem setLeftBarButtonItem:[webView canGoBack]? [[UIBarButtonItem alloc]
|
||||
|
||||
@@ -19,9 +19,10 @@
|
||||
#import "MPiOSAppDelegate.h"
|
||||
#import "MPAppDelegate_Key.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
#import "IASKSettingsReader.h"
|
||||
#import "MPStoreViewController.h"
|
||||
|
||||
#define MP_HANG_TIME_MAIN 3 // s
|
||||
|
||||
@interface MPiOSAppDelegate()<UIDocumentInteractionControllerDelegate>
|
||||
|
||||
@property(nonatomic, strong) UIDocumentInteractionController *interactionController;
|
||||
@@ -53,16 +54,15 @@
|
||||
NSString *crashlyticsAPIKey = [self crashlyticsAPIKey];
|
||||
if ([crashlyticsAPIKey length]) {
|
||||
inf( @"Initializing Crashlytics" );
|
||||
#if defined (DEBUG) || defined (ADHOC)
|
||||
#if DEBUG
|
||||
[Crashlytics sharedInstance].debugMode = YES;
|
||||
#endif
|
||||
[[Crashlytics sharedInstance] setUserIdentifier:[PearlKeyChain deviceIdentifier]];
|
||||
[[Crashlytics sharedInstance] setObjectValue:[PearlKeyChain deviceIdentifier] forKey:@"deviceIdentifier"];
|
||||
[[Crashlytics sharedInstance] setUserName:@"Anonymous"];
|
||||
[[Crashlytics sharedInstance] setObjectValue:@"Anonymous" forKey:@"username"];
|
||||
[Crashlytics startWithAPIKey:crashlyticsAPIKey];
|
||||
[[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
|
||||
PearlLogLevel level = PearlLogLevelInfo;
|
||||
PearlLogLevel level = PearlLogLevelWarn;
|
||||
if ([[MPConfig get].sendInfo boolValue])
|
||||
level = PearlLogLevelDebug;
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
[Crashlytics sharedInstance].version, [PearlInfoPlist get].CFBundleName, [PearlInfoPlist get].CFBundleVersion );
|
||||
}
|
||||
#endif
|
||||
|
||||
[self installHangDetector];
|
||||
}
|
||||
@catch (id exception) {
|
||||
err( @"During Analytics Setup: %@", exception );
|
||||
@@ -83,24 +85,9 @@
|
||||
PearlAddNotificationObserver( MPCheckConfigNotification, nil, [NSOperationQueue mainQueue], ^(id self, NSNotification *note) {
|
||||
[self updateConfigKey:note.object];
|
||||
} );
|
||||
// PearlAddNotificationObserver( kIASKAppSettingChanged, nil, nil, ^(id self, NSNotification *note) {
|
||||
// [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:note.object];
|
||||
// } );
|
||||
PearlAddNotificationObserver( NSUserDefaultsDidChangeNotification, nil, nil, ^(id self, NSNotification *note) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
|
||||
} );
|
||||
|
||||
#ifdef ADHOC
|
||||
[PearlAlert showAlertWithTitle:@"Welcome, tester!" message:
|
||||
@"Thank you for taking the time to test Master Password.\n\n"
|
||||
@"Please provide any feedback, however minor it may seem, via the Feedback action item accessible from the top right.\n\n"
|
||||
@"Contact me directly at:\n"
|
||||
@"lhunath@lyndir.com\n"
|
||||
@"Or report detailed issues at:\n"
|
||||
@"https://youtrack.lyndir.com\n"
|
||||
viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:nil
|
||||
cancelTitle:nil otherTitles:[PearlStrings get].commonButtonOkay, nil];
|
||||
#endif
|
||||
}
|
||||
@catch (id exception) {
|
||||
err( @"During Config Test: %@", exception );
|
||||
@@ -156,6 +143,31 @@
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)installHangDetector {
|
||||
|
||||
__block NSDate *latestPing = [NSDate date];
|
||||
__block __weak VoidBlock wPingOp, wPongOp;
|
||||
|
||||
VoidBlock pingOp = ^{
|
||||
latestPing = [NSDate date];
|
||||
dispatch_after( dispatch_time( DISPATCH_TIME_NOW, 100 * NSEC_PER_MSEC ), dispatch_get_main_queue(), wPingOp );
|
||||
}, pongOp = ^{
|
||||
NSTimeInterval hangTime = -[latestPing timeIntervalSinceNow];
|
||||
if (hangTime > MP_HANG_TIME_MAIN) {
|
||||
MPError( [NSError errorWithDomain:MPErrorDomain code:MPErrorHangCode userInfo:@{
|
||||
@"time": @(hangTime)
|
||||
}], @"Timeout waiting for main thread after %fs.", hangTime );
|
||||
}
|
||||
else
|
||||
dbg( @"hangTime=%f", hangTime );
|
||||
|
||||
dispatch_after( dispatch_time( DISPATCH_TIME_NOW, NSEC_PER_SEC ), dispatch_get_global_queue( QOS_CLASS_BACKGROUND, 0 ), wPongOp );
|
||||
};
|
||||
|
||||
(wPingOp = pingOp)();
|
||||
(wPongOp = pongOp)();
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
|
||||
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
|
||||
|
||||
@@ -164,26 +176,25 @@
|
||||
return NO;
|
||||
|
||||
// Arbitrary URL to mpsites data.
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
NSError *error;
|
||||
NSURLResponse *response;
|
||||
NSData *importedSitesData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url]
|
||||
returningResponse:&response error:&error];
|
||||
if (error)
|
||||
err( @"While reading imported sites from %@: %@", url, [error fullDescription] );
|
||||
if (!importedSitesData) {
|
||||
[PearlAlert showError:strf( @"Master Password couldn't read the import sites.\n\n%@", [error localizedDescription]?: error )];
|
||||
return;
|
||||
}
|
||||
[[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:
|
||||
^(NSData *importedSitesData, NSURLResponse *response, NSError *error) {
|
||||
if (error)
|
||||
MPError( error, @"While reading imported sites from %@.", url );
|
||||
|
||||
NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding];
|
||||
if (!importedSitesString) {
|
||||
[PearlAlert showError:@"Master Password couldn't understand the import file."];
|
||||
return;
|
||||
}
|
||||
if (!importedSitesData) {
|
||||
[PearlAlert showError:strf( @"Master Password couldn't read the import sites.\n\n%@",
|
||||
[error localizedDescription]?: error )];
|
||||
return;
|
||||
}
|
||||
|
||||
[self importSites:importedSitesString];
|
||||
} );
|
||||
NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding];
|
||||
if (!importedSitesString) {
|
||||
[PearlAlert showError:@"Master Password couldn't understand the import file."];
|
||||
return;
|
||||
}
|
||||
|
||||
[self importSites:importedSitesString];
|
||||
}] resume];
|
||||
|
||||
return YES;
|
||||
}
|
||||
@@ -199,57 +210,33 @@
|
||||
|
||||
PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:@"Importing"];
|
||||
MPImportResult result = [self importSites:importedSitesString askImportPassword:^NSString *(NSString *userName) {
|
||||
__block NSString *masterPassword = nil;
|
||||
|
||||
dispatch_group_t importPasswordGroup = dispatch_group_create();
|
||||
dispatch_group_enter( importPasswordGroup );
|
||||
dispatch_async( dispatch_get_main_queue(), ^{
|
||||
return PearlAwait( ^(void (^setResult)(id)) {
|
||||
[PearlAlert showAlertWithTitle:@"Import File's Master Password"
|
||||
message:strf( @"%@'s export was done using a different master password.\n"
|
||||
@"Enter that master password to unlock the exported data.", userName )
|
||||
viewStyle:UIAlertViewStyleSecureTextInput
|
||||
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||
@try {
|
||||
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
||||
return;
|
||||
|
||||
masterPassword = [alert_ textFieldAtIndex:0].text;
|
||||
}
|
||||
@finally {
|
||||
dispatch_group_leave( importPasswordGroup );
|
||||
}
|
||||
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
||||
setResult( nil );
|
||||
else
|
||||
setResult( [alert_ textFieldAtIndex:0].text );
|
||||
}
|
||||
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Unlock Import", nil];
|
||||
} );
|
||||
dispatch_group_wait( importPasswordGroup, DISPATCH_TIME_FOREVER );
|
||||
|
||||
return masterPassword;
|
||||
} askUserPassword:^NSString *(NSString *userName, NSUInteger importCount, NSUInteger deleteCount) {
|
||||
__block NSString *masterPassword = nil;
|
||||
|
||||
dispatch_group_t userPasswordGroup = dispatch_group_create();
|
||||
dispatch_group_enter( userPasswordGroup );
|
||||
dispatch_async( dispatch_get_main_queue(), ^{
|
||||
return PearlAwait( (id)^(void (^setResult)(id)) {
|
||||
[PearlAlert showAlertWithTitle:strf( @"Master Password for\n%@", userName )
|
||||
message:strf( @"Imports %lu sites, overwriting %lu.",
|
||||
(unsigned long)importCount, (unsigned long)deleteCount )
|
||||
viewStyle:UIAlertViewStyleSecureTextInput
|
||||
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||
@try {
|
||||
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
||||
return;
|
||||
|
||||
masterPassword = [alert_ textFieldAtIndex:0].text;
|
||||
}
|
||||
@finally {
|
||||
dispatch_group_leave( userPasswordGroup );
|
||||
}
|
||||
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
||||
setResult( nil );
|
||||
else
|
||||
setResult( [alert_ textFieldAtIndex:0].text );
|
||||
}
|
||||
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Import", nil];
|
||||
} );
|
||||
dispatch_group_wait( userPasswordGroup, DISPATCH_TIME_FOREVER );
|
||||
|
||||
return masterPassword;
|
||||
}];
|
||||
|
||||
switch (result) {
|
||||
@@ -283,6 +270,44 @@
|
||||
if (![[MPiOSConfig get].rememberLogin boolValue])
|
||||
[self signOutAnimated:NO];
|
||||
|
||||
// self.task = [application beginBackgroundTaskWithExpirationHandler:^{
|
||||
// [application endBackgroundTask:self.task];
|
||||
// dbg( @"background expiring" );
|
||||
// }];
|
||||
// PearlNotMainQueueOperation( ^{
|
||||
// NSString *pbstring = [UIPasteboard generalPasteboard].string;
|
||||
// while (YES) {
|
||||
// NSString *newString = [UIPasteboard generalPasteboard].string;
|
||||
// if (![newString isEqualToString:pbstring]) {
|
||||
// dbg( @"pasteboard changed to: %@", newString );
|
||||
// pbstring = newString;
|
||||
// NSURL *url = [NSURL URLWithString:pbstring];
|
||||
// if (url) {
|
||||
// NSString *siteName = [url host];
|
||||
// }
|
||||
// MPKey *key = [MPiOSAppDelegate get].key;
|
||||
// if (key)
|
||||
// [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
// NSFetchRequest<MPSiteEntity *>
|
||||
// *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||
// fetchRequest.sortDescriptors = @[
|
||||
// [[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO]
|
||||
// ];
|
||||
// fetchRequest.fetchBatchSize = 2;
|
||||
// fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(name LIKE[cd] %@) AND user == %@", siteName,
|
||||
// [[MPiOSAppDelegate get] activeUserOID]];
|
||||
// NSError *error = nil;
|
||||
// NSArray<MPSiteEntity *> *results = [fetchRequest execute:&error];
|
||||
// dbg( @"site search, error: %@, results:\n%@", error, results );
|
||||
// if ([results count]) {
|
||||
// [UIPasteboard generalPasteboard].string = [[results firstObject] resolvePasswordUsingKey:key];
|
||||
// }
|
||||
// }];
|
||||
// }
|
||||
// [NSThread sleepForTimeInterval:5];
|
||||
// }
|
||||
// } );
|
||||
|
||||
[super applicationDidEnterBackground:application];
|
||||
}
|
||||
|
||||
@@ -483,7 +508,7 @@
|
||||
NSError *error = nil;
|
||||
if (![[exportedSites dataUsingEncoding:NSUTF8StringEncoding]
|
||||
writeToURL:exportURL options:NSDataWritingFileProtectionComplete error:&error])
|
||||
err( @"Failed to write export data to URL %@: %@", exportURL, [error fullDescription] );
|
||||
MPError( error, @"Failed to write export data to URL %@.", exportURL );
|
||||
else {
|
||||
self.interactionController = [UIDocumentInteractionController interactionControllerWithURL:exportURL];
|
||||
self.interactionController.UTI = @"com.lyndir.masterpassword.sites";
|
||||
@@ -574,7 +599,7 @@
|
||||
static NSDictionary *crashlyticsInfo = nil;
|
||||
if (crashlyticsInfo == nil)
|
||||
crashlyticsInfo = [[NSDictionary alloc] initWithContentsOfURL:
|
||||
[[NSBundle mainBundle] URLForResource:@"Crashlytics" withExtension:@"plist"]];
|
||||
[[NSBundle mainBundle] URLForResource:@"Fabric" withExtension:@"plist"]];
|
||||
|
||||
return crashlyticsInfo;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>© 2011-2016 Lyndir</string>
|
||||
<string>© 2011-2017</string>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>Exo2.0-Bold.otf</string>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<array>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>Enable this setting to send us carefully anonymized information to help us diagnose and resolve issues you might experience in the future.</string>
|
||||
<string>Enable this setting to send carefully anonymized information to help us diagnose and resolve any issues you encounter in a future update.</string>
|
||||
<key>Title</key>
|
||||
<string></string>
|
||||
<key>Type</key>
|
||||
@@ -50,7 +50,7 @@
|
||||
<key>Key</key>
|
||||
<string>sendInfo</string>
|
||||
<key>DefaultValue</key>
|
||||
<false/>
|
||||
<true/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,4 +5,4 @@ set -e
|
||||
cd "${BASH_SOURCE%/*}"
|
||||
|
||||
rm -vfr lib/*/{.unpacked,.patched,src} lib/include
|
||||
rm -vfr {core,cli}{*.o,*.dSYM} mpw mpw-bench mpw-tests
|
||||
rm -vfr {core,cli,.}/{*.o,*.dSYM} mpw mpw-bench mpw-tests
|
||||
|
||||
@@ -11,19 +11,24 @@ mpwArchive=mpw-$commit.tar.gz
|
||||
[[ -e $mpwArchive ]] && echo >&2 "WARNING: $mpwArchive already exists. Will overwrite."
|
||||
read -n1 -p "Will prepare and release $mpwArchive. Press a key to continue or ^C to abort."
|
||||
|
||||
echo "Cleaning .."
|
||||
( git clean -ffdx . && cd core && git clean -ffdx . )
|
||||
|
||||
echo "Creating archive $mpwArchive .."
|
||||
git ls-files -z . | xargs -0 tar -Lcvzf "$mpwArchive"
|
||||
|
||||
echo "Creating archive signature $mpwArchive.sig .."
|
||||
gpg --detach-sign "$mpwArchive"
|
||||
|
||||
echo "Installing archive and signature in current site .."
|
||||
cd ../../public/site/current
|
||||
ln -sf "../../../platform-independent/cli-c/$mpwArchive"; [[ -e $_ ]]
|
||||
ln -sf "../../../platform-independent/cli-c/$mpwArchive.sig"; [[ -e $_ ]]
|
||||
mv "../../../platform-independent/cli-c/$mpwArchive" .; [[ -e $_ ]]
|
||||
mv "../../../platform-independent/cli-c/$mpwArchive.sig" .; [[ -e $_ ]]
|
||||
ln -sf "$mpwArchive" "masterpassword-cli.tar.gz"; [[ -e $_ ]]
|
||||
ln -sf "$mpwArchive.sig" "masterpassword-cli.tar.gz.sig"; [[ -e $_ ]]
|
||||
|
||||
echo
|
||||
echo "Done. Don't forget to publish the site."
|
||||
echo "package: $mpwArchive"
|
||||
echo "signature: $mpwArchive.sig"
|
||||
echo "Done. Ready to publish the site."
|
||||
echo " package: $mpwArchive"
|
||||
echo " signature: $mpwArchive.sig"
|
||||
echo " url: https://ssl.masterpasswordapp.com/$mpwArchive"
|
||||
|
||||
@@ -1 +1 @@
|
||||
mpw-2.5-cli-1-0-gb01e370f.tar.gz
|
||||
mpw-2.5-cli-2-0-g3ed6b937.tar.gz
|
||||
@@ -1 +1 @@
|
||||
mpw-2.5-cli-1-0-gb01e370f.tar.gz.sig
|
||||
mpw-2.5-cli-2-0-g3ed6b937.tar.gz.sig
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
../../../platform-independent/cli-c/mpw-2.1-cli1-0-g10f1001.tar.gz
|
||||
BIN
public/site/2013-05/mpw-2.1-cli1-0-g10f1001.tar.gz
Normal file
BIN
public/site/2013-05/mpw-2.1-cli1-0-g10f1001.tar.gz
Normal file
Binary file not shown.
@@ -1 +0,0 @@
|
||||
../../../platform-independent/cli-c/mpw-2.1-cli2-2-g82c96dd.tar.gz
|
||||
BIN
public/site/2013-05/mpw-2.1-cli2-2-g82c96dd.tar.gz
Normal file
BIN
public/site/2013-05/mpw-2.1-cli2-2-g82c96dd.tar.gz
Normal file
Binary file not shown.
@@ -1 +0,0 @@
|
||||
../../../platform-independent/cli-c/mpw-2.1-cli3-0-g438daf2.tar.gz
|
||||
BIN
public/site/2013-05/mpw-2.1-cli3-0-g438daf2.tar.gz
Normal file
BIN
public/site/2013-05/mpw-2.1-cli3-0-g438daf2.tar.gz
Normal file
Binary file not shown.
@@ -1 +0,0 @@
|
||||
../../../platform-independent/cli-c/mpw-2.1-cli4-0-gf6b2287.tar.gz
|
||||
BIN
public/site/2013-05/mpw-2.1-cli4-0-gf6b2287.tar.gz
Normal file
BIN
public/site/2013-05/mpw-2.1-cli4-0-gf6b2287.tar.gz
Normal file
Binary file not shown.
@@ -1 +0,0 @@
|
||||
../../../platform-independent/cli-c/mpw-2.5-cli-1-0-gb01e370f.tar.gz
|
||||
BIN
public/site/2013-05/mpw-2.5-cli-1-0-gb01e370f.tar.gz
Normal file
BIN
public/site/2013-05/mpw-2.5-cli-1-0-gb01e370f.tar.gz
Normal file
Binary file not shown.
@@ -1 +0,0 @@
|
||||
../../../platform-independent/cli-c/mpw-2.5-cli-1-0-gb01e370f.tar.gz.sig
|
||||
BIN
public/site/2013-05/mpw-2.5-cli-1-0-gb01e370f.tar.gz.sig
Normal file
BIN
public/site/2013-05/mpw-2.5-cli-1-0-gb01e370f.tar.gz.sig
Normal file
Binary file not shown.
BIN
public/site/2013-05/mpw-2.5-cli-2-0-g3ed6b937.tar.gz
Normal file
BIN
public/site/2013-05/mpw-2.5-cli-2-0-g3ed6b937.tar.gz
Normal file
Binary file not shown.
BIN
public/site/2013-05/mpw-2.5-cli-2-0-g3ed6b937.tar.gz.sig
Normal file
BIN
public/site/2013-05/mpw-2.5-cli-2-0-g3ed6b937.tar.gz.sig
Normal file
Binary file not shown.
Reference in New Issue
Block a user