2
0

Compare commits

...

12 Commits

Author SHA1 Message Date
Maarten Billemont
f86210f5da Ability to handle versions without suffix after platform. 2017-05-04 14:30:50 -04:00
Maarten Billemont
e96f678236 Introduce a main thread lockup test feature. 2017-05-04 13:57:12 -04:00
Maarten Billemont
8b9067ab4b Merge tag '2.5-ios-4'
2.5-ios-4
2017-05-01 18:44:08 -04:00
Maarten Billemont
25b13dfb22 Rollback temporary storyboard hack. 2017-05-01 18:41:55 -04:00
Maarten Billemont
635692ef09 Fix issue causing site list to appear empty on login. 2017-05-01 18:40:51 -04:00
Maarten Billemont
e6bab4e504 Support for associating a URL to sites. 2017-05-01 18:32:52 -04:00
Maarten Billemont
cd6b7e6051 Settle on a method of making the password cells visible in storyboard. 2017-04-30 19:08:34 -04:00
Maarten Billemont
b180202e07 Dismiss keyboard when dropping down preferences or app deactivates. 2017-04-30 18:54:07 -04:00
Maarten Billemont
f83f2af529 Fix store product images and http URL links. 2017-04-30 18:45:08 -04:00
Maarten Billemont
cf2c30cfe6 Convert store into template cells for products. 2017-04-30 17:48:03 -04:00
Maarten Billemont
834e94ebd5 Fix usage of dubious objectID in global context. 2017-04-29 23:52:57 -04:00
Maarten Billemont
6d9be3fdfe Add support for Answers and improved Fabric integration. 2017-04-29 23:03:50 -04:00
67 changed files with 1617 additions and 1330 deletions

View File

@@ -42,7 +42,6 @@
93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */; }; 93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */; };
93D399246DC90F50913A1287 /* UIResponder+PearlFirstResponder.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A1DDFA09AE2E14D26DC /* UIResponder+PearlFirstResponder.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 */; }; 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 */; }; 93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D396C311C3725870343EE0 /* mpw-util.c */; };
93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D395105935859D71679931 /* MPOverlayViewController.m */; }; 93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D395105935859D71679931 /* MPOverlayViewController.m */; };
93D399E4BC1E092A8C8B12AE /* NSOrderedSetOrArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39FBF8FCEB4C106272334 /* NSOrderedSetOrArray.h */; }; 93D399E4BC1E092A8C8B12AE /* NSOrderedSetOrArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39FBF8FCEB4C106272334 /* NSOrderedSetOrArray.h */; };
@@ -87,6 +86,20 @@
DA0CC5361EAB99BA009A8ED9 /* IASKTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5241EAB99BA009A8ED9 /* IASKTextField.m */; }; DA0CC5361EAB99BA009A8ED9 /* IASKTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5241EAB99BA009A8ED9 /* IASKTextField.m */; };
DA0CC5371EAB99BA009A8ED9 /* IASKTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5261EAB99BA009A8ED9 /* IASKTextView.m */; }; DA0CC5371EAB99BA009A8ED9 /* IASKTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5261EAB99BA009A8ED9 /* IASKTextView.m */; };
DA0CC5381EAB99BA009A8ED9 /* IASKTextViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5281EAB99BA009A8ED9 /* IASKTextViewCell.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 */; }; 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 */; }; 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 */; }; DA24EBE819DAD6DE00FF010B /* Icon-320.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBE619DAD6DE00FF010B /* Icon-320.png */; };
@@ -114,7 +127,6 @@
DA29993219C9132F00AF7DF1 /* thumb_generated_login@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA29993119C9132F00AF7DF1 /* thumb_generated_login@3x.png */; }; 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 */; }; 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 */; }; 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 */; }; DA2C3D631BD96126001137B3 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA2C3D621BD96126001137B3 /* libc++.tbd */; };
DA2C3D651BD9612F001137B3 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA2C3D641BD9612F001137B3 /* libz.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 */; }; DA2CA4DD18D28859007798F8 /* NSArray+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4D918D28859007798F8 /* NSArray+Pearl.m */; };
@@ -128,10 +140,6 @@
DA30E9D415722EF400A68B4C /* Pearl-UIKit.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D315722EF400A68B4C /* Pearl-UIKit.m */; }; DA30E9D415722EF400A68B4C /* Pearl-UIKit.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D315722EF400A68B4C /* Pearl-UIKit.m */; };
DA30E9D715723E6900A68B4C /* PearlLazy.h in Headers */ = {isa = PBXBuildFile; fileRef = DA30E9D515723E6900A68B4C /* PearlLazy.h */; }; DA30E9D715723E6900A68B4C /* PearlLazy.h in Headers */ = {isa = PBXBuildFile; fileRef = DA30E9D515723E6900A68B4C /* PearlLazy.h */; };
DA30E9D815723E6900A68B4C /* PearlLazy.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D615723E6900A68B4C /* PearlLazy.m */; }; 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 */; }; 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 */; }; 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 */; }; DA32D01A19D046E1004F3F0E /* PearlFixedTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32D01819D046E1004F3F0E /* PearlFixedTableView.m */; };
@@ -149,7 +157,6 @@
DA32D05019D2F59B004F3F0E /* meter_fuel.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D04D19D2F59B004F3F0E /* meter_fuel.png */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; DA32D07B19D7D784004F3F0E /* background@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D07819D7D784004F3F0E /* background@2x.png */; };
DA32D07C19D7D784004F3F0E /* background.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D07919D7D784004F3F0E /* background.png */; }; DA32D07C19D7D784004F3F0E /* background.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D07919D7D784004F3F0E /* background.png */; };
@@ -165,7 +172,6 @@
DA45224A190628A1008F650A /* icon_wrench@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD386B1711E29700CF925C /* icon_wrench@2x.png */; }; 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 */; }; 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 */; }; 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 */; }; DA4DA1D91564471A00F6F596 /* libjrswizzle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6326C148680650075AEA5 /* libjrswizzle.a */; };
DA4DA1DA1564471F00F6F596 /* libuicolor-utilities.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6325D1486805C0075AEA5 /* libuicolor-utilities.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 */; }; DA5A09DF171A70E4005284AB /* play.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5A09DD171A70E4005284AB /* play.png */; };
@@ -202,7 +208,6 @@
DA945C8717E3F3FD0053236B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA945C8617E3F3FD0053236B /* Images.xcassets */; }; 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, ); }; }; 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 */; }; 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 */; }; DA95D5F214DF0B2C008D1B94 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA95D5F014DF0B1E008D1B94 /* MessageUI.framework */; };
DAA141201922FF020032B392 /* PearlTween.m in Sources */ = {isa = PBXBuildFile; fileRef = DAA1411C1922FF020032B392 /* PearlTween.m */; }; DAA141201922FF020032B392 /* PearlTween.m in Sources */ = {isa = PBXBuildFile; fileRef = DAA1411C1922FF020032B392 /* PearlTween.m */; };
DAA141211922FF020032B392 /* PearlTween.h in Headers */ = {isa = PBXBuildFile; fileRef = DAA1411D1922FF020032B392 /* PearlTween.h */; }; DAA141211922FF020032B392 /* PearlTween.h in Headers */ = {isa = PBXBuildFile; fileRef = DAA1411D1922FF020032B392 /* PearlTween.h */; };
@@ -335,7 +340,6 @@
DAC77CAE148291A600BCF976 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; }; DAC77CAE148291A600BCF976 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DAC8DF47192831E100BA7D71 /* icon_key.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD379A1711E29600CF925C /* icon_key.png */; }; 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 */; }; 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 */; }; DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA29711705E1A8002C6C22 /* ciphers.plist */; };
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */ = {isa = PBXBuildFile; fileRef = DACA29721705E1A8002C6C22 /* dictionary.lst */; }; DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */ = {isa = PBXBuildFile; fileRef = DACA29721705E1A8002C6C22 /* dictionary.lst */; };
DACA298D1705E2BD002C6C22 /* JRSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA29771705E2BD002C6C22 /* JRSwizzle.h */; }; DACA298D1705E2BD002C6C22 /* JRSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA29771705E2BD002C6C22 /* JRSwizzle.h */; };
@@ -667,6 +671,37 @@
DA0CC5261EAB99BA009A8ED9 /* IASKTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKTextView.m; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; DA24EBB419DAD4D000FF010B /* Icon-60@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60@3x.png"; sourceTree = "<group>"; };
@@ -715,14 +750,6 @@
DA30E9D315722EF400A68B4C /* Pearl-UIKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Pearl-UIKit.m"; sourceTree = "<group>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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; }; DA32D02019D111C6004F3F0E /* libKCOrderedAccessorFix.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libKCOrderedAccessorFix.a; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -737,8 +764,6 @@
DA32D04B19D2F59B004F3F0E /* meter_fuel@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "meter_fuel@3x.png"; sourceTree = "<group>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; DA32D07919D7D784004F3F0E /* background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = background.png; path = ios/launch/background.png; sourceTree = "<group>"; };
@@ -775,14 +800,6 @@
DA95B50B1C476B6A0067F5EF /* LocalAuthentication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LocalAuthentication.framework; path = System/Library/Frameworks/LocalAuthentication.framework; sourceTree = SDKROOT; }; 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>"; }; 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>"; }; 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; }; 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>"; }; 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>"; }; DAA1411C1922FF020032B392 /* PearlTween.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlTween.m; sourceTree = "<group>"; };
@@ -1496,7 +1513,6 @@
DAC632871486D95D0075AEA5 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 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; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; DACA29771705E2BD002C6C22 /* JRSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JRSwizzle.h; sourceTree = "<group>"; };
@@ -1623,6 +1639,7 @@
DA2C3D651BD9612F001137B3 /* libz.tbd in Frameworks */, DA2C3D651BD9612F001137B3 /* libz.tbd in Frameworks */,
DA2C3D631BD96126001137B3 /* libc++.tbd in Frameworks */, DA2C3D631BD96126001137B3 /* libc++.tbd in Frameworks */,
DAA1761B19D86D0D0044227B /* libAttributedMarkdown.a in Frameworks */, DAA1761B19D86D0D0044227B /* libAttributedMarkdown.a in Frameworks */,
DA0CC5421EB57BD4009A8ED9 /* Crashlytics.framework in Frameworks */,
DA32D03E19D11293004F3F0E /* libKCOrderedAccessorFix.a in Frameworks */, DA32D03E19D11293004F3F0E /* libKCOrderedAccessorFix.a in Frameworks */,
DA04E33E14B1E70400ECA4F3 /* MobileCoreServices.framework in Frameworks */, DA04E33E14B1E70400ECA4F3 /* MobileCoreServices.framework in Frameworks */,
DAE2725A19C93B8E007C5262 /* StoreKit.framework in Frameworks */, DAE2725A19C93B8E007C5262 /* StoreKit.framework in Frameworks */,
@@ -1634,15 +1651,13 @@
DA672D3014F9413D004A189C /* libPearl.a in Frameworks */, DA672D3014F9413D004A189C /* libPearl.a in Frameworks */,
DAEBC45314F6364500987BF6 /* QuartzCore.framework in Frameworks */, DAEBC45314F6364500987BF6 /* QuartzCore.framework in Frameworks */,
DA95D5F214DF0B2C008D1B94 /* MessageUI.framework in Frameworks */, DA95D5F214DF0B2C008D1B94 /* MessageUI.framework in Frameworks */,
DA48856019A5A82E000C2D79 /* Crashlytics.framework in Frameworks */,
DAC632891486D9690075AEA5 /* Security.framework in Frameworks */, DAC632891486D9690075AEA5 /* Security.framework in Frameworks */,
DA5BFA49147E415C00F98B1E /* UIKit.framework in Frameworks */, DA5BFA49147E415C00F98B1E /* UIKit.framework in Frameworks */,
DA0979171E9A81EE00F0BFE8 /* libsodium.a in Frameworks */, DA0979171E9A81EE00F0BFE8 /* libsodium.a in Frameworks */,
DA5BFA4B147E415C00F98B1E /* Foundation.framework in Frameworks */, DA5BFA4B147E415C00F98B1E /* Foundation.framework in Frameworks */,
DA5BFA4D147E415C00F98B1E /* CoreGraphics.framework in Frameworks */, DA5BFA4D147E415C00F98B1E /* CoreGraphics.framework in Frameworks */,
DA5BFA4F147E415C00F98B1E /* CoreData.framework in Frameworks */, DA5BFA4F147E415C00F98B1E /* CoreData.framework in Frameworks */,
93D399433EA75E50656040CB /* Twitter.framework in Frameworks */, DA0CC5411EB57BD4009A8ED9 /* Fabric.framework in Frameworks */,
DA2C3D611BD95EEE001137B3 /* Fabric.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -1861,6 +1876,14 @@
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DA0CC5391EB57B5C009A8ED9 /* Fabric */ = {
isa = PBXGroup;
children = (
DA0CC53A1EB57B5C009A8ED9 /* Fabric.plist */,
);
path = Fabric;
sourceTree = "<group>";
};
DA24EBB019DAD4D000FF010B /* ios */ = { DA24EBB019DAD4D000FF010B /* ios */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -2022,6 +2045,7 @@
DAA141181922FED80032B392 /* iOS */ = { DAA141181922FED80032B392 /* iOS */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DA0CC53F1EB57B91009A8ED9 /* Fabric.framework */,
DAA141191922FED80032B392 /* Crashlytics.framework */, DAA141191922FED80032B392 /* Crashlytics.framework */,
); );
path = iOS; path = iOS;
@@ -2771,7 +2795,7 @@
children = ( children = (
DABD3BD71711E2DC00CF925C /* iOS */, DABD3BD71711E2DC00CF925C /* iOS */,
DA771FE41E6E1595004D7EDE /* MasterPassword-Prefix.pch */, DA771FE41E6E1595004D7EDE /* MasterPassword-Prefix.pch */,
DA95B5101C477DB50067F5EF /* MasterPassword.xcdatamodeld */, DA0CC5441EB6AD0E009A8ED9 /* MasterPassword.xcdatamodeld */,
DABD3BA01711E2DC00CF925C /* MPAlgorithm.h */, DABD3BA01711E2DC00CF925C /* MPAlgorithm.h */,
DABD3BA11711E2DC00CF925C /* MPAlgorithm.m */, DABD3BA11711E2DC00CF925C /* MPAlgorithm.m */,
DABD3BA21711E2DC00CF925C /* MPAlgorithmV0.h */, DABD3BA21711E2DC00CF925C /* MPAlgorithmV0.h */,
@@ -2796,20 +2820,30 @@
DABD3BB51711E2DC00CF925C /* MPEntities.m */, DABD3BB51711E2DC00CF925C /* MPEntities.m */,
93D399F244BB522A317811BB /* MPFixable.h */, 93D399F244BB522A317811BB /* MPFixable.h */,
93D39A813CA9D7E192261ED2 /* MPFixable.m */, 93D39A813CA9D7E192261ED2 /* MPFixable.m */,
DA32CFEF19CF1C8F004F3F0E /* MPGeneratedSiteEntity.h */, DA0CC5781EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.h */,
DA32CFEE19CF1C8F004F3F0E /* MPGeneratedSiteEntity.m */, DA0CC5791EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.m */,
DA0CC57A1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataProperties.h */,
DA0CC57B1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataProperties.m */,
DABD3BB61711E2DC00CF925C /* MPKey.h */, DABD3BB61711E2DC00CF925C /* MPKey.h */,
DABD3BB71711E2DC00CF925C /* MPKey.m */, DABD3BB71711E2DC00CF925C /* MPKey.m */,
DA32CFED19CF1C8F004F3F0E /* MPSiteEntity.h */, DA0CC5881EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.h */,
DA32CFEC19CF1C8F004F3F0E /* MPSiteEntity.m */, DA0CC5891EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m */,
DA32D05319D741DC004F3F0E /* MPSiteQuestionEntity.h */, DA0CC58A1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.h */,
DA32D05419D741DC004F3F0E /* MPSiteQuestionEntity.m */, DA0CC58B1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m */,
DA32CFE919CF1C8F004F3F0E /* MPStoredSiteEntity.h */, DA0CC57C1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.h */,
DA32CFE819CF1C8F004F3F0E /* MPStoredSiteEntity.m */, 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 */, DABD3BB81711E2DC00CF925C /* MPTypes.h */,
93D39D72239990DDAC2D75B0 /* MPTypes.m */, 93D39D72239990DDAC2D75B0 /* MPTypes.m */,
DA32CFE719CF1C8F004F3F0E /* MPUserEntity.h */, DA0CC5841EB6B030009A8ED9 /* MPUserEntity+CoreDataClass.h */,
DA32CFE619CF1C8F004F3F0E /* MPUserEntity.m */, DA0CC5851EB6B030009A8ED9 /* MPUserEntity+CoreDataClass.m */,
DA0CC5861EB6B030009A8ED9 /* MPUserEntity+CoreDataProperties.h */,
DA0CC5871EB6B030009A8ED9 /* MPUserEntity+CoreDataProperties.m */,
93D393CB0B1F4EC8C17CFE43 /* NSString+MPMarkDown.h */, 93D393CB0B1F4EC8C17CFE43 /* NSString+MPMarkDown.h */,
93D39C41A27AA42D044D68AE /* NSString+MPMarkDown.m */, 93D39C41A27AA42D044D68AE /* NSString+MPMarkDown.m */,
); );
@@ -2908,7 +2942,7 @@
DACA23B41705DF7D002C6C22 /* Resources */ = { DACA23B41705DF7D002C6C22 /* Resources */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DACA26991705DF81002C6C22 /* Crashlytics */, DA0CC5391EB57B5C009A8ED9 /* Fabric */,
DACA29701705E1A8002C6C22 /* Data */, DACA29701705E1A8002C6C22 /* Data */,
DAE1EF2417E942DE00BC0086 /* Localizable.strings */, DAE1EF2417E942DE00BC0086 /* Localizable.strings */,
DABD360D1711E29400CF925C /* Media */, DABD360D1711E29400CF925C /* Media */,
@@ -2916,14 +2950,6 @@
path = Resources; path = Resources;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DACA26991705DF81002C6C22 /* Crashlytics */ = {
isa = PBXGroup;
children = (
DACA269A1705DF81002C6C22 /* Crashlytics.plist */,
);
path = Crashlytics;
sourceTree = "<group>";
};
DACA29701705E1A8002C6C22 /* Data */ = { DACA29701705E1A8002C6C22 /* Data */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -3287,7 +3313,7 @@
DA5BFA41147E415C00F98B1E /* Frameworks */, DA5BFA41147E415C00F98B1E /* Frameworks */,
DA5BFA42147E415C00F98B1E /* Resources */, DA5BFA42147E415C00F98B1E /* Resources */,
DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */, DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */,
DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */, DAD3125D155288AA00A3F9ED /* Run Script: Fabric */,
); );
buildRules = ( buildRules = (
); );
@@ -3548,7 +3574,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
DAFE4A5A1503982E003ABA7C /* Pearl.strings in Resources */, DAFE4A5A1503982E003ABA7C /* Pearl.strings in Resources */,
DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */, DA0CC53B1EB57B5C009A8ED9 /* Fabric.plist in Resources */,
DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */, DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */,
DA32D04F19D2F59B004F3F0E /* meter_fuel@2x.png in Resources */, DA32D04F19D2F59B004F3F0E /* meter_fuel@2x.png in Resources */,
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */, DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */,
@@ -3778,19 +3804,19 @@
shellScript = "exec Scripts/genassets"; shellScript = "exec Scripts/genassets";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */ = { DAD3125D155288AA00A3F9ED /* Run Script: Fabric */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputPaths = ( inputPaths = (
); );
name = "Run Script: Crashlytics"; name = "Run Script: Fabric";
outputPaths = ( outputPaths = (
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = "/bin/bash -e"; 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; showEnvVarsInLog = 0;
}; };
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
@@ -3808,7 +3834,9 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
DA0CC5921EB6B030009A8ED9 /* MPUserEntity+CoreDataClass.m in Sources */,
DABD3BFD1711E2DC00CF925C /* MPAlgorithm.m in Sources */, DABD3BFD1711E2DC00CF925C /* MPAlgorithm.m in Sources */,
DA0CC5951EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m in Sources */,
DABD3BFE1711E2DC00CF925C /* MPAlgorithmV0.m in Sources */, DABD3BFE1711E2DC00CF925C /* MPAlgorithmV0.m in Sources */,
DABD3BFF1711E2DC00CF925C /* MPAlgorithmV1.m in Sources */, DABD3BFF1711E2DC00CF925C /* MPAlgorithmV1.m in Sources */,
DABD3C001711E2DC00CF925C /* MPAppDelegate_Key.m in Sources */, DABD3C001711E2DC00CF925C /* MPAppDelegate_Key.m in Sources */,
@@ -3816,38 +3844,40 @@
DABD3C021711E2DC00CF925C /* MPAppDelegate_Store.m in Sources */, DABD3C021711E2DC00CF925C /* MPAppDelegate_Store.m in Sources */,
DABD3C031711E2DC00CF925C /* MPConfig.m in Sources */, DABD3C031711E2DC00CF925C /* MPConfig.m in Sources */,
DABD3C071711E2DC00CF925C /* MPEntities.m in Sources */, DABD3C071711E2DC00CF925C /* MPEntities.m in Sources */,
DA32CFF319CF1C8F004F3F0E /* MPSiteEntity.m in Sources */,
DABD3C081711E2DC00CF925C /* MPKey.m in Sources */, DABD3C081711E2DC00CF925C /* MPKey.m in Sources */,
DABD3C151711E2DC00CF925C /* MPiOSAppDelegate.m in Sources */, DABD3C151711E2DC00CF925C /* MPiOSAppDelegate.m in Sources */,
DA0CC5941EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m in Sources */,
DABD3C1C1711E2DC00CF925C /* MPGuideViewController.m in Sources */, DABD3C1C1711E2DC00CF925C /* MPGuideViewController.m in Sources */,
DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */, DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */,
DABD3C1F1711E2DC00CF925C /* MPTypeViewController.m in Sources */, DABD3C1F1711E2DC00CF925C /* MPTypeViewController.m in Sources */,
DABD3C211711E2DC00CF925C /* MPiOSConfig.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 */, DABD3C271711E2DC00CF925C /* main.m in Sources */,
DA0CC5931EB6B030009A8ED9 /* MPUserEntity+CoreDataProperties.m in Sources */,
93D39F8A9254177891F38705 /* MPSetupViewController.m in Sources */, 93D39F8A9254177891F38705 /* MPSetupViewController.m in Sources */,
DA095E75172F4CD8001C948B /* MPLogsViewController.m in Sources */, DA095E75172F4CD8001C948B /* MPLogsViewController.m in Sources */,
93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */, 93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */,
DA32CFF419CF1C8F004F3F0E /* MPGeneratedSiteEntity.m in Sources */,
93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */, 93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */,
93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */, 93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */,
DA32D05519D741DC004F3F0E /* MPSiteQuestionEntity.m in Sources */,
93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */, 93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */,
93D39392DEDA376F93C6C718 /* MPCell.m in Sources */, 93D39392DEDA376F93C6C718 /* MPCell.m in Sources */,
DA0CC58E1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.m in Sources */,
93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */, 93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */,
93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */, 93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */,
DA95B50F1C4776F00067F5EF /* NSMutableSet+Pearl.m in Sources */, DA95B50F1C4776F00067F5EF /* NSMutableSet+Pearl.m in Sources */,
93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */, 93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */,
DA0CC5911EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.m in Sources */,
93D39673DDC085BE72C34D7C /* MPPopdownSegue.m in Sources */, 93D39673DDC085BE72C34D7C /* MPPopdownSegue.m in Sources */,
93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */, 93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */,
DA0CC58F1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataProperties.m in Sources */,
93D396D8B67DA6522CDBA142 /* MPCoachmarkViewController.m in Sources */, 93D396D8B67DA6522CDBA142 /* MPCoachmarkViewController.m in Sources */,
DAADBFE01A68763B00F7A756 /* mpw-algorithm.c in Sources */, DAADBFE01A68763B00F7A756 /* mpw-algorithm.c in Sources */,
DA95B5191C477DB50067F5EF /* MasterPassword.xcdatamodeld in Sources */, DA0CC54E1EB6AD0E009A8ED9 /* MasterPassword.xcdatamodeld in Sources */,
93D39EAA4D064193074D3021 /* MPFixable.m in Sources */, 93D39EAA4D064193074D3021 /* MPFixable.m in Sources */,
DA32CFF119CF1C8F004F3F0E /* MPStoredSiteEntity.m in Sources */,
93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */, 93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */,
93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */, 93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */,
93D3939661CE37180AF7CD6A /* MPStoreViewController.m in Sources */, 93D3939661CE37180AF7CD6A /* MPStoreViewController.m in Sources */,
DA32CFF019CF1C8F004F3F0E /* MPUserEntity.m in Sources */,
93D390C1B93F9D3AE37DD0A5 /* MPAnswersViewController.m in Sources */, 93D390C1B93F9D3AE37DD0A5 /* MPAnswersViewController.m in Sources */,
93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */, 93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */,
93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */, 93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */,
@@ -3857,6 +3887,7 @@
93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */, 93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */,
93D39577FD8BB0945DB2F0A3 /* MPAlgorithmV3.m in Sources */, 93D39577FD8BB0945DB2F0A3 /* MPAlgorithmV3.m in Sources */,
93D39E5F7F6D7F5C0FAD090F /* MPTypes.m in Sources */, 93D39E5F7F6D7F5C0FAD090F /* MPTypes.m in Sources */,
DA0CC58C1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.m in Sources */,
DA92614E1BE1A57500369DE5 /* MPAppDelegate_InApp.m in Sources */, DA92614E1BE1A57500369DE5 /* MPAppDelegate_InApp.m in Sources */,
93D39508A6814612A5B3C226 /* MPMessageViewController.m in Sources */, 93D39508A6814612A5B3C226 /* MPMessageViewController.m in Sources */,
); );
@@ -4137,6 +4168,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = NO;
CODE_SIGN_ENTITLEMENTS = Source/iOS/MasterPassword.entitlements; CODE_SIGN_ENTITLEMENTS = Source/iOS/MasterPassword.entitlements;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-";
COPY_PHASE_STRIP = YES; COPY_PHASE_STRIP = YES;
@@ -4146,8 +4178,9 @@
"\"$(SRCROOT)/External/iOS\"", "\"$(SRCROOT)/External/iOS\"",
); );
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist"; INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib", "$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib",
@@ -4263,6 +4296,7 @@
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1", "DEBUG=1",
"$(inherited)", "$(inherited)",
"CRASHLYTICS=1",
); );
GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
@@ -4404,6 +4438,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = NO;
CODE_SIGN_ENTITLEMENTS = Source/iOS/MasterPassword.entitlements; CODE_SIGN_ENTITLEMENTS = Source/iOS/MasterPassword.entitlements;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@@ -4411,8 +4446,9 @@
"\"$(SRCROOT)/External/iOS\"", "\"$(SRCROOT)/External/iOS\"",
); );
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist"; INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib", "$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib",
@@ -4430,6 +4466,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = NO;
CODE_SIGN_ENTITLEMENTS = Source/iOS/MasterPassword.entitlements; CODE_SIGN_ENTITLEMENTS = Source/iOS/MasterPassword.entitlements;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES; COPY_PHASE_STRIP = YES;
@@ -4439,8 +4476,9 @@
"\"$(SRCROOT)/External/iOS\"", "\"$(SRCROOT)/External/iOS\"",
); );
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch"; GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist"; INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib", "$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib",
@@ -4627,19 +4665,20 @@
/* End XCConfigurationList section */ /* End XCConfigurationList section */
/* Begin XCVersionGroup section */ /* Begin XCVersionGroup section */
DA95B5101C477DB50067F5EF /* MasterPassword.xcdatamodeld */ = { DA0CC5441EB6AD0E009A8ED9 /* MasterPassword.xcdatamodeld */ = {
isa = XCVersionGroup; isa = XCVersionGroup;
children = ( children = (
DA95B5111C477DB50067F5EF /* MasterPassword 1.xcdatamodel */, DA0CC5451EB6AD0E009A8ED9 /* MasterPassword 1.xcdatamodel */,
DA95B5121C477DB50067F5EF /* MasterPassword 2.xcdatamodel */, DA0CC5461EB6AD0E009A8ED9 /* MasterPassword 2.xcdatamodel */,
DA95B5131C477DB50067F5EF /* MasterPassword 3.xcdatamodel */, DA0CC5471EB6AD0E009A8ED9 /* MasterPassword 3.xcdatamodel */,
DA95B5141C477DB50067F5EF /* MasterPassword 4.xcdatamodel */, DA0CC5481EB6AD0E009A8ED9 /* MasterPassword 4.xcdatamodel */,
DA95B5151C477DB50067F5EF /* MasterPassword 5.xcdatamodel */, DA0CC5491EB6AD0E009A8ED9 /* MasterPassword 5.xcdatamodel */,
DA95B5161C477DB50067F5EF /* MasterPassword 6.xcdatamodel */, DA0CC54A1EB6AD0E009A8ED9 /* MasterPassword 6.xcdatamodel */,
DA95B5171C477DB50067F5EF /* MasterPassword 7.xcdatamodel */, DA0CC54B1EB6AD0E009A8ED9 /* MasterPassword 7.xcdatamodel */,
DA95B5181C477DB50067F5EF /* MasterPassword 8.xcdatamodel */, DA0CC54C1EB6AD0E009A8ED9 /* MasterPassword 8.xcdatamodel */,
DA0CC54D1EB6AD0E009A8ED9 /* MasterPassword 9.xcdatamodel */,
); );
currentVersion = DA95B5181C477DB50067F5EF /* MasterPassword 8.xcdatamodel */; currentVersion = DA0CC54D1EB6AD0E009A8ED9 /* MasterPassword 9.xcdatamodel */;
path = MasterPassword.xcdatamodeld; path = MasterPassword.xcdatamodeld;
sourceTree = "<group>"; sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel; versionGroupType = wrapper.xcdatamodel;

View File

@@ -27,6 +27,8 @@
DA09745B1E99582900F0BFE8 /* mpw-tests.c in Sources */ = {isa = PBXBuildFile; fileRef = DA0974571E99582200F0BFE8 /* mpw-tests.c */; }; DA09745B1E99582900F0BFE8 /* mpw-tests.c in Sources */ = {isa = PBXBuildFile; fileRef = DA0974571E99582200F0BFE8 /* mpw-tests.c */; };
DA09745E1E99586600F0BFE8 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA09745D1E99586600F0BFE8 /* libxml2.tbd */; }; DA09745E1E99586600F0BFE8 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA09745D1E99586600F0BFE8 /* libxml2.tbd */; };
DA0979681E9A834C00F0BFE8 /* libsodium.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA0979571E9A824700F0BFE8 /* libsodium.a */; }; 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, ); }; }; DA1000801998A4C6002B873F /* openssl in Headers */ = {isa = PBXBuildFile; fileRef = DAE8E65719867AF500416A0F /* openssl */; settings = {ATTRIBUTES = (Public, ); }; };
DA16B341170661DB000A0EAB /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA16B340170661DB000A0EAB /* Carbon.framework */; }; DA16B341170661DB000A0EAB /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA16B340170661DB000A0EAB /* Carbon.framework */; };
DA16B342170661E0000A0EAB /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC632871486D95D0075AEA5 /* Security.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 */; }; DA9261521BE1A86700369DE5 /* Fabric.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9261501BE1A86700369DE5 /* Fabric.framework */; };
DA9261541BE1A88900369DE5 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9261531BE1A88900369DE5 /* libc++.tbd */; }; DA9261541BE1A88900369DE5 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9261531BE1A88900369DE5 /* libc++.tbd */; };
DA9261561BE1A89600369DE5 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9261551BE1A89600369DE5 /* libz.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 */; }; DAAA81B0195A8D1300FA30D9 /* gradient.png in Resources */ = {isa = PBXBuildFile; fileRef = DAAA81AF195A8D1300FA30D9 /* gradient.png */; };
DAADCC4719FAFFAD00987B1D /* NSNotificationCenter+PearlEasyCleanup.h in Headers */ = {isa = PBXBuildFile; fileRef = DAADCC3E19FAFFAD00987B1D /* NSNotificationCenter+PearlEasyCleanup.h */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA29711705E1A8002C6C22 /* ciphers.plist */; };
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */ = {isa = PBXBuildFile; fileRef = DACA29721705E1A8002C6C22 /* dictionary.lst */; }; DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */ = {isa = PBXBuildFile; fileRef = DACA29721705E1A8002C6C22 /* dictionary.lst */; };
DACA298D1705E2BD002C6C22 /* JRSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA29771705E2BD002C6C22 /* JRSwizzle.h */; }; 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>"; }; 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>"; }; 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>"; }; 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; }; 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; }; 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>"; }; 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>"; }; 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; }; 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; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; DACA29771705E2BD002C6C22 /* JRSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JRSwizzle.h; sourceTree = "<group>"; };
@@ -1132,6 +1133,14 @@
path = lib; path = lib;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DA0CC53C1EB57B69009A8ED9 /* Fabric */ = {
isa = PBXGroup;
children = (
DA0CC53D1EB57B69009A8ED9 /* Fabric.plist */,
);
path = Fabric;
sourceTree = "<group>";
};
DA2508F819513C1400AC23F1 /* Other Frameworks */ = { DA2508F819513C1400AC23F1 /* Other Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -1212,7 +1221,7 @@
children = ( children = (
DA5E5CB21724A667003798D8 /* Mac */, DA5E5CB21724A667003798D8 /* Mac */,
DA771FE51E6E15A1004D7EDE /* MasterPassword-Prefix.pch */, DA771FE51E6E15A1004D7EDE /* MasterPassword-Prefix.pch */,
DA95B51A1C477DE10067F5EF /* MasterPassword.xcdatamodeld */, DA0CC54F1EB6AE45009A8ED9 /* MasterPassword.xcdatamodeld */,
DA5E5C971724A667003798D8 /* MPAlgorithm.h */, DA5E5C971724A667003798D8 /* MPAlgorithm.h */,
DA5E5C981724A667003798D8 /* MPAlgorithm.m */, DA5E5C981724A667003798D8 /* MPAlgorithm.m */,
DA5E5C991724A667003798D8 /* MPAlgorithmV0.h */, DA5E5C991724A667003798D8 /* MPAlgorithmV0.h */,
@@ -1772,8 +1781,8 @@
DACA23B41705DF7D002C6C22 /* Resources */ = { DACA23B41705DF7D002C6C22 /* Resources */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DA0CC53C1EB57B69009A8ED9 /* Fabric */,
DA09745F1E995EB500F0BFE8 /* mpw_tests.xml */, DA09745F1E995EB500F0BFE8 /* mpw_tests.xml */,
DACA26991705DF81002C6C22 /* Crashlytics */,
DACA29701705E1A8002C6C22 /* Data */, DACA29701705E1A8002C6C22 /* Data */,
DACA23B51705DF7D002C6C22 /* Media */, DACA23B51705DF7D002C6C22 /* Media */,
); );
@@ -1856,14 +1865,6 @@
path = Fonts; path = Fonts;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DACA26991705DF81002C6C22 /* Crashlytics */ = {
isa = PBXGroup;
children = (
DACA269A1705DF81002C6C22 /* Crashlytics.plist */,
);
path = Crashlytics;
sourceTree = "<group>";
};
DACA29701705E1A8002C6C22 /* Data */ = { DACA29701705E1A8002C6C22 /* Data */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -2086,7 +2087,7 @@
DA5BFA42147E415C00F98B1E /* Resources */, DA5BFA42147E415C00F98B1E /* Resources */,
DAD9B5EE1762CA3A001835F9 /* Copy LoginHelper */, DAD9B5EE1762CA3A001835F9 /* Copy LoginHelper */,
DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */, DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */,
DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */, DAD3125D155288AA00A3F9ED /* Run Script: Fabric */,
); );
buildRules = ( buildRules = (
); );
@@ -2295,13 +2296,13 @@
DACA27331705DF81002C6C22 /* avatar-12@2x.png in Resources */, DACA27331705DF81002C6C22 /* avatar-12@2x.png in Resources */,
DACA27341705DF81002C6C22 /* avatar-2@2x.png in Resources */, DACA27341705DF81002C6C22 /* avatar-2@2x.png in Resources */,
DA60717D195D040500CA98B5 /* icon_gear@2x.png in Resources */, DA60717D195D040500CA98B5 /* icon_gear@2x.png in Resources */,
DA0CC53E1EB57B69009A8ED9 /* Fabric.plist in Resources */,
DACA27351705DF81002C6C22 /* avatar-11.png in Resources */, DACA27351705DF81002C6C22 /* avatar-11.png in Resources */,
DACA27361705DF81002C6C22 /* avatar-0@2x.png in Resources */, DACA27361705DF81002C6C22 /* avatar-0@2x.png in Resources */,
DACA27371705DF81002C6C22 /* avatar-10@2x.png in Resources */, DACA27371705DF81002C6C22 /* avatar-10@2x.png in Resources */,
DACA27381705DF81002C6C22 /* menu-icon.png in Resources */, DACA27381705DF81002C6C22 /* menu-icon.png in Resources */,
DACA29671705DF81002C6C22 /* SourceCodePro-ExtraLight.otf in Resources */, DACA29671705DF81002C6C22 /* SourceCodePro-ExtraLight.otf in Resources */,
DACA29681705DF81002C6C22 /* SourceCodePro-Black.otf in Resources */, DACA29681705DF81002C6C22 /* SourceCodePro-Black.otf in Resources */,
DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */,
DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */, DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */,
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */, DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */,
DA5E5D081724A667003798D8 /* MasterPassword.entitlements in Resources */, DA5E5D081724A667003798D8 /* MasterPassword.entitlements in Resources */,
@@ -2359,19 +2360,19 @@
shellPath = "/bin/sh -e"; shellPath = "/bin/sh -e";
shellScript = "exec Scripts/updatePlist"; shellScript = "exec Scripts/updatePlist";
}; };
DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */ = { DAD3125D155288AA00A3F9ED /* Run Script: Fabric */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputPaths = ( inputPaths = (
); );
name = "Run Script: Crashlytics"; name = "Run Script: Fabric";
outputPaths = ( outputPaths = (
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = "/bin/sh -e"; 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; showEnvVarsInLog = 0;
}; };
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
@@ -2408,8 +2409,8 @@
93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */, 93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */,
DA32CFDF19CF1C70004F3F0E /* MPSiteEntity.m in Sources */, DA32CFDF19CF1C70004F3F0E /* MPSiteEntity.m in Sources */,
93D394C4254EEB45FB335AFB /* MPSitesTableView.m in Sources */, 93D394C4254EEB45FB335AFB /* MPSitesTableView.m in Sources */,
DA95B5231C477DE10067F5EF /* MasterPassword.xcdatamodeld in Sources */,
DA6774291A4746AF004F356A /* mpw-algorithm.c in Sources */, DA6774291A4746AF004F356A /* mpw-algorithm.c in Sources */,
DA0CC5591EB6AE45009A8ED9 /* MasterPassword.xcdatamodeld in Sources */,
93D395E4830290EBB6E71F34 /* MPNoStateButton.m in Sources */, 93D395E4830290EBB6E71F34 /* MPNoStateButton.m in Sources */,
DA4DAE941A7D8117003E5423 /* MPAlgorithmV3.m in Sources */, DA4DAE941A7D8117003E5423 /* MPAlgorithmV3.m in Sources */,
DA4DAE951A7D8117003E5423 /* MPTypes.m in Sources */, DA4DAE951A7D8117003E5423 /* MPTypes.m in Sources */,
@@ -3129,19 +3130,20 @@
/* End XCConfigurationList section */ /* End XCConfigurationList section */
/* Begin XCVersionGroup section */ /* Begin XCVersionGroup section */
DA95B51A1C477DE10067F5EF /* MasterPassword.xcdatamodeld */ = { DA0CC54F1EB6AE45009A8ED9 /* MasterPassword.xcdatamodeld */ = {
isa = XCVersionGroup; isa = XCVersionGroup;
children = ( children = (
DA95B51B1C477DE10067F5EF /* MasterPassword 1.xcdatamodel */, DA0CC5501EB6AE45009A8ED9 /* MasterPassword 1.xcdatamodel */,
DA95B51C1C477DE10067F5EF /* MasterPassword 2.xcdatamodel */, DA0CC5511EB6AE45009A8ED9 /* MasterPassword 2.xcdatamodel */,
DA95B51D1C477DE10067F5EF /* MasterPassword 3.xcdatamodel */, DA0CC5521EB6AE45009A8ED9 /* MasterPassword 3.xcdatamodel */,
DA95B51E1C477DE10067F5EF /* MasterPassword 4.xcdatamodel */, DA0CC5531EB6AE45009A8ED9 /* MasterPassword 4.xcdatamodel */,
DA95B51F1C477DE10067F5EF /* MasterPassword 5.xcdatamodel */, DA0CC5541EB6AE45009A8ED9 /* MasterPassword 5.xcdatamodel */,
DA95B5201C477DE10067F5EF /* MasterPassword 6.xcdatamodel */, DA0CC5551EB6AE45009A8ED9 /* MasterPassword 6.xcdatamodel */,
DA95B5211C477DE10067F5EF /* MasterPassword 7.xcdatamodel */, DA0CC5561EB6AE45009A8ED9 /* MasterPassword 7.xcdatamodel */,
DA95B5221C477DE10067F5EF /* MasterPassword 8.xcdatamodel */, DA0CC5571EB6AE45009A8ED9 /* MasterPassword 8.xcdatamodel */,
DA0CC5581EB6AE45009A8ED9 /* MasterPassword 9.xcdatamodel */,
); );
currentVersion = DA95B5221C477DE10067F5EF /* MasterPassword 8.xcdatamodel */; currentVersion = DA0CC5581EB6AE45009A8ED9 /* MasterPassword 9.xcdatamodel */;
path = MasterPassword.xcdatamodeld; path = MasterPassword.xcdatamodeld;
sourceTree = "<group>"; sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel; versionGroupType = wrapper.xcdatamodel;

View File

@@ -41,9 +41,9 @@ case $PLATFORM_NAME in
*) ftl 'ERROR: Unknown platform: %s.' "$PLATFORM_NAME"; exit 1 ;; *) ftl 'ERROR: Unknown platform: %s.' "$PLATFORM_NAME"; exit 1 ;;
esac 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=${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" addPlistWithKey GITDescription string "$description"
setPlistWithKey CFBundleVersion "$(hr "${version%%.*}" 14).${version#*.}" setPlistWithKey CFBundleVersion "$(hr "${version%%.*}" 14).${version#*.}"
@@ -55,14 +55,14 @@ setSettingWithTitle "Copyright" "$(getPlistWithKey NSHumanReadableCopyright)"
if [[ $DEPLOYMENT_LOCATION = YES ]]; then if [[ $DEPLOYMENT_LOCATION = YES ]]; then
# This build is a release. Do some release checks. # 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 passed=1
[[ $description != *-dirty ]] || \ [[ $description != *-dirty ]] || \
{ passed=0; err 'ERROR: Cannot release a dirty version, first commit any changes.'; } { passed=0; err 'ERROR: Cannot release a dirty version, first commit any changes.'; }
[[ $build == 0 ]] || \ [[ $build == 0 ]] || \
{ passed=0; err 'ERROR: Commit is not tagged for release, first tag accordingly.'; } { passed=0; err 'ERROR: Commit is not tagged for release, first tag accordingly.'; }
[[ -r "$crashlyticsPlist" && $(PlistBuddy -c "Print :'API Key'" "$crashlyticsPlist" 2>/dev/null) ]] || \ [[ -r "$fabricPlist" && $(PlistBuddy -c "Print :'API Key'" "$fabricPlist" 2>/dev/null) ]] || \
{ passed=0; err 'ERROR: Cannot release: Crashlytics API key is missing.'; } { passed=0; err 'ERROR: Cannot release: Fabric API key is missing.'; }
(( passed )) || \ (( passed )) || \
{ ftl "Failed to pass release checks. Fix the above errors and re-try. Aborting."; exit 1; } { ftl "Failed to pass release checks. Fix the above errors and re-try. Aborting."; exit 1; }
fi fi

View File

@@ -17,9 +17,9 @@
//============================================================================== //==============================================================================
#import "MPKey.h" #import "MPKey.h"
#import "MPStoredSiteEntity.h" #import "MPStoredSiteEntity+CoreDataClass.h"
#import "MPGeneratedSiteEntity.h" #import "MPGeneratedSiteEntity+CoreDataClass.h"
#import "MPSiteQuestionEntity.h" #import "MPSiteQuestionEntity+CoreDataClass.h"
#import "mpw-algorithm.h" #import "mpw-algorithm.h"
#define MPAlgorithmDefaultVersion MPAlgorithmVersionCurrent #define MPAlgorithmDefaultVersion MPAlgorithmVersionCurrent

View File

@@ -93,7 +93,7 @@ NSOperationQueue *_mpwQueue = nil;
migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d AND user == %@", self.version, user]; migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d AND user == %@", self.version, user];
NSArray *migrationSites = [moc executeFetchRequest:migrationRequest error:&error]; NSArray *migrationSites = [moc executeFetchRequest:migrationRequest error:&error];
if (!migrationSites) { if (!migrationSites) {
err( @"While looking for sites to migrate: %@", [error fullDescription] ); MPError( error, @"While looking for sites to migrate." );
return NO; return NO;
} }

View File

@@ -25,17 +25,20 @@
#define MPProductTouchID @"com.lyndir.masterpassword.products.touchid" #define MPProductTouchID @"com.lyndir.masterpassword.products.touchid"
#define MPProductFuel @"com.lyndir.masterpassword.products.fuel" #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 @protocol MPInAppDelegate
- (void)updateWithProducts:(NSArray /* SKProduct */ *)products; - (void)updateWithProducts:(NSDictionary<NSString *, SKProduct *> *)products
- (void)updateWithTransaction:(SKPaymentTransaction *)transaction; transactions:(NSDictionary<NSString *, SKPaymentTransaction *> *)transactions;
@end @end
@interface MPAppDelegate_Shared(InApp) @interface MPAppDelegate_Shared(InApp)
- (NSDictionary<NSString *, SKProduct *> *)products;
- (NSDictionary<NSString *, SKPaymentTransaction *> *)transactions;
- (void)registerProductsObserver:(id<MPInAppDelegate>)delegate; - (void)registerProductsObserver:(id<MPInAppDelegate>)delegate;
- (void)removeProductsObserver:(id<MPInAppDelegate>)delegate; - (void)removeProductsObserver:(id<MPInAppDelegate>)delegate;

View File

@@ -23,9 +23,20 @@
@implementation MPAppDelegate_Shared(InApp) @implementation MPAppDelegate_Shared(InApp)
PearlAssociatedObjectProperty( NSArray*, Products, products ); PearlAssociatedObjectProperty( NSDictionary*, Products, products );
PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObservers ); 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 { - (void)registerProductsObserver:(id<MPInAppDelegate>)delegate {
if (!self.productObservers) if (!self.productObservers)
@@ -33,7 +44,7 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
[self.productObservers addObject:delegate]; [self.productObservers addObject:delegate];
if (self.products) if (self.products)
[delegate updateWithProducts:self.products]; [delegate updateWithProducts:self.products transactions:[self transactions]];
else else
[self reloadProducts]; [self reloadProducts];
} }
@@ -101,11 +112,13 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
} }
#endif #endif
for (SKProduct *product in self.products) for (SKProduct *product in [self.products allValues])
if ([product.productIdentifier isEqualToString:productIdentifier]) { if ([product.productIdentifier isEqualToString:productIdentifier]) {
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product]; SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
if (payment) {
payment.quantity = quantity; payment.quantity = quantity;
[[self paymentQueue] addPayment:payment]; [[self paymentQueue] addPayment:payment];
}
return; return;
} }
} }
@@ -114,15 +127,22 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
inf( @"products: %@, invalid: %@", response.products, response.invalidProductIdentifiers ); if ([response.invalidProductIdentifiers count])
self.products = response.products; 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) 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 { - (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
MPError( error, @"StoreKit request (%@) failed.", request );
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
[PearlAlert showAlertWithTitle:@"Purchase Failed" message: [PearlAlert showAlertWithTitle:@"Purchase Failed" message:
strf( @"%@\n\n%@", error.localizedDescription, strf( @"%@\n\n%@", error.localizedDescription,
@@ -131,7 +151,6 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
cancelTitle:@"OK" otherTitles:nil]; cancelTitle:@"OK" otherTitles:nil];
#else #else
#endif #endif
err( @"StoreKit request (%@) failed: %@", request, [error fullDescription] );
} }
- (void)requestDidFinish:(SKRequest *)request { - (void)requestDidFinish:(SKRequest *)request {
@@ -145,23 +164,41 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
for (SKPaymentTransaction *transaction in transactions) { for (SKPaymentTransaction *transaction in transactions) {
dbg( @"transaction updated: %@ -> %d", transaction.payment.productIdentifier, (int)(transaction.transactionState) ); dbg( @"transaction updated: %@ -> %d", transaction.payment.productIdentifier, (int)(transaction.transactionState) );
switch (transaction.transactionState) { switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased: { case SKPaymentTransactionStatePurchased: {
inf( @"purchased: %@", transaction.payment.productIdentifier ); inf( @"Purchased: %@", transaction.payment.productIdentifier );
NSMutableDictionary *attributes = [NSMutableDictionary new];
if ([transaction.payment.productIdentifier isEqualToString:MPProductFuel]) { if ([transaction.payment.productIdentifier isEqualToString:MPProductFuel]) {
float currentFuel = [[MPiOSConfig get].developmentFuelRemaining floatValue]; float currentFuel = [[MPiOSConfig get].developmentFuelRemaining floatValue];
float purchasedFuel = transaction.payment.quantity / MP_FUEL_HOURLY_RATE; float purchasedFuel = transaction.payment.quantity / MP_FUEL_HOURLY_RATE;
[MPiOSConfig get].developmentFuelRemaining = @(currentFuel + purchasedFuel); [MPiOSConfig get].developmentFuelRemaining = @(currentFuel + purchasedFuel);
if (![MPiOSConfig get].developmentFuelChecked || currentFuel < DBL_EPSILON) if (![MPiOSConfig get].developmentFuelChecked || currentFuel < DBL_EPSILON)
[MPiOSConfig get].developmentFuelChecked = [NSDate date]; [MPiOSConfig get].developmentFuelChecked = [NSDate date];
[attributes addEntriesFromDictionary:@{
@"currentFuel" : @(currentFuel),
@"purchasedFuel": @(purchasedFuel),
}];
} }
[[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier [[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier
forKey:transaction.payment.productIdentifier]; forKey:transaction.payment.productIdentifier];
[queue finishTransaction:transaction]; [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; break;
} }
case SKPaymentTransactionStateRestored: { case SKPaymentTransactionStateRestored: {
inf( @"restored: %@", transaction.payment.productIdentifier ); inf( @"Restored: %@", transaction.payment.productIdentifier );
[[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier [[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier
forKey:transaction.payment.productIdentifier]; forKey:transaction.payment.productIdentifier];
[queue finishTransaction:transaction]; [queue finishTransaction:transaction];
@@ -171,21 +208,38 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
case SKPaymentTransactionStateDeferred: case SKPaymentTransactionStateDeferred:
break; break;
case SKPaymentTransactionStateFailed: case SKPaymentTransactionStateFailed:
err( @"Transaction failed: %@, reason: %@", transaction.payment.productIdentifier, [transaction.error fullDescription] ); MPError( transaction.error, @"Transaction failed: %@.", transaction.payment.productIdentifier );
[queue finishTransaction:transaction]; [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; break;
} }
for (id<MPInAppDelegate> productObserver in self.productObservers)
[productObserver updateWithTransaction:transaction];
} }
if (![[NSUserDefaults standardUserDefaults] synchronize]) if (![[NSUserDefaults standardUserDefaults] synchronize])
wrn( @"Couldn't synchronize after transaction updates." ); 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 { - (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
err( @"StoreKit restore failed: %@", [error fullDescription] ); MPError( error, @"StoreKit restore failed." );
} }
@end @end

View File

@@ -16,18 +16,19 @@
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>. // LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//============================================================================== //==============================================================================
#import <Crashlytics/Answers.h>
#import "MPAppDelegate_Key.h" #import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
@interface MPAppDelegate_Shared() @interface MPAppDelegate_Shared()
@property(strong, nonatomic) MPKey *key; @property(strong, atomic) MPKey *key;
@end @end
@implementation MPAppDelegate_Shared(Key) @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 TARGET_OS_IPHONE
if (user.touchID && kSecUseAuthenticationUI) { if (user.touchID && kSecUseAuthenticationUI) {
@@ -37,8 +38,8 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
CFErrorRef acError = NULL; CFErrorRef acError = NULL;
id accessControl = (__bridge_transfer id)SecAccessControlCreateWithFlags( kCFAllocatorDefault, id accessControl = (__bridge_transfer id)SecAccessControlCreateWithFlags( kCFAllocatorDefault,
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlTouchIDCurrentSet, &acError ); kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlTouchIDCurrentSet, &acError );
if (!accessControl || acError) if (!accessControl)
err( @"Could not use TouchID on this device: %@", acError ); MPError( (__bridge_transfer NSError *)acError, @"Could not use TouchID on this device." );
else else
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
@@ -71,7 +72,7 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
- (MPKey *)loadSavedKeyFor:(MPUserEntity *)user { - (MPKey *)loadSavedKeyFor:(MPUserEntity *)user {
MPKeyOrigin keyOrigin; MPKeyOrigin keyOrigin;
NSDictionary *keyQuery = createKeyQuery( user, NO, &keyOrigin ); NSDictionary *keyQuery = [self createKeyQueryforUser:user origin:&keyOrigin];
id<MPAlgorithm> keyAlgorithm = user.algorithm; id<MPAlgorithm> keyAlgorithm = user.algorithm;
MPKey *key = [[MPKey alloc] initForFullName:user.name withKeyResolver:^NSData *(id<MPAlgorithm> algorithm) { MPKey *key = [[MPKey alloc] initForFullName:user.name withKeyResolver:^NSData *(id<MPAlgorithm> algorithm) {
return ![algorithm isEqual:keyAlgorithm]? nil: return ![algorithm isEqual:keyAlgorithm]? nil:
@@ -99,7 +100,7 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
[self forgetSavedKeyFor:user]; [self forgetSavedKeyFor:user];
inf( @"Saving key in keychain for user: %@", user.userID ); 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 }]; withAttributes:@{ (__bridge id)kSecValueData: keyData }];
} }
} }
@@ -107,7 +108,7 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
- (void)forgetSavedKeyFor:(MPUserEntity *)user { - (void)forgetSavedKeyFor:(MPUserEntity *)user {
OSStatus result = [PearlKeyChain deleteItemForQuery:createKeyQuery( user, NO, nil )]; OSStatus result = [PearlKeyChain deleteItemForQuery:[self createKeyQueryforUser:user origin:nil]];
if (result == noErr) { if (result == noErr) {
inf( @"Removed key from keychain for user: %@", user.userID ); inf( @"Removed key from keychain for user: %@", user.userID );
@@ -177,6 +178,14 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
else else
dbg( @"Automatic login failed for user: %@", user.userID ); 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; return NO;
} }
inf( @"Logged in user: %@", user.userID ); inf( @"Logged in user: %@", user.userID );
@@ -198,8 +207,11 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
@try { @try {
if ([[MPConfig get].sendInfo boolValue]) { if ([[MPConfig get].sendInfo boolValue]) {
#ifdef CRASHLYTICS #ifdef CRASHLYTICS
[[Crashlytics sharedInstance] setObjectValue:user.userID forKey:@"username"];
[[Crashlytics sharedInstance] setUserName:user.userID]; [[Crashlytics sharedInstance] setUserName:user.userID];
[Answers logLoginWithMethod:password? @"Password": @"Automatic" success:@YES customAttributes:@{
@"algorithm": @(user.algorithm.version),
}];
#endif #endif
} }
} }

View File

@@ -26,9 +26,9 @@
#endif #endif
@property(strong, nonatomic, readonly) MPKey *key; @property(strong, atomic, readonly) MPKey *key;
@property(strong, nonatomic, readonly) NSManagedObjectID *activeUserOID; @property(strong, atomic, readonly) NSManagedObjectID *activeUserOID;
@property(strong, nonatomic, readonly) NSPersistentStoreCoordinator *storeCoordinator; @property(strong, atomic, readonly) NSPersistentStoreCoordinator *storeCoordinator;
+ (instancetype)get; + (instancetype)get;

View File

@@ -23,9 +23,9 @@
@interface MPAppDelegate_Shared() @interface MPAppDelegate_Shared()
@property(strong, nonatomic) MPKey *key; @property(strong, atomic) MPKey *key;
@property(strong, nonatomic) NSManagedObjectID *activeUserOID; @property(strong, atomic) NSManagedObjectID *activeUserOID;
@property(strong, nonatomic) NSPersistentStoreCoordinator *storeCoordinator; @property(strong, atomic) NSPersistentStoreCoordinator *storeCoordinator;
@end @end
@@ -74,14 +74,11 @@
- (void)setActiveUser:(MPUserEntity *)activeUser { - (void)setActiveUser:(MPUserEntity *)activeUser {
NSError *error; self.activeUserOID = activeUser.permanentObjectID;
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;
} }
- (void)handleCoordinatorError:(NSError *)error { - (void)handleCoordinatorError:(NSError *)error {
} }
@end @end

View File

@@ -237,7 +237,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
NSURL *localStoreURL = [self localStoreURL]; NSURL *localStoreURL = [self localStoreURL];
if (![[NSFileManager defaultManager] createDirectoryAtURL:[localStoreURL URLByDeletingLastPathComponent] if (![[NSFileManager defaultManager] createDirectoryAtURL:[localStoreURL URLByDeletingLastPathComponent]
withIntermediateDirectories:YES attributes:nil error:&error]) { 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; return;
} }
if (![self.storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self localStoreURL] if (![self.storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self localStoreURL]
@@ -246,7 +246,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
NSInferMappingModelAutomaticallyOption : @YES, NSInferMappingModelAutomaticallyOption : @YES,
STORE_OPTIONS STORE_OPTIONS
} error:&error]) { } error:&error]) {
err( @"Failed to open store: %@", [error fullDescription] ); MPError( error, @"Failed to open store." );
self.storeCorrupted = @YES; self.storeCorrupted = @YES;
[self handleCoordinatorError:error]; [self handleCoordinatorError:error];
return; return;
@@ -255,12 +255,15 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
PearlAddNotificationObserver( UIApplicationWillResignActiveNotification, UIApp, [NSOperationQueue mainQueue], PearlAddNotificationObserver( UIApplicationWillResignActiveNotification, UIApp, [NSOperationQueue mainQueue],
#else
PearlAddNotificationObserver( NSApplicationWillResignActiveNotification, NSApp, [NSOperationQueue mainQueue],
#endif
^(MPAppDelegate_Shared *self, NSNotification *note) { ^(MPAppDelegate_Shared *self, NSNotification *note) {
[self.mainManagedObjectContext saveToStore]; [self.mainManagedObjectContext saveToStore];
} ); } );
#else
PearlAddNotificationObserver( NSApplicationWillResignActiveNotification, NSApp, [NSOperationQueue mainQueue],
^(MPAppDelegate_Shared *self, NSNotification *note) {
[self.mainManagedObjectContext saveToStore];
} );
#endif
// Perform a data sanity check on the newly loaded store to find and fix any issues. // Perform a data sanity check on the newly loaded store to find and fix any issues.
if ([[MPConfig get].checkInconsistency boolValue]) if ([[MPConfig get].checkInconsistency boolValue])
@@ -286,10 +289,10 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
NSError *error = nil; NSError *error = nil;
for (NSPersistentStore *store in self.storeCoordinator.persistentStores) { for (NSPersistentStore *store in self.storeCoordinator.persistentStores) {
if (![self.storeCoordinator removePersistentStore:store error:&error]) 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]) 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]; [self loadStore];
} }
@@ -307,7 +310,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
fetchRequest.entity = entity; fetchRequest.entity = entity;
NSArray *objects = [context executeFetchRequest:fetchRequest error:&error]; NSArray *objects = [context executeFetchRequest:fetchRequest error:&error];
if (!objects) { if (!objects) {
err( @"Failed to fetch %@ objects: %@", entity, [error fullDescription] ); MPError( error, @"Failed to fetch %@ objects.", entity );
continue; continue;
} }
@@ -332,7 +335,8 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
- (void)migrateStore { - (void)migrateStore {
MPStoreMigrationLevel migrationLevel = (signed)[[NSUserDefaults standardUserDefaults] integerForKey:MPMigrationLevelLocalStoreKey]; MPStoreMigrationLevel migrationLevel = (MPStoreMigrationLevel)
[[NSUserDefaults standardUserDefaults] integerForKey:MPMigrationLevelLocalStoreKey];
if (migrationLevel >= MPStoreMigrationLevelCurrent) if (migrationLevel >= MPStoreMigrationLevelCurrent)
// Local store up-to-date. // Local store up-to-date.
return; return;
@@ -374,8 +378,8 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
NSError *error = nil; NSError *error = nil;
if (![NSPersistentStore migrateStore:oldLocalStoreURL withOptions:@{ STORE_OPTIONS } if (![NSPersistentStore migrateStore:oldLocalStoreURL withOptions:@{ STORE_OPTIONS }
toStore:newLocalStoreURL withOptions:@{ STORE_OPTIONS } toStore:newLocalStoreURL withOptions:@{ STORE_OPTIONS }
model:nil error:&error]) { error:&error]) {
err( @"Couldn't migrate the old store to the new location: %@", [error fullDescription] ); MPError( error, @"Couldn't migrate the old store to the new location." );
return NO; return NO;
} }
@@ -421,14 +425,52 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
NSMigratePersistentStoresAutomaticallyOption: @YES, NSMigratePersistentStoresAutomaticallyOption: @YES,
NSInferMappingModelAutomaticallyOption : @YES, NSInferMappingModelAutomaticallyOption : @YES,
STORE_OPTIONS STORE_OPTIONS
} model:nil error:&error]) { } error:&error]) {
err( @"Couldn't migrate the old store to the new location: %@", [error fullDescription] ); MPError( error, @"Couldn't migrate the old store to the new location." );
return NO; return NO;
} }
return YES; 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 #pragma mark - Utilities
- (void)addSiteNamed:(NSString *)siteName completion:(void ( ^ )(MPSiteEntity *site, NSManagedObjectContext *context))completion { - (void)addSiteNamed:(NSString *)siteName completion:(void ( ^ )(MPSiteEntity *site, NSManagedObjectContext *context))completion {
@@ -457,10 +499,6 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
site.lastUsed = [NSDate date]; site.lastUsed = [NSDate date];
site.algorithm = algorithm; 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]; [context saveToStore];
completion( site, context ); completion( site, context );
@@ -489,18 +527,14 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
newSite.algorithm = site.algorithm; newSite.algorithm = site.algorithm;
newSite.loginName = site.loginName; 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 deleteObject:site];
[context saveToStore]; [context saveToStore];
[[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.objectID]; [[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.permanentObjectID];
site = newSite; site = newSite;
} }
[[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.objectID]; [[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.permanentObjectID];
return site; return site;
} }
@@ -537,7 +571,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
initWithPattern:@"^#[[:space:]]*([^:]+): (.*)" initWithPattern:@"^#[[:space:]]*([^:]+): (.*)"
options:(NSRegularExpressionOptions)0 error:&error]; options:(NSRegularExpressionOptions)0 error:&error];
if (error) { if (error) {
err( @"Error loading the header pattern: %@", [error fullDescription] ); MPError( error, @"Error loading the header pattern." );
return MPImportResultInternalError; return MPImportResultInternalError;
} }
} }
@@ -551,7 +585,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
options:(NSRegularExpressionOptions)0 error:&error] options:(NSRegularExpressionOptions)0 error:&error]
]; ];
if (error) { if (error) {
err( @"Error loading the site patterns: %@", [error fullDescription] ); MPError( error, @"Error loading the site patterns." );
return MPImportResultInternalError; return MPImportResultInternalError;
} }
} }
@@ -610,7 +644,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", importUserName]; userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", importUserName];
NSArray *users = [context executeFetchRequest:userFetchRequest error:&error]; NSArray *users = [context executeFetchRequest:userFetchRequest error:&error];
if (!users) { if (!users) {
err( @"While looking for user: %@, error: %@", importUserName, [error fullDescription] ); MPError( error, @"While looking for user: %@.", importUserName );
return MPImportResultInternalError; return MPImportResultInternalError;
} }
if ([users count] > 1) { if ([users count] > 1) {
@@ -694,7 +728,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
siteFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", siteName, user]; siteFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", siteName, user];
NSArray *existingSites = [context executeFetchRequest:siteFetchRequest error:&error]; NSArray *existingSites = [context executeFetchRequest:siteFetchRequest error:&error];
if (!existingSites) { 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; return MPImportResultInternalError;
} }
if ([existingSites count]) { if ([existingSites count]) {

View File

@@ -20,7 +20,7 @@
[self.defaults registerDefaults:@{ [self.defaults registerDefaults:@{
NSStringFromSelector( @selector( askForReviews ) ) : @YES, NSStringFromSelector( @selector( askForReviews ) ) : @YES,
NSStringFromSelector( @selector( sendInfo ) ) : @NO, NSStringFromSelector( @selector( sendInfo ) ) : @YES,
NSStringFromSelector( @selector( rememberLogin ) ) : @NO, NSStringFromSelector( @selector( rememberLogin ) ) : @NO,
NSStringFromSelector( @selector( hidePasswords ) ) : @NO, NSStringFromSelector( @selector( hidePasswords ) ) : @NO,
NSStringFromSelector( @selector( checkInconsistency ) ): @NO, NSStringFromSelector( @selector( checkInconsistency ) ): @NO,

View File

@@ -7,10 +7,10 @@
// //
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import "MPSiteEntity.h" #import "MPSiteEntity+CoreDataClass.h"
#import "MPStoredSiteEntity.h" #import "MPStoredSiteEntity+CoreDataClass.h"
#import "MPGeneratedSiteEntity.h" #import "MPGeneratedSiteEntity+CoreDataClass.h"
#import "MPUserEntity.h" #import "MPUserEntity+CoreDataClass.h"
#import "MPAlgorithm.h" #import "MPAlgorithm.h"
#import "MPFixable.h" #import "MPFixable.h"
@@ -22,6 +22,12 @@
@end @end
@interface NSManagedObject(MP)
- (NSManagedObjectID *)permanentObjectID;
@end
@interface MPSiteQuestionEntity(MP) @interface MPSiteQuestionEntity(MP)
- (NSString *)resolveQuestionAnswerUsingKey:(MPKey *)key; - (NSString *)resolveQuestionAnswerUsingKey:(MPKey *)key;

View File

@@ -21,11 +21,11 @@
@try { @try {
NSError *error = nil; NSError *error = nil;
if (!(success = [self save:&error])) if (!(success = [self save:&error]))
err( @"While saving: %@", [error fullDescription] ); MPError( error, @"While saving." );
} }
@catch (NSException *exception) { @catch (NSException *exception) {
success = NO; success = NO;
err( @"While saving: %@", [exception fullDescription] ); err( @"While saving.\n%@", [exception fullDescription] );
} }
}]; }];
@@ -34,6 +34,23 @@
@end @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) @implementation MPSiteQuestionEntity(MP)
- (NSString *)resolveQuestionAnswerUsingKey:(MPKey *)key { - (NSString *)resolveQuestionAnswerUsingKey:(MPKey *)key {

View 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"

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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"

View 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

View 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

View 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

View File

@@ -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

View File

@@ -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

View 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"

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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"

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -6,8 +6,12 @@
// Copyright (c) 2012 Lyndir. All rights reserved. // Copyright (c) 2012 Lyndir. All rights reserved.
// //
#import <Crashlytics/Crashlytics.h>
#import <Crashlytics/Answers.h>
__BEGIN_DECLS __BEGIN_DECLS
extern NSString *const MPErrorDomain; extern NSString *const MPErrorDomain;
extern NSInteger const MPErrorHangCode;
extern NSString *const MPSignedInNotification; extern NSString *const MPSignedInNotification;
extern NSString *const MPSignedOutNotification; extern NSString *const MPSignedOutNotification;
@@ -19,4 +23,21 @@ extern NSString *const MPFoundInconsistenciesNotification;
extern NSString *const MPSitesImportedNotificationUserKey; extern NSString *const MPSitesImportedNotificationUserKey;
extern NSString *const MPInconsistenciesFixResultUserKey; extern NSString *const MPInconsistenciesFixResultUserKey;
__END_DECLS __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

View File

@@ -9,6 +9,7 @@
#import "MPTypes.h" #import "MPTypes.h"
NSString *const MPErrorDomain = @"MPErrorDomain"; NSString *const MPErrorDomain = @"MPErrorDomain";
NSInteger const MPErrorHangCode = 1;
NSString *const MPSignedInNotification = @"MPSignedInNotification"; NSString *const MPSignedInNotification = @"MPSignedInNotification";
NSString *const MPSignedOutNotification = @"MPSignedOutNotification"; NSString *const MPSignedOutNotification = @"MPSignedOutNotification";

View 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"

View 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

View 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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -78,7 +78,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[[Crashlytics sharedInstance] setUserIdentifier:[PearlKeyChain deviceIdentifier]]; [[Crashlytics sharedInstance] setUserIdentifier:[PearlKeyChain deviceIdentifier]];
[[Crashlytics sharedInstance] setObjectValue:[PearlKeyChain deviceIdentifier] forKey:@"deviceIdentifier"]; [[Crashlytics sharedInstance] setObjectValue:[PearlKeyChain deviceIdentifier] forKey:@"deviceIdentifier"];
[[Crashlytics sharedInstance] setUserName:@"Anonymous"]; [[Crashlytics sharedInstance] setUserName:@"Anonymous"];
[[Crashlytics sharedInstance] setObjectValue:@"Anonymous" forKey:@"username"];
[Crashlytics startWithAPIKey:crashlyticsAPIKey]; [Crashlytics startWithAPIKey:crashlyticsAPIKey];
[[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) { [[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
PearlLogLevel level = PearlLogLevelInfo; PearlLogLevel level = PearlLogLevelInfo;
@@ -172,17 +171,17 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
// Save changes in the application's managed object context before the application terminates. // Save changes in the application's managed object context before the application terminates.
NSManagedObjectContext *context = [MPMacAppDelegate managedObjectContextForMainThreadIfReady]; NSManagedObjectContext *mainContext = [MPMacAppDelegate managedObjectContextForMainThreadIfReady];
if (!context) if (!mainContext)
return NSTerminateNow; return NSTerminateNow;
if (![context commitEditing]) if (![mainContext commitEditing])
return NSTerminateCancel; return NSTerminateCancel;
if (![context hasChanges]) if (![mainContext hasChanges])
return NSTerminateNow; return NSTerminateNow;
[context saveToStore]; [mainContext saveToStore];
return NSTerminateNow; return NSTerminateNow;
} }
@@ -272,7 +271,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler: [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:
^(NSData *importedSitesData, NSURLResponse *response, NSError *error) { ^(NSData *importedSitesData, NSURLResponse *response, NSError *error) {
if (error) if (error)
err( @"While reading imported sites from %@: %@", url, [error fullDescription] ); MPError( error, @"While reading imported sites from %@.", url );
if (!importedSitesData) if (!importedSitesData)
return; return;
@@ -389,20 +388,16 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
return; return;
NSString *name = [(NSSecureTextField *)alert.accessoryView stringValue]; NSString *name = [(NSSecureTextField *)alert.accessoryView stringValue];
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPUserEntity *newUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass( [MPUserEntity class] ) MPUserEntity *newUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass( [MPUserEntity class] )
inManagedObjectContext:moc]; inManagedObjectContext:context];
newUser.name = name; newUser.name = name;
[moc saveToStore]; [context saveToStore];
NSError *error = nil;
if (![moc obtainPermanentIDsForObjects:@[ newUser ] error:&error])
err( @"Failed to obtain permanent object ID for new user: %@", [error fullDescription] );
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self updateUsers];
[self setActiveUser:newUser]; [self setActiveUser:newUser];
PearlMainQueue( ^{
[self showPasswordWindow:nil]; [self showPasswordWindow:nil];
}]; } );
}]; }];
} }
@@ -416,10 +411,10 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
if ([alert runModal] != NSAlertFirstButtonReturn) if ([alert runModal] != NSAlertFirstButtonReturn)
return; return;
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
[moc deleteObject:[self activeUserInContext:moc]]; [context deleteObject:[self activeUserInContext:context]];
[self setActiveUser:nil]; [self setActiveUser:nil];
[moc saveToStore]; [context saveToStore];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self updateUsers]; [self updateUsers];
@@ -516,21 +511,24 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
NSError *coordinateError = nil; NSError *coordinateError = nil;
NSString *exportedSites = [self exportSitesRevealPasswords:revealPasswords]; NSString *exportedSites = [self exportSitesRevealPasswords:revealPasswords];
[[[NSFileCoordinator alloc] initWithFilePresenter:nil] coordinateWritingItemAtURL:savePanel.URL options:0 error:&coordinateError [[[NSFileCoordinator alloc] initWithFilePresenter:nil] coordinateWritingItemAtURL:savePanel.URL options:0
byAccessor:^(NSURL *newURL) { error:&coordinateError byAccessor:
^(NSURL *newURL) {
NSError *writeError = nil; NSError *writeError = nil;
if (![exportedSites writeToURL:newURL atomically:NO if (![exportedSites writeToURL:newURL atomically:NO encoding:NSUTF8StringEncoding error:&writeError])
encoding:NSUTF8StringEncoding MPError( writeError, @"Could not write to the export file." );
error:&writeError])
PearlMainQueue( ^{ PearlMainQueue( ^{
[[NSAlert alertWithError:writeError] runModal]; [[NSAlert alertWithError:writeError] runModal];
} ); } );
}]; }];
if (coordinateError) if (coordinateError) {
MPError( coordinateError, @"Write access to the export file could not be obtained." );
PearlMainQueue( ^{ PearlMainQueue( ^{
[[NSAlert alertWithError:coordinateError] runModal]; [[NSAlert alertWithError:coordinateError] runModal];
} ); } );
} }
}
- (void)updateUsers { - (void)updateUsers {
@@ -567,7 +565,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ]; fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error]; NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
if (!users) if (!users)
err( @"Failed to load users: %@", [error fullDescription] ); MPError( error, @"Failed to load users." );
if (![users count]) { if (![users count]) {
NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""]; 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) { for (MPUserEntity *user in users) {
NSMenuItem *userItem = [[NSMenuItem alloc] initWithTitle:user.name action:@selector( selectUser: ) keyEquivalent:@""]; NSMenuItem *userItem = [[NSMenuItem alloc] initWithTitle:user.name action:@selector( selectUser: ) keyEquivalent:@""];
[userItem setTarget:self]; [userItem setTarget:self];
[userItem setRepresentedObject:[user objectID]]; [userItem setRepresentedObject:user.permanentObjectID];
[[self.usersItem submenu] addItem:userItem]; [[self.usersItem submenu] addItem:userItem];
if (!mainActiveUser && [user.name isEqualToString:[MPMacConfig get].usedUserName]) if (!mainActiveUser && [user.name isEqualToString:[MPMacConfig get].usedUserName])

View File

@@ -160,10 +160,10 @@
- (IBAction)doUnlockUser:(id)sender { - (IBAction)doUnlockUser:(id)sender {
[self.progressView startAnimation:self]; [self.progressView startAnimation:self];
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc]; MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:context];
NSString *userName = activeUser.name; 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( ^{ PearlMainQueue( ^{
self.masterPassword = nil; self.masterPassword = nil;
@@ -555,7 +555,7 @@
NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error]; NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error];
if (!siteResults) { if (!siteResults) {
prof_finish( @"executeFetchRequest: %@ // %@", fetchRequest.predicate, [error fullDescription] ); prof_finish( @"executeFetchRequest: %@ // %@", fetchRequest.predicate, [error fullDescription] );
err( @"While fetching sites for completion: %@", [error fullDescription] ); MPError( error, @"While fetching sites for completion." );
return; return;
} }
prof_rewind( @"executeFetchRequest: %@", fetchRequest.predicate ); prof_rewind( @"executeFetchRequest: %@", fetchRequest.predicate );
@@ -633,9 +633,9 @@
return; return;
} }
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
[[self.selectedSite entityInContext:moc] use]; [[self.selectedSite entityInContext:context] use];
[moc saveToStore]; [context saveToStore];
}]; }];
} }

View File

@@ -52,9 +52,9 @@
- (void)setEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups { - (void)setEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups {
if ([_entityOID isEqual:entity.objectID]) if ([_entityOID isEqual:entity.permanentObjectID])
return; return;
_entityOID = entity.objectID; _entityOID = entity.permanentObjectID;
NSString *siteName = entity.name; NSString *siteName = entity.name;
NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName]; NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName];
@@ -115,7 +115,7 @@
NSError *error; NSError *error;
MPSiteEntity *entity = (MPSiteEntity *)[moc existingObjectWithID:_entityOID error:&error]; MPSiteEntity *entity = (MPSiteEntity *)[moc existingObjectWithID:_entityOID error:&error];
if (!entity) if (!entity)
err( @"Couldn't retrieve active site: %@", [error fullDescription] ); MPError( error, @"Couldn't retrieve active site." );
return entity; return entity;
} }

View File

@@ -3,6 +3,6 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>_XCCurrentVersionName</key> <key>_XCCurrentVersionName</key>
<string>MasterPassword 8.xcdatamodel</string> <string>MasterPassword 9.xcdatamodel</string>
</dict> </dict>
</plist> </plist>

View File

@@ -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>

View File

@@ -18,7 +18,7 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import "MPTypeViewController.h" #import "MPTypeViewController.h"
#import "MPSiteQuestionEntity.h" #import "MPSiteQuestionEntity+CoreDataClass.h"
@interface MPAnswersViewController : UIViewController @interface MPAnswersViewController : UIViewController

View File

@@ -80,7 +80,7 @@
- (void)setSite:(MPSiteEntity *)site { - (void)setSite:(MPSiteEntity *)site {
_siteOID = [site objectID]; _siteOID = site.permanentObjectID;
_multiple = [site.questions count] > 0; _multiple = [site.questions count] > 0;
[self.tableView reloadData]; [self.tableView reloadData];
[self updateAnimated:NO]; [self updateAnimated:NO];
@@ -301,8 +301,8 @@
- (void)setQuestion:(MPSiteQuestionEntity *)question forSite:(MPSiteEntity *)site inVC:(MPAnswersViewController *)answersVC { - (void)setQuestion:(MPSiteQuestionEntity *)question forSite:(MPSiteEntity *)site inVC:(MPAnswersViewController *)answersVC {
_siteOID = site.objectID; _siteOID = site.permanentObjectID;
_questionOID = question.objectID; _questionOID = question.permanentObjectID;
_answersVC = answersVC; _answersVC = answersVC;
[self updateAnswerForQuestion:question ofSite:site]; [self updateAnswerForQuestion:question ofSite:site];
@@ -333,14 +333,7 @@
question.keyword = keyword; question.keyword = keyword;
if ([context saveToStore]) { if ([context saveToStore]) {
if ([question.objectID isTemporaryID]) { _questionOID = question.permanentObjectID;
NSError *error = nil;
[context obtainPermanentIDsForObjects:@[ question ] error:&error];
if (error)
err( @"Failed to obtain permanent object ID: %@", [error fullDescription] );
}
_questionOID = question.objectID;
[self updateAnswerForQuestion:question ofSite:site]; [self updateAnswerForQuestion:question ofSite:site];
if (didAddQuestionObject) if (didAddQuestionObject)

View File

@@ -171,7 +171,7 @@
- (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated { - (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated {
_siteOID = [site objectID]; _siteOID = site.permanentObjectID;
[self updateAnimated:animated]; [self updateAnimated:animated];
} }
@@ -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 { - (IBAction)doIncrementCounter:(UIButton *)sender {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
@@ -506,6 +531,7 @@
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]; MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
// UI // UI
self.backgroundColor = mainSite.url? [UIColor greenColor]: [UIColor redColor];
self.upgradeButton.gone = !mainSite.requiresExplicitMigration && ![[MPiOSConfig get].allowDowngrade boolValue]; self.upgradeButton.gone = !mainSite.requiresExplicitMigration && ![[MPiOSConfig get].allowDowngrade boolValue];
self.answersButton.gone = ![[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateAnswers]; self.answersButton.gone = ![[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateAnswers];
BOOL settingsMode = self.mode == MPPasswordCellModeSettings; BOOL settingsMode = self.mode == MPPasswordCellModeSettings;

View File

@@ -161,6 +161,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)collectionViewLayout; UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)collectionViewLayout;
UIEdgeInsets occludedInsets = [self.passwordCollectionView occludedInsets]; UIEdgeInsets occludedInsets = [self.passwordCollectionView occludedInsets];
UIEdgeInsets insets = layout.sectionInset; UIEdgeInsets insets = layout.sectionInset;
insets.top = insets.bottom; // Undo storyboard hack for manual top-occluded insets.
if (section == 0) if (section == 0)
insets.top += occludedInsets.top; insets.top += occludedInsets.top;
@@ -321,6 +322,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
PearlRemoveNotificationObservers(); PearlRemoveNotificationObservers();
PearlAddNotificationObserver( UIApplicationWillResignActiveNotification, nil, [NSOperationQueue mainQueue], PearlAddNotificationObserver( UIApplicationWillResignActiveNotification, nil, [NSOperationQueue mainQueue],
^(MPPasswordsViewController *self, NSNotification *note) { ^(MPPasswordsViewController *self, NSNotification *note) {
[self.view endEditing:YES];
self.passwordSelectionContainer.visible = NO; self.passwordSelectionContainer.visible = NO;
} ); } );
PearlAddNotificationObserver( UIApplicationDidBecomeActiveNotification, nil, [NSOperationQueue mainQueue], PearlAddNotificationObserver( UIApplicationDidBecomeActiveNotification, nil, [NSOperationQueue mainQueue],
@@ -399,7 +401,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
self.fetchedResultsController.fetchRequest.predicate = self.fetchedResultsController.fetchRequest.predicate =
[NSPredicate predicateWithFormat:@"name LIKE[cd] %@ AND user == %@", queryPattern, [MPiOSAppDelegate get].activeUserOID]; [NSPredicate predicateWithFormat:@"name LIKE[cd] %@ AND user == %@", queryPattern, [MPiOSAppDelegate get].activeUserOID];
if (![self.fetchedResultsController performFetch:&error]) if (![self.fetchedResultsController performFetch:&error])
err( @"Couldn't fetch sites: %@", [error fullDescription] ); MPError( error, @"Couldn't fetch sites." );
PearlMainQueue( ^{ PearlMainQueue( ^{
[self.passwordCollectionView updateDataSource:_passwordCollectionSections [self.passwordCollectionView updateDataSource:_passwordCollectionSections

View File

@@ -38,6 +38,7 @@
metrics:nil views:NSDictionaryOfVariableBindings( popdownView )]; metrics:nil views:NSDictionaryOfVariableBindings( popdownView )];
[passwordsVC.popdownToTopConstraint layoutIfNeeded]; [passwordsVC.popdownToTopConstraint layoutIfNeeded];
[passwordsVC.view endEditing:YES];
[UIView animateWithDuration:0.6f delay:0 usingSpringWithDamping:0.75f initialSpringVelocity:1 [UIView animateWithDuration:0.6f delay:0 usingSpringWithDamping:0.75f initialSpringVelocity:1
options:UIViewAnimationOptionCurveEaseOut animations:^{ options:UIViewAnimationOptionCurveEaseOut animations:^{

View File

@@ -207,13 +207,13 @@
- (IBAction)homePageButton:(id)sender { - (IBAction)homePageButton:(id)sender {
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender: [[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender:
[NSURL URLWithString:@"http://masterpasswordapp.com"]]; [NSURL URLWithString:@"https://ssl.masterpasswordapp.com"]];
} }
- (IBAction)securityButton:(id)sender { - (IBAction)securityButton:(id)sender {
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" 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 { - (IBAction)sourceButton:(id)sender {
@@ -225,7 +225,7 @@
- (IBAction)thanksButton:(id)sender { - (IBAction)thanksButton:(id)sender {
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender: [[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender:
[NSURL URLWithString:@"http://thanks.lhunath.com"]]; [NSURL URLWithString:@"https://thanks.lhunath.com"]];
} }
#pragma mark - Private #pragma mark - Private

View File

@@ -17,20 +17,11 @@
//============================================================================== //==============================================================================
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <StoreKit/StoreKit.h>
@class MPStoreProductCell; @class MPStoreProductCell;
@interface MPStoreViewController : PearlMutableStaticTableViewController @interface MPStoreViewController : UITableViewController
@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;
+ (NSString *)latestStoreFeatures; + (NSString *)latestStoreFeatures;
@@ -39,7 +30,23 @@
@interface MPStoreProductCell : UITableViewCell @interface MPStoreProductCell : UITableViewCell
@property(nonatomic) IBOutlet UILabel *priceLabel; @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 UIActivityIndicatorView *activityIndicator;
@property(nonatomic) IBOutlet UIView *purchasedIndicator; @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 @end

View File

@@ -27,8 +27,9 @@ PearlEnum( MPDevelopmentFuelConsumption,
@interface MPStoreViewController()<MPInAppDelegate> @interface MPStoreViewController()<MPInAppDelegate>
@property(nonatomic, strong) NSNumberFormatter *currencyFormatter; @property(nonatomic, strong) NSDictionary<NSString *, SKProduct *> *products;
@property(nonatomic, strong) NSArray *products; @property(nonatomic, strong) NSDictionary<NSString *, SKPaymentTransaction *> *transactions;
@property(nonatomic, strong) NSMutableArray<NSArray *> *dataSource;
@end @end
@@ -43,7 +44,7 @@ PearlEnum( MPDevelopmentFuelConsumption,
]; ];
NSInteger storeVersion = [[NSUserDefaults standardUserDefaults] integerForKey:@"storeVersion"]; NSInteger storeVersion = [[NSUserDefaults standardUserDefaults] integerForKey:@"storeVersion"];
for (; storeVersion < [storeVersions count]; ++storeVersion) for (; storeVersion < [storeVersions count]; ++storeVersion)
[features appendFormat:@"%@\n", storeVersions[storeVersion]]; [features appendFormat:@"%@\n", storeVersions[(NSUInteger)storeVersion]];
if (![features length]) if (![features length])
return nil; return nil;
@@ -57,14 +58,13 @@ PearlEnum( MPDevelopmentFuelConsumption,
[super viewDidLoad]; [super viewDidLoad];
self.currencyFormatter = [NSNumberFormatter new];
self.currencyFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
self.tableView.tableHeaderView = [UIView new]; self.tableView.tableHeaderView = [UIView new];
self.tableView.tableFooterView = [UIView new]; self.tableView.tableFooterView = [UIView new];
self.tableView.rowHeight = UITableViewAutomaticDimension; self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 400; self.tableView.estimatedRowHeight = 400;
self.view.backgroundColor = [UIColor clearColor]; self.view.backgroundColor = [UIColor clearColor];
self.dataSource = [@[ @[], @[ @"MPStoreCellSpinner", @"MPStoreCellFooter" ] ] mutableCopy];
} }
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
@@ -73,50 +73,44 @@ PearlEnum( MPDevelopmentFuelConsumption,
self.tableView.contentInset = UIEdgeInsetsMake( 64, 0, 49, 0 ); self.tableView.contentInset = UIEdgeInsetsMake( 64, 0, 49, 0 );
[self updateCellsHiding:self.allCellsBySection[0] showing:@[ self.loadingCell ]];
[self.allCellsBySection[0] enumerateObjectsUsingBlock:^(MPStoreProductCell *cell, NSUInteger idx, BOOL *stop) {
if ([cell isKindOfClass:[MPStoreProductCell class]]) {
cell.purchasedIndicator.visible = NO;
[cell.activityIndicator stopAnimating];
}
}];
PearlAddNotificationObserver( NSUserDefaultsDidChangeNotification, nil, [NSOperationQueue mainQueue],
^(MPStoreViewController *self, NSNotification *note) {
[self updateProducts];
[self updateFuel];
} );
[[MPiOSAppDelegate get] registerProductsObserver:self]; [[MPiOSAppDelegate get] registerProductsObserver:self];
[self updateFuel];
} }
- (void)viewWillDisappear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated]; [super viewWillDisappear:animated];
PearlRemoveNotificationObservers(); [[MPiOSAppDelegate get] removeProductsObserver:self];
} }
#pragma mark - UITableViewDataSource #pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [self.dataSource count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.dataSource[(NSUInteger)section] count];
}
- (MPStoreProductCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - (MPStoreProductCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MPStoreProductCell *cell = (MPStoreProductCell *)[super tableView:tableView cellForRowAtIndexPath:indexPath]; id content = self.dataSource[(NSUInteger)indexPath.section][(NSUInteger)indexPath.row];
if (indexPath.section == 0) if ([content isKindOfClass:[SKProduct class]]) {
cell.selectionStyle = [[MPiOSAppDelegate get] isFeatureUnlocked:[self productForCell:cell].productIdentifier]? SKProduct *product = content;
UITableViewCellSelectionStyleNone: UITableViewCellSelectionStyleDefault; MPStoreProductCell *cell;
if ([product.productIdentifier isEqualToString:MPProductFuel])
if (cell.selectionStyle != UITableViewCellSelectionStyleNone) { cell = [MPStoreFuelProductCell dequeueCellFromTableView:tableView indexPath:indexPath];
cell.selectedBackgroundView = [[UIView alloc] initWithFrame:cell.bounds]; else
cell.selectedBackgroundView.backgroundColor = [UIColor colorWithRGBAHex:0x78DDFB33]; cell = [MPStoreProductCell dequeueCellFromTableView:tableView indexPath:indexPath];
} [cell updateWithProduct:product transaction:self.transactions[product.productIdentifier]];
return cell; return cell;
} }
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { return [tableView dequeueReusableCellWithIdentifier:content forIndexPath:indexPath];
return NO;
} }
#pragma mark - UITableViewDelegate #pragma mark - UITableViewDelegate
@@ -128,14 +122,13 @@ PearlEnum( MPDevelopmentFuelConsumption,
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
MPStoreProductCell *cell = (MPStoreProductCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath]; UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
if (cell.selectionStyle == UITableViewCellSelectionStyleNone) if (cell.selectionStyle != UITableViewCellSelectionStyleNone && [cell isKindOfClass:[MPStoreProductCell class]]) {
return; MPStoreProductCell *productCell = (MPStoreProductCell *)cell;
SKProduct *product = [self productForCell:cell]; if (productCell.product && ![[MPAppDelegate_Shared get] isFeatureUnlocked:productCell.product.productIdentifier])
if (product && ![[MPAppDelegate_Shared get] isFeatureUnlocked:product.productIdentifier]) [[MPAppDelegate_Shared get] purchaseProductWithIdentifier:productCell.product.productIdentifier quantity:productCell.quantity];
[[MPAppDelegate_Shared get] purchaseProductWithIdentifier:product.productIdentifier }
quantity:[self quantityForProductIdentifier:product.productIdentifier]];
[tableView deselectRowAtIndexPath:indexPath animated:YES]; [tableView deselectRowAtIndexPath:indexPath animated:YES];
} }
@@ -146,7 +139,9 @@ PearlEnum( MPDevelopmentFuelConsumption,
NSUInteger fuelConsumption = [[MPiOSConfig get].developmentFuelConsumption unsignedIntegerValue]; NSUInteger fuelConsumption = [[MPiOSConfig get].developmentFuelConsumption unsignedIntegerValue];
[MPiOSConfig get].developmentFuelConsumption = @((fuelConsumption + 1) % MPDevelopmentFuelConsumptionCount); [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 { - (IBAction)restorePurchases:(id)sender {
@@ -165,41 +160,33 @@ PearlEnum( MPDevelopmentFuelConsumption,
- (IBAction)sendThanks:(id)sender { - (IBAction)sendThanks:(id)sender {
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender: [[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender:
[NSURL URLWithString:@"http://thanks.lhunath.com"]]; [NSURL URLWithString:@"https://thanks.lhunath.com"]];
} }
#pragma mark - MPInAppDelegate #pragma mark - MPInAppDelegate
- (void)updateWithProducts:(NSArray *)products { - (void)updateWithProducts:(NSDictionary<NSString *, SKProduct *> *)products
transactions:(NSDictionary<NSString *, SKPaymentTransaction *> *)transactions {
self.products = products; 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]; [self.tableView updateDataSource:self.dataSource toSections:newDataSource
if (!cell) reloadItems:reloadProducts withRowAnimation:UITableViewRowAnimationAutomatic];
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;
}
} }
#pragma mark - Private #pragma mark - Private
@@ -216,60 +203,80 @@ PearlEnum( MPDevelopmentFuelConsumption,
return nil; return nil;
} }
- (SKProduct *)productForCell:(MPStoreProductCell *)cell { @end
for (SKProduct *product in self.products) @implementation MPStoreProductCell
if ([self cellForProductIdentifier:product.productIdentifier] == cell)
return product; - (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; return nil;
} }
- (MPStoreProductCell *)cellForProductIdentifier:(NSString *)productIdentifier { - (NSString *)price {
if ([productIdentifier isEqualToString:MPProductGenerateLogins]) NSNumberFormatter *currencyFormatter = [NSNumberFormatter new];
return self.generateLoginCell; currencyFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
if ([productIdentifier isEqualToString:MPProductGenerateAnswers]) currencyFormatter.locale = self.product.priceLocale;
return self.generateAnswersCell;
if ([productIdentifier isEqualToString:MPProductOSIntegration])
return self.iOSIntegrationCell;
if ([productIdentifier isEqualToString:MPProductTouchID])
return self.touchIDCell;
if ([productIdentifier isEqualToString:MPProductFuel])
return self.fuelCell;
return nil; return [currencyFormatter stringFromNumber:@([self.product.price floatValue] * self.quantity)];
} }
- (void)updateProducts { - (NSInteger)quantity {
NSMutableArray *showCells = [NSMutableArray array]; return 1;
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]; @end
[self updateCellsHiding:hideCells showing:showCells];
}
- (void)updateFuel { @implementation MPStoreFuelProductCell
- (void)updateWithProduct:(SKProduct *)product transaction:(SKPaymentTransaction *)transaction {
[super updateWithProduct:product transaction:transaction];
CGFloat weeklyFuelConsumption = [self weeklyFuelConsumption]; /* consume x fuel / week */ 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 fuelRemaining = [[MPiOSConfig get].developmentFuelRemaining floatValue]; /* x fuel left */
CGFloat fuelInvested = [[MPiOSConfig get].developmentFuelInvested floatValue]; /* x fuel left */ CGFloat fuelInvested = [[MPiOSConfig get].developmentFuelInvested floatValue]; /* x fuel left */
NSDate *now = [NSDate date]; NSDate *now = [NSDate date], *checked = [MPiOSConfig get].developmentFuelChecked;
NSTimeInterval fuelSecondsElapsed = -[[MPiOSConfig get].developmentFuelChecked timeIntervalSinceDate:now]; if (!checked || 3600 < (fuelSecondsElapsed = [now timeIntervalSinceDate:checked])) {
if (fuelSecondsElapsed > 3600 || ![MPiOSConfig get].developmentFuelChecked) {
NSTimeInterval weeksElapsed = fuelSecondsElapsed / (3600 * 24 * 7 /* 1 week */); /* x weeks elapsed */ NSTimeInterval weeksElapsed = fuelSecondsElapsed / (3600 * 24 * 7 /* 1 week */); /* x weeks elapsed */
NSTimeInterval fuelConsumed = weeklyFuelConsumption * weeksElapsed; NSTimeInterval fuelConsumed = MIN( fuelRemaining, weeklyFuelConsumption * weeksElapsed );
fuelRemaining -= fuelConsumed; fuelRemaining -= fuelConsumed;
fuelInvested += fuelConsumed; fuelInvested += fuelConsumed;
[MPiOSConfig get].developmentFuelChecked = now; [MPiOSConfig get].developmentFuelChecked = now;
@@ -277,54 +284,43 @@ PearlEnum( MPDevelopmentFuelConsumption,
[MPiOSConfig get].developmentFuelInvested = @(fuelInvested); [MPiOSConfig get].developmentFuelInvested = @(fuelInvested);
} }
CGFloat fuelRatio = weeklyFuelConsumption == 0? 0: fuelRemaining / weeklyFuelConsumption; /* x weeks worth of fuel left */ 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.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 ); self.fuelStatusLabel.text = strf( @"Fuel left: %0.1f work hours\nFunded: %0.1f work hours", fuelRemaining, fuelInvested );
self.fuelStatusLabel.hidden = (fuelRemaining + fuelInvested) == 0; self.fuelStatusLabel.hidden = (fuelRemaining + fuelInvested) == 0;
} }
- (NSInteger)quantity {
return MAX( 1, (NSInteger)ceil( MP_FUEL_HOURLY_RATE * [self weeklyFuelConsumption] ) );
}
- (CGFloat)weeklyFuelConsumption { - (CGFloat)weeklyFuelConsumption {
switch ((MPDevelopmentFuelConsumption)[[MPiOSConfig get].developmentFuelConsumption unsignedIntegerValue]) { switch ((MPDevelopmentFuelConsumption)[[MPiOSConfig get].developmentFuelConsumption unsignedIntegerValue]) {
case MPDevelopmentFuelConsumptionQuarterly: case MPDevelopmentFuelConsumptionQuarterly:
[self.fuelSpeedButton setTitle:@"1h / quarter" forState:UIControlStateNormal];
return 1.f / 12 /* 12 weeks */; return 1.f / 12 /* 12 weeks */;
case MPDevelopmentFuelConsumptionMonthly: case MPDevelopmentFuelConsumptionMonthly:
[self.fuelSpeedButton setTitle:@"1h / month" forState:UIControlStateNormal];
return 1.f / 4 /* 4 weeks */; return 1.f / 4 /* 4 weeks */;
case MPDevelopmentFuelWeekly: case MPDevelopmentFuelWeekly:
[self.fuelSpeedButton setTitle:@"1h / week" forState:UIControlStateNormal]; return 1.f /* 1 week */;
return 1.f;
} }
return 0; return 0;
} }
- (void)showCellForProductWithIdentifier:(NSString *)productIdentifier ifProduct:(SKProduct *)product - (NSString *)weeklyFuelConsumptionTitle {
showingCells:(NSMutableArray *)showCells {
if (![product.productIdentifier isEqualToString:productIdentifier]) switch ((MPDevelopmentFuelConsumption)[[MPiOSConfig get].developmentFuelConsumption unsignedIntegerValue]) {
return; case MPDevelopmentFuelConsumptionQuarterly:
return @"1h / quarter";
MPStoreProductCell *cell = [self cellForProductIdentifier:productIdentifier]; case MPDevelopmentFuelConsumptionMonthly:
[showCells addObject:cell]; return @"1h / month";
case MPDevelopmentFuelWeekly:
self.currencyFormatter.locale = product.priceLocale; return @"1h / week";
BOOL purchased = [[MPiOSAppDelegate get] isFeatureUnlocked:productIdentifier];
NSInteger quantity = [self quantityForProductIdentifier:productIdentifier];
cell.priceLabel.text = purchased? @"": [self.currencyFormatter stringFromNumber:@([product.price floatValue] * quantity)];
cell.purchasedIndicator.visible = purchased;
} }
- (NSInteger)quantityForProductIdentifier:(NSString *)productIdentifier { return nil;
if ([productIdentifier isEqualToString:MPProductFuel])
return (NSInteger)(MP_FUEL_HOURLY_RATE * [self weeklyFuelConsumption] + .5f);
return 1;
} }
@end @end
@implementation MPStoreProductCell
@end

View File

@@ -16,6 +16,7 @@
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>. // LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//============================================================================== //==============================================================================
#import <Crashlytics/Answers.h>
#import "MPUsersViewController.h" #import "MPUsersViewController.h"
#import "MPEntities.h" #import "MPEntities.h"
#import "MPAvatarCell.h" #import "MPAvatarCell.h"
@@ -118,8 +119,9 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"web"]) if ([segue.identifier isEqualToString:@"thanks"])
((MPWebViewController *)segue.destinationViewController).initialURL = [NSURL URLWithString:@"http://thanks.lhunath.com"]; ((MPWebViewController *)segue.destinationViewController).initialURL =
[NSURL URLWithString:@"https://thanks.lhunath.com"];
} }
- (void)viewWillDisappear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated {
@@ -224,6 +226,17 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
user.defaultType = user.algorithm.defaultType; user.defaultType = user.algorithm.defaultType;
user.avatar = newUserAvatar; user.avatar = newUserAvatar;
user.name = newUserName; 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 BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context
@@ -260,6 +273,9 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
// This isn't really in UITextFieldDelegate. We fake it from UITextFieldTextDidChangeNotification. // This isn't really in UITextFieldDelegate. We fake it from UITextFieldTextDidChangeNotification.
- (void)textFieldEditingChanged:(UITextField *)textField { - (void)textFieldEditingChanged:(UITextField *)textField {
if ([[textField.text lowercaseString] isEqualToString:@"hangtest"])
[NSThread sleepForTimeInterval:10];
if (textField == self.entryField) { if (textField == self.entryField) {
switch (self.activeUserState) { switch (self.activeUserState) {
case MPActiveUserStateNone: case MPActiveUserStateNone:
@@ -369,7 +385,7 @@ referenceSizeForFooterInSection:(NSInteger)section {
MPAvatarCell *userAvatar = [self selectedAvatar]; MPAvatarCell *userAvatar = [self selectedAvatar];
userAvatar.spinnerActive = YES; userAvatar.spinnerActive = YES;
if (!isNew && mainUser && [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { 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]; BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context usingMasterPassword:nil];
PearlMainQueue( ^{ PearlMainQueue( ^{
@@ -412,10 +428,10 @@ referenceSizeForFooterInSection:(NSInteger)section {
BOOL isNew = NO; BOOL isNew = NO;
MPUserEntity *user = [self userForAvatar:avatarCell inContext:mainContext isNew:&isNew]; MPUserEntity *user = [self userForAvatar:avatarCell inContext:mainContext isNew:&isNew];
NSManagedObjectID *userID = user.objectID;
if (isNew || !user) if (isNew || !user)
return; return;
NSManagedObjectID *userID = user.permanentObjectID;
[PearlSheet showSheetWithTitle:user.name [PearlSheet showSheetWithTitle:user.name
viewStyle:UIActionSheetStyleBlackTranslucent viewStyle:UIActionSheetStyleBlackTranslucent
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) { initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
@@ -719,13 +735,13 @@ referenceSizeForFooterInSection:(NSInteger)section {
]; ];
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error]; NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
if (!users) { if (!users) {
err( @"Failed to load users: %@", [error fullDescription] ); MPError( error, @"Failed to load users." );
self.userIDs = nil; self.userIDs = nil;
} }
NSMutableArray *userIDs = [NSMutableArray arrayWithCapacity:[users count]]; NSMutableArray *userIDs = [NSMutableArray arrayWithCapacity:[users count]];
for (MPUserEntity *user in users) for (MPUserEntity *user in users)
[userIDs addObject:user.objectID]; [userIDs addObject:user.permanentObjectID];
self.userIDs = userIDs; self.userIDs = userIDs;
}]) }])
self.userIDs = nil; self.userIDs = nil;
@@ -753,7 +769,7 @@ referenceSizeForFooterInSection:(NSInteger)section {
NSManagedObjectID *selectUserID = [MPiOSAppDelegate get].activeUserOID; NSManagedObjectID *selectUserID = [MPiOSAppDelegate get].activeUserOID;
if (!selectUserID) if (!selectUserID)
selectUserID = [self selectedUserInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady] selectUserID = [self selectedUserInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]
isNew:&isNew].objectID; isNew:&isNew].permanentObjectID;
[self.avatarCollectionView reloadData]; [self.avatarCollectionView reloadData];
NSUInteger selectedAvatarItem = isNew? [_userIDs count]: selectUserID? [_userIDs indexOfObject:selectUserID]: NSNotFound; NSUInteger selectedAvatarItem = isNew? [_userIDs count]: selectUserID? [_userIDs indexOfObject:selectUserID]: NSNotFound;

View File

@@ -32,7 +32,7 @@
[super viewWillAppear:animated]; [super viewWillAppear:animated];
if (!self.initialURL) 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.webNavigationItem.title = self.initialURL.host;

View File

@@ -21,11 +21,12 @@
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import "MPStoreViewController.h" #import "MPStoreViewController.h"
#define MP_HANG_TIME_MAIN 3 // s
@interface MPiOSAppDelegate()<UIDocumentInteractionControllerDelegate> @interface MPiOSAppDelegate()<UIDocumentInteractionControllerDelegate>
@property(nonatomic, strong) UIDocumentInteractionController *interactionController; @property(nonatomic, strong) UIDocumentInteractionController *interactionController;
@property(nonatomic) UIBackgroundTaskIdentifier task;
@end @end
@implementation MPiOSAppDelegate @implementation MPiOSAppDelegate
@@ -59,10 +60,9 @@
[[Crashlytics sharedInstance] setUserIdentifier:[PearlKeyChain deviceIdentifier]]; [[Crashlytics sharedInstance] setUserIdentifier:[PearlKeyChain deviceIdentifier]];
[[Crashlytics sharedInstance] setObjectValue:[PearlKeyChain deviceIdentifier] forKey:@"deviceIdentifier"]; [[Crashlytics sharedInstance] setObjectValue:[PearlKeyChain deviceIdentifier] forKey:@"deviceIdentifier"];
[[Crashlytics sharedInstance] setUserName:@"Anonymous"]; [[Crashlytics sharedInstance] setUserName:@"Anonymous"];
[[Crashlytics sharedInstance] setObjectValue:@"Anonymous" forKey:@"username"];
[Crashlytics startWithAPIKey:crashlyticsAPIKey]; [Crashlytics startWithAPIKey:crashlyticsAPIKey];
[[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) { [[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
PearlLogLevel level = PearlLogLevelInfo; PearlLogLevel level = PearlLogLevelWarn;
if ([[MPConfig get].sendInfo boolValue]) if ([[MPConfig get].sendInfo boolValue])
level = PearlLogLevelDebug; level = PearlLogLevelDebug;
@@ -75,6 +75,8 @@
[Crashlytics sharedInstance].version, [PearlInfoPlist get].CFBundleName, [PearlInfoPlist get].CFBundleVersion ); [Crashlytics sharedInstance].version, [PearlInfoPlist get].CFBundleName, [PearlInfoPlist get].CFBundleVersion );
} }
#endif #endif
[self installHangDetector];
} }
@catch (id exception) { @catch (id exception) {
err( @"During Analytics Setup: %@", exception ); err( @"During Analytics Setup: %@", exception );
@@ -83,9 +85,6 @@
PearlAddNotificationObserver( MPCheckConfigNotification, nil, [NSOperationQueue mainQueue], ^(id self, NSNotification *note) { PearlAddNotificationObserver( MPCheckConfigNotification, nil, [NSOperationQueue mainQueue], ^(id self, NSNotification *note) {
[self updateConfigKey:note.object]; [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) { PearlAddNotificationObserver( NSUserDefaultsDidChangeNotification, nil, nil, ^(id self, NSNotification *note) {
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil]; [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
} ); } );
@@ -144,6 +143,31 @@
return YES; 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 - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
@@ -155,7 +179,8 @@
[[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler: [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:
^(NSData *importedSitesData, NSURLResponse *response, NSError *error) { ^(NSData *importedSitesData, NSURLResponse *response, NSError *error) {
if (error) if (error)
err( @"While reading imported sites from %@: %@", url, [error fullDescription] ); MPError( error, @"While reading imported sites from %@.", url );
if (!importedSitesData) { if (!importedSitesData) {
[PearlAlert showError:strf( @"Master Password couldn't read the import sites.\n\n%@", [PearlAlert showError:strf( @"Master Password couldn't read the import sites.\n\n%@",
[error localizedDescription]?: error )]; [error localizedDescription]?: error )];
@@ -483,7 +508,7 @@
NSError *error = nil; NSError *error = nil;
if (![[exportedSites dataUsingEncoding:NSUTF8StringEncoding] if (![[exportedSites dataUsingEncoding:NSUTF8StringEncoding]
writeToURL:exportURL options:NSDataWritingFileProtectionComplete error:&error]) 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 { else {
self.interactionController = [UIDocumentInteractionController interactionControllerWithURL:exportURL]; self.interactionController = [UIDocumentInteractionController interactionControllerWithURL:exportURL];
self.interactionController.UTI = @"com.lyndir.masterpassword.sites"; self.interactionController.UTI = @"com.lyndir.masterpassword.sites";
@@ -574,7 +599,7 @@
static NSDictionary *crashlyticsInfo = nil; static NSDictionary *crashlyticsInfo = nil;
if (crashlyticsInfo == nil) if (crashlyticsInfo == nil)
crashlyticsInfo = [[NSDictionary alloc] initWithContentsOfURL: crashlyticsInfo = [[NSDictionary alloc] initWithContentsOfURL:
[[NSBundle mainBundle] URLForResource:@"Crashlytics" withExtension:@"plist"]]; [[NSBundle mainBundle] URLForResource:@"Fabric" withExtension:@"plist"]];
return crashlyticsInfo; return crashlyticsInfo;
} }

View File

@@ -60,7 +60,7 @@
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>© 2011-2016 Lyndir</string> <string>© 2011-2017</string>
<key>UIAppFonts</key> <key>UIAppFonts</key>
<array> <array>
<string>Exo2.0-Bold.otf</string> <string>Exo2.0-Bold.otf</string>

View File

@@ -6,7 +6,7 @@
<array> <array>
<dict> <dict>
<key>FooterText</key> <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> <key>Title</key>
<string></string> <string></string>
<key>Type</key> <key>Type</key>
@@ -50,7 +50,7 @@
<key>Key</key> <key>Key</key>
<string>sendInfo</string> <string>sendInfo</string>
<key>DefaultValue</key> <key>DefaultValue</key>
<false/> <true/>
</dict> </dict>
<dict> <dict>
<key>FooterText</key> <key>FooterText</key>

View File

@@ -292,7 +292,7 @@
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state> </state>
<connections> <connections>
<segue destination="Sd5-eW-Cx2" kind="modal" identifier="web" id="gtb-zE-u9H"/> <segue destination="Sd5-eW-Cx2" kind="modal" identifier="thanks" id="gtb-zE-u9H"/>
</connections> </connections>
</button> </button>
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="069-Pu-yXe" userLabel="Thanks Tip"> <view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="069-Pu-yXe" userLabel="Thanks Tip">
@@ -526,7 +526,7 @@
<objects> <objects>
<navigationController definesPresentationContext="YES" navigationBarHidden="YES" id="Q1S-vU-GGO" customClass="MPNavigationController" sceneMemberID="viewController"> <navigationController definesPresentationContext="YES" navigationBarHidden="YES" id="Q1S-vU-GGO" customClass="MPNavigationController" sceneMemberID="viewController">
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/> <simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
<navigationBar key="navigationBar" contentMode="scaleToFill" misplaced="YES" id="4yl-zs-iUd"> <navigationBar key="navigationBar" contentMode="scaleToFill" id="4yl-zs-iUd">
<rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/> <rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
</navigationBar> </navigationBar>
@@ -708,13 +708,13 @@
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="249" text="The password type to use to when creating a new site." lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mzg-1u-aEg"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="249" text="The password type to use to when creating a new site." lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mzg-1u-aEg">
<rect key="frame" x="20" y="48" width="335" height="50.5"/> <rect key="frame" x="20" y="48" width="335" height="51"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<segmentedControl opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bar" translatesAutoresizingMaskIntoConstraints="NO" id="E0r-Ey-eVH" userLabel="Type"> <segmentedControl opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bar" translatesAutoresizingMaskIntoConstraints="NO" id="E0r-Ey-eVH" userLabel="Type">
<rect key="frame" x="131" y="106.5" width="113" height="29"/> <rect key="frame" x="131" y="107" width="113" height="29"/>
<segments> <segments>
<segment title="Phrase"/> <segment title="Phrase"/>
<segment title="Name"/> <segment title="Name"/>
@@ -725,7 +725,7 @@
</connections> </connections>
</segmentedControl> </segmentedControl>
<segmentedControl opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bar" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="H8F-E0-dqF" userLabel="Type"> <segmentedControl opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bar" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="H8F-E0-dqF" userLabel="Type">
<rect key="frame" x="41" y="142.5" width="293" height="29"/> <rect key="frame" x="41" y="143" width="293" height="29"/>
<segments> <segments>
<segment title="Max"/> <segment title="Max"/>
<segment title="Long"/> <segment title="Long"/>
@@ -740,7 +740,7 @@
</connections> </connections>
</segmentedControl> </segmentedControl>
<segmentedControl opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bar" translatesAutoresizingMaskIntoConstraints="NO" id="Rei-aO-UBD" userLabel="Type"> <segmentedControl opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bar" translatesAutoresizingMaskIntoConstraints="NO" id="Rei-aO-UBD" userLabel="Type">
<rect key="frame" x="86" y="178.5" width="203" height="29"/> <rect key="frame" x="86" y="179" width="203" height="29"/>
<segments> <segments>
<segment title="Personal"/> <segment title="Personal"/>
<segment title="Device Private"/> <segment title="Device Private"/>
@@ -751,7 +751,7 @@
</connections> </connections>
</segmentedControl> </segmentedControl>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="eg. LarwPopm4@Zewt" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lhS-L1-2OB"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="eg. LarwPopm4@Zewt" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lhS-L1-2OB">
<rect key="frame" x="20" y="214.5" width="335" height="14.5"/> <rect key="frame" x="20" y="215" width="335" height="14"/>
<fontDescription key="fontDescription" name="Exo2.0-Thin" family="Exo 2.0" pointSize="12"/> <fontDescription key="fontDescription" name="Exo2.0-Thin" family="Exo 2.0" pointSize="12"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@@ -1039,7 +1039,7 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="249.5"/> <rect key="frame" x="0.0" y="0.0" width="375" height="249.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="999" verticalCompressionResistancePriority="1000" text="© 2012-2014, Maarten Billemont (lhunath)" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sPw-mV-mFF"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="999" verticalCompressionResistancePriority="1000" text="© 2011-2017, Maarten Billemont (lhunath)" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sPw-mV-mFF">
<rect key="frame" x="20" y="4" width="335" height="105"/> <rect key="frame" x="20" y="4" width="335" height="105"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@@ -1158,16 +1158,15 @@
<collectionView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" minimumZoomScale="0.0" maximumZoomScale="0.0" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="aXw-tn-8Sj" userLabel="Password Collection"> <collectionView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" minimumZoomScale="0.0" maximumZoomScale="0.0" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="aXw-tn-8Sj" userLabel="Password Collection">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<inset key="scrollIndicatorInsets" minX="0.0" minY="108" maxX="0.0" maxY="0.0"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="Mv1-29-TWx"> <collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="Mv1-29-TWx">
<size key="itemSize" width="355" height="100"/> <size key="itemSize" width="355" height="100"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/> <size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/> <size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="10" minY="10" maxX="10" maxY="10"/> <inset key="sectionInset" minX="10" minY="200" maxX="10" maxY="10"/>
</collectionViewFlowLayout> </collectionViewFlowLayout>
<cells> <cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPPasswordCell" id="W2g-yv-V3V" customClass="MPPasswordCell"> <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPPasswordCell" id="W2g-yv-V3V" customClass="MPPasswordCell">
<rect key="frame" x="10" y="10" width="355" height="100"/> <rect key="frame" x="10" y="200" width="355" height="100"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center"> <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="355" height="100"/> <rect key="frame" x="0.0" y="0.0" width="355" height="100"/>
@@ -1210,7 +1209,7 @@
</connections> </connections>
</button> </button>
<label opaque="NO" userInteractionEnabled="NO" alpha="0.20000000000000001" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="⚙" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="z3Z-Zc-1EC"> <label opaque="NO" userInteractionEnabled="NO" alpha="0.20000000000000001" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="⚙" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="z3Z-Zc-1EC">
<rect key="frame" x="332" y="5" width="15" height="13"/> <rect key="frame" x="332" y="4.5" width="15" height="13.5"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@@ -1247,12 +1246,12 @@
</constraints> </constraints>
</view> </view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="2tX-WK-ASq" userLabel="Password Container"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="2tX-WK-ASq" userLabel="Password Container">
<rect key="frame" x="0.0" y="20" width="355" height="43"/> <rect key="frame" x="0.0" y="19" width="355" height="45"/>
<subviews> <subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="CuzaSasy3*Rimo" textAlignment="center" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="blw-Ou-8I8" userLabel="Password"> <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="CuzaSasy3*Rimo" textAlignment="center" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="blw-Ou-8I8" userLabel="Password">
<rect key="frame" x="8" y="0.0" width="339" height="31"/> <rect key="frame" x="8" y="0.0" width="339" height="33"/>
<color key="textColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" name="SourceCodePro-Black" family="Source Code Pro" pointSize="24"/> <fontDescription key="fontDescription" name="SourceCodePro-Black" family="Source Code Pro" pointSize="26"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="alphabet" keyboardAppearance="alert" returnKeyType="next"/> <textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="alphabet" keyboardAppearance="alert" returnKeyType="next"/>
<connections> <connections>
<action selector="textFieldDidChange:" destination="W2g-yv-V3V" eventType="editingChanged" id="gTE-0d-r2l"/> <action selector="textFieldDidChange:" destination="W2g-yv-V3V" eventType="editingChanged" id="gTE-0d-r2l"/>
@@ -1260,7 +1259,7 @@
</connections> </connections>
</textField> </textField>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="3" contentMode="left" text="&gt; age of the universe" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="wfM-xz-roR" userLabel="Strength"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="3" contentMode="left" text="&gt; age of the universe" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="wfM-xz-roR" userLabel="Strength">
<rect key="frame" x="0.0" y="31" width="355" height="12"/> <rect key="frame" x="0.0" y="33" width="355" height="12"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="10"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="10"/>
<color key="textColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@@ -1308,9 +1307,12 @@
</view> </view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LvK-28-fbm" userLabel="Settings Container"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LvK-28-fbm" userLabel="Settings Container">
<rect key="frame" x="355" y="0.0" width="355" height="100"/> <rect key="frame" x="355" y="0.0" width="355" height="100"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="kfn-Wq-g5M">
<rect key="frame" x="-4" y="56" width="315" height="44"/>
<subviews> <subviews>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IKd-Ot-0n4" userLabel="Upgrade"> <button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IKd-Ot-0n4" userLabel="Upgrade">
<rect key="frame" x="40" y="56" width="44" height="44"/> <rect key="frame" x="0.0" y="0.0" width="44" height="44"/>
<accessibility key="accessibilityConfiguration" hint="Upgrades the password."/> <accessibility key="accessibilityConfiguration" hint="Upgrades the password."/>
<gestureRecognizers/> <gestureRecognizers/>
<constraints> <constraints>
@@ -1330,8 +1332,29 @@
<action selector="doUpgrade:" destination="W2g-yv-V3V" eventType="touchUpInside" id="kTZ-AM-qGa"/> <action selector="doUpgrade:" destination="W2g-yv-V3V" eventType="touchUpInside" id="kTZ-AM-qGa"/>
</connections> </connections>
</button> </button>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yPG-30-POQ" userLabel="Action">
<rect key="frame" x="44" y="0.0" width="44" height="44"/>
<accessibility key="accessibilityConfiguration" hint="Upgrades the password."/>
<gestureRecognizers/>
<constraints>
<constraint firstAttribute="width" constant="44" id="Ruj-lV-Vr6"/>
<constraint firstAttribute="height" constant="44" id="fkk-9t-ufv"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<inset key="contentEdgeInsets" minX="6" minY="6" maxX="6" maxY="6"/>
<state key="normal" image="icon_action.png">
<color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<state key="highlighted">
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="doAction:" destination="W2g-yv-V3V" eventType="touchUpInside" id="pGL-dJ-lDW"/>
</connections>
</button>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vGk-t6-hZn" userLabel="Answers"> <button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vGk-t6-hZn" userLabel="Answers">
<rect key="frame" x="84" y="56" width="44" height="44"/> <rect key="frame" x="88" y="0.0" width="44" height="44"/>
<accessibility key="accessibilityConfiguration" hint="Upgrades the password."/> <accessibility key="accessibilityConfiguration" hint="Upgrades the password."/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="44" id="8gK-8v-Q0K"/> <constraint firstAttribute="width" constant="44" id="8gK-8v-Q0K"/>
@@ -1351,7 +1374,7 @@
</connections> </connections>
</button> </button>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="1" textAlignment="right" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="PKP-M9-T8E" userLabel="Counter"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.5" contentMode="left" text="1" textAlignment="right" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="PKP-M9-T8E" userLabel="Counter">
<rect key="frame" x="128" y="67.5" width="7" height="21"/> <rect key="frame" x="132" y="11.5" width="7" height="21.5"/>
<accessibility key="accessibilityConfiguration" hint="Site's counter."/> <accessibility key="accessibilityConfiguration" hint="Site's counter."/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@@ -1360,7 +1383,7 @@
<size key="shadowOffset" width="0.0" height="1"/> <size key="shadowOffset" width="0.0" height="1"/>
</label> </label>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uZi-FT-Fe8" userLabel="Incrementer"> <button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uZi-FT-Fe8" userLabel="Incrementer">
<rect key="frame" x="135" y="56" width="44" height="44"/> <rect key="frame" x="139" y="0.0" width="44" height="44"/>
<accessibility key="accessibilityConfiguration" hint="Increments the site counter."/> <accessibility key="accessibilityConfiguration" hint="Increments the site counter."/>
<gestureRecognizers/> <gestureRecognizers/>
<constraints> <constraints>
@@ -1381,7 +1404,7 @@
</connections> </connections>
</button> </button>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qBo-Kw-vN9" userLabel="Edit"> <button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qBo-Kw-vN9" userLabel="Edit">
<rect key="frame" x="179" y="56" width="44" height="44"/> <rect key="frame" x="183" y="0.0" width="44" height="44"/>
<accessibility key="accessibilityConfiguration" hint="Edits the password."/> <accessibility key="accessibilityConfiguration" hint="Edits the password."/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="44" id="j1z-K8-OXJ"/> <constraint firstAttribute="width" constant="44" id="j1z-K8-OXJ"/>
@@ -1401,7 +1424,7 @@
</connections> </connections>
</button> </button>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="I0v-ou-hDb" userLabel="Delete"> <button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="I0v-ou-hDb" userLabel="Delete">
<rect key="frame" x="223" y="56" width="44" height="44"/> <rect key="frame" x="227" y="0.0" width="44" height="44"/>
<accessibility key="accessibilityConfiguration" hint="Edits the password."/> <accessibility key="accessibilityConfiguration" hint="Edits the password."/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="44" id="XvV-Lr-Z21"/> <constraint firstAttribute="height" constant="44" id="XvV-Lr-Z21"/>
@@ -1421,7 +1444,7 @@
</connections> </connections>
</button> </button>
<button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="K8q-bM-tzh" userLabel="Type"> <button opaque="NO" alpha="0.5" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="K8q-bM-tzh" userLabel="Type">
<rect key="frame" x="267" y="56" width="44" height="44"/> <rect key="frame" x="271" y="0.0" width="44" height="44"/>
<accessibility key="accessibilityConfiguration" hint="Edits the password."/> <accessibility key="accessibilityConfiguration" hint="Edits the password."/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="44" id="K3c-Ok-krB"/> <constraint firstAttribute="height" constant="44" id="K3c-Ok-krB"/>
@@ -1441,17 +1464,12 @@
</connections> </connections>
</button> </button>
</subviews> </subviews>
</stackView>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="PKP-M9-T8E" firstAttribute="leading" secondItem="IKd-Ot-0n4" secondAttribute="trailing" priority="750" id="3sN-hc-iUd"/> <constraint firstAttribute="trailing" secondItem="kfn-Wq-g5M" secondAttribute="trailing" constant="44" id="lGN-Bt-mrg"/>
<constraint firstItem="PKP-M9-T8E" firstAttribute="leading" secondItem="vGk-t6-hZn" secondAttribute="trailing" id="47y-gh-Ibs"/> <constraint firstAttribute="bottom" secondItem="kfn-Wq-g5M" secondAttribute="bottom" id="lcH-Rb-skY"/>
<constraint firstItem="PKP-M9-T8E" firstAttribute="centerY" secondItem="uZi-FT-Fe8" secondAttribute="centerY" id="KEK-0r-cob"/>
<constraint firstItem="qBo-Kw-vN9" firstAttribute="leading" secondItem="uZi-FT-Fe8" secondAttribute="trailing" id="LLk-5B-1g9"/>
<constraint firstItem="vGk-t6-hZn" firstAttribute="leading" secondItem="IKd-Ot-0n4" secondAttribute="trailing" id="e2D-Rg-OQz"/>
<constraint firstItem="K8q-bM-tzh" firstAttribute="leading" secondItem="I0v-ou-hDb" secondAttribute="trailing" id="il2-7H-Hgk"/>
<constraint firstItem="I0v-ou-hDb" firstAttribute="leading" secondItem="qBo-Kw-vN9" secondAttribute="trailing" id="myx-31-6tz"/>
<constraint firstAttribute="trailing" secondItem="K8q-bM-tzh" secondAttribute="trailing" constant="44" id="qQK-ts-seh"/>
<constraint firstItem="uZi-FT-Fe8" firstAttribute="leading" secondItem="PKP-M9-T8E" secondAttribute="trailing" id="xFJ-GZ-jp9"/>
</constraints> </constraints>
<userDefinedRuntimeAttributes> <userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="ignoreTouches" value="YES"/> <userDefinedRuntimeAttribute type="boolean" keyPath="ignoreTouches" value="YES"/>
@@ -1479,7 +1497,7 @@
</connections> </connections>
</button> </button>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="⬇︎" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tuh-Au-J9k"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="⬇︎" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tuh-Au-J9k">
<rect key="frame" x="325" y="36" width="17" height="20"/> <rect key="frame" x="325" y="35.5" width="17" height="20.5"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@@ -1488,20 +1506,14 @@
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="bottom" secondItem="tl3-hd-x35" secondAttribute="bottom" id="2x2-iA-utK"/> <constraint firstAttribute="bottom" secondItem="tl3-hd-x35" secondAttribute="bottom" id="2x2-iA-utK"/>
<constraint firstItem="b5f-wN-2xb" firstAttribute="centerY" secondItem="I0v-ou-hDb" secondAttribute="centerY" id="KPv-e0-uR0"/>
<constraint firstItem="LvK-28-fbm" firstAttribute="leading" secondItem="tl3-hd-x35" secondAttribute="trailing" id="NXL-Fd-whf"/> <constraint firstItem="LvK-28-fbm" firstAttribute="leading" secondItem="tl3-hd-x35" secondAttribute="trailing" id="NXL-Fd-whf"/>
<constraint firstItem="b5f-wN-2xb" firstAttribute="top" secondItem="tuh-Au-J9k" secondAttribute="bottom" id="SBC-kJ-Gku"/> <constraint firstItem="b5f-wN-2xb" firstAttribute="top" secondItem="tuh-Au-J9k" secondAttribute="bottom" id="SBC-kJ-Gku"/>
<constraint firstAttribute="bottom" secondItem="LvK-28-fbm" secondAttribute="bottom" id="ZBx-Lf-XHF"/> <constraint firstAttribute="bottom" secondItem="LvK-28-fbm" secondAttribute="bottom" id="ZBx-Lf-XHF"/>
<constraint firstItem="b5f-wN-2xb" firstAttribute="centerY" secondItem="vGk-t6-hZn" secondAttribute="centerY" id="fGB-8g-u5b"/>
<constraint firstItem="tl3-hd-x35" firstAttribute="leading" secondItem="bff-RU-OcY" secondAttribute="leading" id="fx5-KQ-LSM"/> <constraint firstItem="tl3-hd-x35" firstAttribute="leading" secondItem="bff-RU-OcY" secondAttribute="leading" id="fx5-KQ-LSM"/>
<constraint firstItem="tl3-hd-x35" firstAttribute="top" secondItem="bff-RU-OcY" secondAttribute="top" id="jc9-39-FY3"/> <constraint firstItem="tl3-hd-x35" firstAttribute="top" secondItem="bff-RU-OcY" secondAttribute="top" id="jc9-39-FY3"/>
<constraint firstItem="b5f-wN-2xb" firstAttribute="centerY" secondItem="IKd-Ot-0n4" secondAttribute="centerY" id="mCJ-ug-mmv"/>
<constraint firstItem="K8q-bM-tzh" firstAttribute="centerY" secondItem="b5f-wN-2xb" secondAttribute="centerY" id="ofO-r3-bjz"/>
<constraint firstItem="b5f-wN-2xb" firstAttribute="centerX" secondItem="tuh-Au-J9k" secondAttribute="centerX" id="pYG-bz-kd8"/> <constraint firstItem="b5f-wN-2xb" firstAttribute="centerX" secondItem="tuh-Au-J9k" secondAttribute="centerX" id="pYG-bz-kd8"/>
<constraint firstAttribute="trailing" secondItem="LvK-28-fbm" secondAttribute="trailing" id="r9d-ym-Frs"/> <constraint firstAttribute="trailing" secondItem="LvK-28-fbm" secondAttribute="trailing" id="r9d-ym-Frs"/>
<constraint firstItem="LvK-28-fbm" firstAttribute="top" secondItem="bff-RU-OcY" secondAttribute="top" id="uqb-hB-Iq8"/> <constraint firstItem="LvK-28-fbm" firstAttribute="top" secondItem="bff-RU-OcY" secondAttribute="top" id="uqb-hB-Iq8"/>
<constraint firstItem="b5f-wN-2xb" firstAttribute="centerY" secondItem="uZi-FT-Fe8" secondAttribute="centerY" id="xz2-kK-B4g"/>
<constraint firstItem="b5f-wN-2xb" firstAttribute="centerY" secondItem="qBo-Kw-vN9" secondAttribute="centerY" id="zaR-iF-Hea"/>
</constraints> </constraints>
<connections> <connections>
<outlet property="delegate" destination="W2g-yv-V3V" id="HgX-fq-za5"/> <outlet property="delegate" destination="W2g-yv-V3V" id="HgX-fq-za5"/>
@@ -1576,6 +1588,7 @@
<constraint firstItem="aXw-tn-8Sj" firstAttribute="top" secondItem="K2e-Gh-7hH" secondAttribute="top" id="0SC-ZK-6ib"/> <constraint firstItem="aXw-tn-8Sj" firstAttribute="top" secondItem="K2e-Gh-7hH" secondAttribute="top" id="0SC-ZK-6ib"/>
<constraint firstAttribute="bottom" secondItem="aXw-tn-8Sj" secondAttribute="bottom" id="4bI-12-Qmv"/> <constraint firstAttribute="bottom" secondItem="aXw-tn-8Sj" secondAttribute="bottom" id="4bI-12-Qmv"/>
<constraint firstItem="aXw-tn-8Sj" firstAttribute="leading" secondItem="K2e-Gh-7hH" secondAttribute="leading" id="Tjh-fE-esc"/> <constraint firstItem="aXw-tn-8Sj" firstAttribute="leading" secondItem="K2e-Gh-7hH" secondAttribute="leading" id="Tjh-fE-esc"/>
<constraint firstItem="aXw-tn-8Sj" firstAttribute="top" secondItem="K2e-Gh-7hH" secondAttribute="top" id="XfY-8C-DuI"/>
<constraint firstAttribute="trailing" secondItem="aXw-tn-8Sj" secondAttribute="trailing" id="Z9z-S5-CEh"/> <constraint firstAttribute="trailing" secondItem="aXw-tn-8Sj" secondAttribute="trailing" id="Z9z-S5-CEh"/>
</constraints> </constraints>
</view> </view>
@@ -1611,7 +1624,7 @@
<outlet property="delegate" destination="nkY-z6-8jd" id="ENG-q5-XwX"/> <outlet property="delegate" destination="nkY-z6-8jd" id="ENG-q5-XwX"/>
</connections> </connections>
</searchBar> </searchBar>
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LEX-BK-PdS" userLabel="Bad Name Tip"> <view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LEX-BK-PdS" userLabel="Bad Name Tip">
<rect key="frame" x="37.5" y="86" width="300.5" height="75.5"/> <rect key="frame" x="37.5" y="86" width="300.5" height="75.5"/>
<subviews> <subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black_top.png" translatesAutoresizingMaskIntoConstraints="NO" id="Rt5-v4-I0R"> <imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black_top.png" translatesAutoresizingMaskIntoConstraints="NO" id="Rt5-v4-I0R">
@@ -2022,7 +2035,7 @@ eg. apple.com, rmitchell@twitter.com</string>
<objects> <objects>
<navigationController definesPresentationContext="YES" id="LBn-EA-NAH" sceneMemberID="viewController"> <navigationController definesPresentationContext="YES" id="LBn-EA-NAH" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" title="Settings" image="icon_gears.png" id="6n3-Ay-Knn"/> <tabBarItem key="tabBarItem" title="Settings" image="icon_gears.png" id="6n3-Ay-Knn"/>
<navigationBar key="navigationBar" contentMode="scaleToFill" misplaced="YES" id="h6j-1o-LHf"> <navigationBar key="navigationBar" contentMode="scaleToFill" id="h6j-1o-LHf">
<rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/> <rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
</navigationBar> </navigationBar>
@@ -2273,7 +2286,7 @@ Suspendisse potenti. Etiam ut nisi id augue tempor ultrices et sit amet sapien.
<objects> <objects>
<navigationController definesPresentationContext="YES" id="Ec4-G7-5Td" customClass="PearlNavigationController" sceneMemberID="viewController"> <navigationController definesPresentationContext="YES" id="Ec4-G7-5Td" customClass="PearlNavigationController" sceneMemberID="viewController">
<extendedEdge key="edgesForExtendedLayout"/> <extendedEdge key="edgesForExtendedLayout"/>
<navigationBar key="navigationBar" contentMode="scaleToFill" misplaced="YES" barStyle="black" id="MA4-cY-VCZ"> <navigationBar key="navigationBar" contentMode="scaleToFill" barStyle="black" id="MA4-cY-VCZ">
<rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/> <rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<color key="tintColor" red="0.47450980390000003" green="0.86666666670000003" blue="0.98431372549999996" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="tintColor" red="0.47450980390000003" green="0.86666666670000003" blue="0.98431372549999996" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@@ -2550,36 +2563,34 @@ See </string>
<scene sceneID="j3J-72-mva"> <scene sceneID="j3J-72-mva">
<objects> <objects>
<tableViewController automaticallyAdjustsScrollViewInsets="NO" id="pdl-xv-zjX" customClass="MPStoreViewController" sceneMemberID="viewController"> <tableViewController automaticallyAdjustsScrollViewInsets="NO" id="pdl-xv-zjX" customClass="MPStoreViewController" sceneMemberID="viewController">
<tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="plain" separatorStyle="default" rowHeight="400" sectionHeaderHeight="22" sectionFooterHeight="22" id="Yd8-L6-OMk"> <tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="380" sectionHeaderHeight="22" sectionFooterHeight="22" id="Yd8-L6-OMk">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.12549020350000001" green="0.1411764771" blue="0.14901961389999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.12549020350000001" green="0.1411764771" blue="0.14901961389999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<inset key="scrollIndicatorInsets" minX="0.0" minY="64" maxX="0.0" maxY="49"/> <inset key="scrollIndicatorInsets" minX="0.0" minY="64" maxX="0.0" maxY="49"/>
<color key="separatorColor" red="0.37254901959999998" green="0.3921568627" blue="0.42745098040000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="separatorColor" red="0.37254901959999998" green="0.3921568627" blue="0.42745098040000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<sections> <prototypes>
<tableViewSection id="efu-Mw-9J5"> <tableViewCell contentMode="scaleToFill" selectionStyle="default" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" reuseIdentifier="MPStoreProductCell" id="JVW-tG-xxe" userLabel="Product" customClass="MPStoreProductCell">
<cells> <rect key="frame" x="0.0" y="22" width="375" height="380"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" reuseIdentifier="MPStoreProductCellGenerateLogin" id="JVW-tG-xxe" userLabel="Generate Login" customClass="MPStoreProductCell">
<rect key="frame" x="0.0" y="0.0" width="375" height="400"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="JVW-tG-xxe" id="CLQ-CW-NGn"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="JVW-tG-xxe" id="CLQ-CW-NGn">
<rect key="frame" x="0.0" y="0.0" width="375" height="399.5"/> <rect key="frame" x="0.0" y="0.0" width="375" height="379.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="Generate Login Names" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Pax-1J-IZi"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="Negare non possum" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Pax-1J-IZi">
<rect key="frame" x="20" y="226" width="291" height="20"/> <rect key="frame" x="20" y="226" width="291" height="21"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/> <fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalCompressionResistancePriority="749" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ra0-yS-99P"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalCompressionResistancePriority="749" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ra0-yS-99P">
<rect key="frame" x="20" y="254" width="335" height="125"/> <rect key="frame" x="20" y="255" width="335" height="104.5"/>
<string key="text">Master Password is great for making your passwords immune to loss, and it even lets you save your login name. But saved login names can't be recovered when all is lost. Generated login names use the same algorithm to generate a good login name to use for each of your sites. In addition, using unique non-identifying login names for your sites helps protect against companies working together to build a bigger picture on your identity.</string> <string key="text">Lorem ipsum dolor sit amet, consectetur adipiscing elit. At multis se probavit. Sic consequentibus vestris sublatis prima tolluntur. Nescio quo modo praetervolavit oratio. Reguli reiciendam; Theophrastus mediocriterne delectat, cum tractat locos ab Aristotele ante tractatos? Duo Reges: constructio interrete.</string>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="thumb_generated_login.png" translatesAutoresizingMaskIntoConstraints="NO" id="DMJ-sd-eNJ"> <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="thumb_generated_answers.png" translatesAutoresizingMaskIntoConstraints="NO" id="DMJ-sd-eNJ">
<rect key="frame" x="88.5" y="20" width="198" height="198"/> <rect key="frame" x="88.5" y="20" width="198" height="198"/>
</imageView> </imageView>
<activityIndicatorView hidden="YES" opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="cef-sc-aph"> <activityIndicatorView hidden="YES" opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="cef-sc-aph">
@@ -2588,13 +2599,13 @@ See </string>
<label opaque="NO" userInteractionEnabled="NO" tag="3" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="✔︎" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FWu-V6-mLT"> <label opaque="NO" userInteractionEnabled="NO" tag="3" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="✔︎" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FWu-V6-mLT">
<rect key="frame" x="200.5" y="-6" width="93" height="132"/> <rect key="frame" x="200.5" y="-6" width="93" height="132"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="110"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="110"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.47450980390000003" green="0.86666666670000003" blue="0.98431372549999996" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="1" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="$0.95" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="68f-wn-UlS"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="1" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="$0.95" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="68f-wn-UlS">
<rect key="frame" x="319" y="226" width="36" height="20"/> <rect key="frame" x="319" y="226" width="36" height="21"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.47450980390000003" green="0.86666666670000003" blue="0.98431372549999996" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
@@ -2620,210 +2631,18 @@ See </string>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<connections> <connections>
<outlet property="activityIndicator" destination="cef-sc-aph" id="VmL-AX-R3f"/> <outlet property="activityIndicator" destination="cef-sc-aph" id="VmL-AX-R3f"/>
<outlet property="descriptionLabel" destination="Ra0-yS-99P" id="99B-ao-lE6"/>
<outlet property="priceLabel" destination="68f-wn-UlS" id="e99-ys-HtZ"/> <outlet property="priceLabel" destination="68f-wn-UlS" id="e99-ys-HtZ"/>
<outlet property="purchasedIndicator" destination="FWu-V6-mLT" id="Zqt-GG-e5v"/> <outlet property="purchasedIndicator" destination="FWu-V6-mLT" id="Zqt-GG-e5v"/>
<outlet property="thumbnailView" destination="DMJ-sd-eNJ" id="hCQ-2c-nti"/>
<outlet property="titleLabel" destination="Pax-1J-IZi" id="kVH-n6-mDs"/>
</connections> </connections>
</tableViewCell> </tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" reuseIdentifier="MPStoreProductCellGenerateAnswers" id="l1g-Ul-Vg8" userLabel="Generate Answers" customClass="MPStoreProductCell"> <tableViewCell contentMode="scaleToFill" selectionStyle="default" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" reuseIdentifier="MPStoreFuelProductCell" id="le3-Q5-MSO" userLabel="Fuel" customClass="MPStoreFuelProductCell">
<rect key="frame" x="0.0" y="400" width="375" height="400"/> <rect key="frame" x="0.0" y="402" width="375" height="380"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="l1g-Ul-Vg8" id="FWE-cE-L02">
<rect key="frame" x="0.0" y="0.0" width="375" height="399.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="Generate Security Answers" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Kkl-gT-YAO">
<rect key="frame" x="20" y="226" width="291" height="20"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalCompressionResistancePriority="749" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yRH-27-edZ">
<rect key="frame" x="20" y="254" width="335" height="125"/>
<string key="text">The infamous security questions are in fact a veritable assault on your security. For starters, they weaken the protection of your account by allowing an attacker in by simply knowing enough about you, allowing him to bypass guessing your strong password. Arguably worse is the amount of highly personal and private information you're "willingly" divulging to these websites, under the assumption that only you know the answers. Generating security answers allows you to stop sharing your highly personal details and weakening your acccounts by generating unguessable answers to these questions.</string>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="thumb_generated_answers.png" translatesAutoresizingMaskIntoConstraints="NO" id="6km-y3-4gu">
<rect key="frame" x="88.5" y="20" width="198" height="198"/>
</imageView>
<activityIndicatorView hidden="YES" opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="X2g-Go-2Hz">
<rect key="frame" x="169.5" y="100" width="37" height="37"/>
</activityIndicatorView>
<label opaque="NO" userInteractionEnabled="NO" tag="2" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="✔︎" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="N9y-ue-L8d">
<rect key="frame" x="200.5" y="-6" width="93" height="132"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="110"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="1" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="$0.95" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9ct-IM-QKR">
<rect key="frame" x="319" y="226" width="36" height="20"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="6km-y3-4gu" firstAttribute="centerY" secondItem="X2g-Go-2Hz" secondAttribute="centerY" id="8cn-tp-Vfc"/>
<constraint firstItem="yRH-27-edZ" firstAttribute="leading" secondItem="FWE-cE-L02" secondAttribute="leading" constant="20" symbolic="YES" id="DSv-a4-VPg"/>
<constraint firstItem="9ct-IM-QKR" firstAttribute="leading" secondItem="Kkl-gT-YAO" secondAttribute="trailing" constant="8" symbolic="YES" id="Fov-kQ-Wkn"/>
<constraint firstItem="6km-y3-4gu" firstAttribute="top" secondItem="FWE-cE-L02" secondAttribute="top" constant="20" symbolic="YES" id="Kdx-UC-Q2x"/>
<constraint firstItem="Kkl-gT-YAO" firstAttribute="leading" secondItem="FWE-cE-L02" secondAttribute="leading" constant="20" symbolic="YES" id="Nsd-7W-1Tq"/>
<constraint firstItem="Kkl-gT-YAO" firstAttribute="bottom" secondItem="9ct-IM-QKR" secondAttribute="bottom" id="Oo3-Q5-Dzd"/>
<constraint firstItem="N9y-ue-L8d" firstAttribute="centerX" secondItem="6km-y3-4gu" secondAttribute="trailing" constant="-40" id="PNi-RN-ybE"/>
<constraint firstItem="Kkl-gT-YAO" firstAttribute="top" secondItem="6km-y3-4gu" secondAttribute="bottom" constant="8" symbolic="YES" id="UPs-VG-2ND"/>
<constraint firstItem="6km-y3-4gu" firstAttribute="centerX" secondItem="X2g-Go-2Hz" secondAttribute="centerX" id="Zpt-vq-iVk"/>
<constraint firstItem="yRH-27-edZ" firstAttribute="top" secondItem="Kkl-gT-YAO" secondAttribute="bottom" constant="8" symbolic="YES" id="eHf-L3-Ong"/>
<constraint firstAttribute="trailing" secondItem="yRH-27-edZ" secondAttribute="trailing" constant="20" id="eUN-6Y-FEC"/>
<constraint firstAttribute="centerX" secondItem="6km-y3-4gu" secondAttribute="centerX" id="gv6-jC-Gaa"/>
<constraint firstAttribute="trailing" secondItem="9ct-IM-QKR" secondAttribute="trailing" constant="20" id="iAM-za-9Ev"/>
<constraint firstAttribute="bottom" secondItem="yRH-27-edZ" secondAttribute="bottom" constant="20" symbolic="YES" id="krK-MM-VKm"/>
<constraint firstItem="Kkl-gT-YAO" firstAttribute="top" secondItem="9ct-IM-QKR" secondAttribute="top" id="lie-rM-flE"/>
<constraint firstItem="6km-y3-4gu" firstAttribute="top" secondItem="N9y-ue-L8d" secondAttribute="centerY" constant="-40" id="z16-aI-lwY"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="activityIndicator" destination="X2g-Go-2Hz" id="PvQ-bP-exW"/>
<outlet property="priceLabel" destination="9ct-IM-QKR" id="UTQ-L7-vbu"/>
<outlet property="purchasedIndicator" destination="N9y-ue-L8d" id="Ppv-ay-M1J"/>
</connections>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" reuseIdentifier="MPStoreProductCellOSIntegration" id="9Na-CL-jBq" userLabel="iOS Integration" customClass="MPStoreProductCell">
<rect key="frame" x="0.0" y="800" width="375" height="400"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="9Na-CL-jBq" id="ETY-Ou-DW7">
<rect key="frame" x="0.0" y="0.0" width="375" height="399.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="iOS Integration" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Zch-DS-J3I">
<rect key="frame" x="20" y="226" width="244" height="20"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalCompressionResistancePriority="749" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="riF-bB-x5g">
<rect key="frame" x="20" y="254" width="335" height="125"/>
<string key="text">Up to now, the best way to use your passwords in other iOS apps was by copy/pasting them from Master Password. With iOS integration, you can access your Master Password passwords directly from other applications, without having to switch back and forth between it and Master Password.</string>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="thumb_ios_integration.png" translatesAutoresizingMaskIntoConstraints="NO" id="yAc-Sm-IVo">
<rect key="frame" x="88.5" y="20" width="198" height="198"/>
</imageView>
<activityIndicatorView hidden="YES" opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="yUe-TX-fli">
<rect key="frame" x="169.5" y="100" width="37" height="37"/>
</activityIndicatorView>
<label opaque="NO" userInteractionEnabled="NO" tag="2" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="✔︎" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ec8-P9-KPY">
<rect key="frame" x="200.5" y="-6" width="93" height="132"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="110"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="1" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="Coming Soon" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3jH-eX-9N2">
<rect key="frame" x="272" y="226" width="83" height="20"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="Zch-DS-J3I" firstAttribute="top" secondItem="yAc-Sm-IVo" secondAttribute="bottom" constant="8" symbolic="YES" id="0M4-0I-kCB"/>
<constraint firstItem="ec8-P9-KPY" firstAttribute="centerX" secondItem="yAc-Sm-IVo" secondAttribute="trailing" constant="-40" id="31k-Co-bGU"/>
<constraint firstItem="yAc-Sm-IVo" firstAttribute="top" secondItem="ETY-Ou-DW7" secondAttribute="top" constant="20" symbolic="YES" id="PnQ-1d-y9S"/>
<constraint firstItem="Zch-DS-J3I" firstAttribute="bottom" secondItem="3jH-eX-9N2" secondAttribute="bottom" id="XMk-An-t5v"/>
<constraint firstItem="riF-bB-x5g" firstAttribute="top" secondItem="Zch-DS-J3I" secondAttribute="bottom" constant="8" symbolic="YES" id="ZMH-Sv-ttk"/>
<constraint firstAttribute="trailing" secondItem="3jH-eX-9N2" secondAttribute="trailing" constant="20" id="diQ-Rs-Vrj"/>
<constraint firstAttribute="centerX" secondItem="yAc-Sm-IVo" secondAttribute="centerX" id="fsX-dO-EGx"/>
<constraint firstItem="Zch-DS-J3I" firstAttribute="top" secondItem="3jH-eX-9N2" secondAttribute="top" id="gZU-lV-XZJ"/>
<constraint firstAttribute="bottom" secondItem="riF-bB-x5g" secondAttribute="bottom" constant="20" symbolic="YES" id="hQZ-Qb-A7u"/>
<constraint firstAttribute="trailing" secondItem="riF-bB-x5g" secondAttribute="trailing" constant="20" id="iCn-kq-xNy"/>
<constraint firstItem="yAc-Sm-IVo" firstAttribute="centerX" secondItem="yUe-TX-fli" secondAttribute="centerX" id="kUQ-lP-EQP"/>
<constraint firstItem="3jH-eX-9N2" firstAttribute="leading" secondItem="Zch-DS-J3I" secondAttribute="trailing" constant="8" symbolic="YES" id="qqB-1E-QPE"/>
<constraint firstItem="yAc-Sm-IVo" firstAttribute="top" secondItem="ec8-P9-KPY" secondAttribute="centerY" constant="-40" id="swf-Ua-uwD"/>
<constraint firstItem="yAc-Sm-IVo" firstAttribute="centerY" secondItem="yUe-TX-fli" secondAttribute="centerY" id="tha-UC-Hwi"/>
<constraint firstItem="Zch-DS-J3I" firstAttribute="leading" secondItem="ETY-Ou-DW7" secondAttribute="leading" constant="20" symbolic="YES" id="uV9-xY-ayE"/>
<constraint firstItem="riF-bB-x5g" firstAttribute="leading" secondItem="ETY-Ou-DW7" secondAttribute="leading" constant="20" symbolic="YES" id="y9l-fO-HMK"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="activityIndicator" destination="yUe-TX-fli" id="DH6-pK-fse"/>
<outlet property="priceLabel" destination="3jH-eX-9N2" id="agT-az-dVU"/>
<outlet property="purchasedIndicator" destination="ec8-P9-KPY" id="M39-bc-Ksp"/>
</connections>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" reuseIdentifier="MPStoreProductCellTouchID" id="8en-6R-GvR" userLabel="TouchID" customClass="MPStoreProductCell">
<rect key="frame" x="0.0" y="1200" width="375" height="400"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="8en-6R-GvR" id="NKB-rd-1Vy">
<rect key="frame" x="0.0" y="0.0" width="375" height="399.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="TouchID Login" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="e1D-jp-GBs">
<rect key="frame" x="20" y="226" width="291.5" height="20"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalCompressionResistancePriority="749" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Yxc-Lr-382">
<rect key="frame" x="20" y="254" width="335" height="125"/>
<string key="text">Since the iPhone 5s, all iPhones are now equipped with an advanced Touch ID fingerprint sensor in the home button. This sensor allows you to quickly and easily identify yourself to the system. With Touch ID support, you will be able to skip the step of entering your Master Password manually and gain the ability to unlock your user using Touch ID instead.</string>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="thumb_touch_id.png" translatesAutoresizingMaskIntoConstraints="NO" id="8v7-JH-mzu">
<rect key="frame" x="88.5" y="20" width="198" height="198"/>
</imageView>
<activityIndicatorView hidden="YES" opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="Dv5-t7-lL1">
<rect key="frame" x="169.5" y="100" width="37" height="37"/>
</activityIndicatorView>
<label opaque="NO" userInteractionEnabled="NO" tag="2" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="✔︎" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yZX-ns-8oV">
<rect key="frame" x="200.5" y="-6" width="93" height="132"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="110"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="1" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="$0.95" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZGg-O6-rsg">
<rect key="frame" x="319.5" y="226" width="35.5" height="20"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="Yxc-Lr-382" firstAttribute="top" secondItem="e1D-jp-GBs" secondAttribute="bottom" constant="8" symbolic="YES" id="BMp-dh-Elb"/>
<constraint firstItem="e1D-jp-GBs" firstAttribute="top" secondItem="8v7-JH-mzu" secondAttribute="bottom" constant="8" symbolic="YES" id="CCe-dk-eBT"/>
<constraint firstItem="Dv5-t7-lL1" firstAttribute="centerY" secondItem="8v7-JH-mzu" secondAttribute="centerY" id="DUJ-Xb-503"/>
<constraint firstItem="e1D-jp-GBs" firstAttribute="bottom" secondItem="ZGg-O6-rsg" secondAttribute="bottom" id="MPG-Zj-vDr"/>
<constraint firstItem="e1D-jp-GBs" firstAttribute="leading" secondItem="NKB-rd-1Vy" secondAttribute="leading" constant="20" symbolic="YES" id="N8Z-X2-ir7"/>
<constraint firstItem="8v7-JH-mzu" firstAttribute="top" secondItem="NKB-rd-1Vy" secondAttribute="top" constant="20" symbolic="YES" id="PEc-aa-i3N"/>
<constraint firstAttribute="trailing" secondItem="ZGg-O6-rsg" secondAttribute="trailing" constant="20" id="QAy-Rp-XJc"/>
<constraint firstItem="Yxc-Lr-382" firstAttribute="leading" secondItem="NKB-rd-1Vy" secondAttribute="leading" constant="20" symbolic="YES" id="UvU-wL-3WN"/>
<constraint firstAttribute="centerX" secondItem="8v7-JH-mzu" secondAttribute="centerX" id="XVh-6p-4qT"/>
<constraint firstAttribute="trailing" secondItem="Yxc-Lr-382" secondAttribute="trailing" constant="20" id="YMA-ID-9KD"/>
<constraint firstItem="Dv5-t7-lL1" firstAttribute="centerX" secondItem="8v7-JH-mzu" secondAttribute="centerX" id="bWe-Wx-6CH"/>
<constraint firstItem="8v7-JH-mzu" firstAttribute="top" secondItem="yZX-ns-8oV" secondAttribute="centerY" constant="-40" id="n0h-tr-yA0"/>
<constraint firstItem="e1D-jp-GBs" firstAttribute="top" secondItem="ZGg-O6-rsg" secondAttribute="top" id="oZN-4h-Awv"/>
<constraint firstAttribute="bottom" secondItem="Yxc-Lr-382" secondAttribute="bottom" constant="20" symbolic="YES" id="pFY-vd-3UR"/>
<constraint firstItem="ZGg-O6-rsg" firstAttribute="leading" secondItem="e1D-jp-GBs" secondAttribute="trailing" constant="8" symbolic="YES" id="stO-51-Mlk"/>
<constraint firstItem="yZX-ns-8oV" firstAttribute="centerX" secondItem="8v7-JH-mzu" secondAttribute="trailing" constant="-40" id="yyS-FS-gvJ"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="activityIndicator" destination="Dv5-t7-lL1" id="AFu-Dd-TgU"/>
<outlet property="priceLabel" destination="ZGg-O6-rsg" id="dAn-xu-gut"/>
<outlet property="purchasedIndicator" destination="yZX-ns-8oV" id="7x0-eq-oSs"/>
</connections>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" reuseIdentifier="MPStoreProductCellFuel" id="le3-Q5-MSO" userLabel="Fuel" customClass="MPStoreProductCell">
<rect key="frame" x="0.0" y="1600" width="375" height="400"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="le3-Q5-MSO" id="SzQ-Y5-XIF"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="le3-Q5-MSO" id="SzQ-Y5-XIF">
<rect key="frame" x="0.0" y="0.0" width="375" height="399.5"/> <rect key="frame" x="0.0" y="0.0" width="375" height="379.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="Fuel Top-Up" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Jnv-uN-xeg"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="Fuel Top-Up" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Jnv-uN-xeg">
@@ -2833,14 +2652,8 @@ See </string>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalCompressionResistancePriority="749" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fz2-AO-aGW"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalCompressionResistancePriority="749" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fz2-AO-aGW">
<rect key="frame" x="20" y="254" width="335" height="125"/> <rect key="frame" x="20" y="254" width="335" height="105.5"/>
<string key="text">You really love Master Password and how it's solving your password problems. You're eager to encourage the maintenance, technical support and development of new features. I am a one-man shop, fuel enables me to allocate more work hours to Master Password. <string key="text">You really love Master Password and how it's solving your password problems. You're eager to encourage the maintenance, technical support and development of new features. I am a one-man shop, fuel enables me to allocate more work hours to Master Password.</string>
UPCOMING:
Safari integration
Touch ID support
Multi-platform support
Your feedback</string>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@@ -2849,17 +2662,20 @@ UPCOMING:
<rect key="frame" x="88.5" y="20" width="198" height="198"/> <rect key="frame" x="88.5" y="20" width="198" height="198"/>
</imageView> </imageView>
<activityIndicatorView hidden="YES" opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="eS4-59-Xny"> <activityIndicatorView hidden="YES" opaque="NO" tag="2" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="eS4-59-Xny">
<rect key="frame" x="169.5" y="100" width="37" height="37"/> <rect key="frame" x="169.5" y="101" width="37" height="37"/>
</activityIndicatorView> </activityIndicatorView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="1" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="$2.95" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EbU-DV-fKF"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="1" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="$2.95" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EbU-DV-fKF">
<rect key="frame" x="320" y="226" width="35" height="20"/> <rect key="frame" x="320" y="226" width="35" height="20"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="14"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.47450980390000003" green="0.86666666670000003" blue="0.98431372549999996" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="▲" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aGb-QC-A92">
<rect key="frame" x="261.5" y="201" width="13" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" red="0.47450980390000003" green="0.86666666670000003" blue="0.98431372549999996" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="meter_fuel.png" translatesAutoresizingMaskIntoConstraints="NO" id="aGb-QC-A92">
<rect key="frame" x="262.5" y="209" width="11" height="9"/>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dsR-fr-dY4"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dsR-fr-dY4">
<rect key="frame" x="20" y="20" width="111" height="44"/> <rect key="frame" x="20" y="20" width="111" height="44"/>
<constraints> <constraints>
@@ -2874,9 +2690,9 @@ UPCOMING:
</connections> </connections>
</button> </button>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kYb-j4-32C"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kYb-j4-32C">
<rect key="frame" x="20" y="64" width="131.5" height="29"/> <rect key="frame" x="20" y="64" width="132" height="29"/>
<string key="text">fuel left: 0.3 work hours <string key="text">Fuel left: 0.3 work hours
invested: 3.7 work hours</string> Invested: 3.7 work hours</string>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@@ -2908,92 +2724,93 @@ invested: 3.7 work hours</string>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<connections> <connections>
<outlet property="activityIndicator" destination="eS4-59-Xny" id="kGW-fn-VqH"/> <outlet property="activityIndicator" destination="eS4-59-Xny" id="kGW-fn-VqH"/>
<outlet property="descriptionLabel" destination="fz2-AO-aGW" id="xkc-xV-Z6Y"/>
<outlet property="fuelMeterConstraint" destination="eMa-Gj-BUc" id="1uc-fa-b20"/>
<outlet property="fuelSpeedButton" destination="dsR-fr-dY4" id="7yS-9g-xkM"/>
<outlet property="fuelStatusLabel" destination="kYb-j4-32C" id="ctc-ES-lGR"/>
<outlet property="priceLabel" destination="EbU-DV-fKF" id="pg2-8o-7We"/> <outlet property="priceLabel" destination="EbU-DV-fKF" id="pg2-8o-7We"/>
<outlet property="thumbnailView" destination="PnG-hP-syh" id="zHB-jE-rZ4"/>
<outlet property="titleLabel" destination="Jnv-uN-xeg" id="Kxi-RE-ipa"/>
</connections> </connections>
</tableViewCell> </tableViewCell>
</cells> <tableViewCell contentMode="scaleToFill" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" reuseIdentifier="MPStoreCellSpinner" rowHeight="170" id="LOh-72-Ifp" userLabel="Spinner">
</tableViewSection> <rect key="frame" x="0.0" y="782" width="375" height="170"/>
<tableViewSection id="hN1-J4-w3w">
<cells>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" reuseIdentifier="MPStoreCellSpinner" rowHeight="150" id="u3P-ng-YHc" userLabel="Spinner">
<rect key="frame" x="0.0" y="2000" width="375" height="150"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="u3P-ng-YHc" id="y85-0Q-tbK"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="LOh-72-Ifp" id="gjr-8l-JJ0">
<rect key="frame" x="0.0" y="0.0" width="375" height="149.5"/> <rect key="frame" x="0.0" y="0.0" width="375" height="169.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="Vjt-7c-BJ4"> <activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="MIJ-rP-BeU">
<rect key="frame" x="169" y="20" width="37" height="21"/> <rect key="frame" x="169" y="20" width="37" height="37"/>
</activityIndicatorView> </activityIndicatorView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="Loading products..." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ARC-xH-0U0"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" verticalCompressionResistancePriority="751" text="Loading products..." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NPg-it-juF">
<rect key="frame" x="115" y="81" width="145.5" height="20"/> <rect key="frame" x="115" y="97" width="145.5" height="24.5"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
<constraints> <constraints>
<constraint firstAttribute="bottom" secondItem="ARC-xH-0U0" secondAttribute="bottom" constant="48" id="5L9-Ag-d1G"/> <constraint firstAttribute="centerX" secondItem="MIJ-rP-BeU" secondAttribute="centerX" id="DXM-74-LKR"/>
<constraint firstAttribute="centerX" secondItem="ARC-xH-0U0" secondAttribute="centerX" id="asI-QK-u6O"/> <constraint firstAttribute="bottom" secondItem="NPg-it-juF" secondAttribute="bottom" constant="48" id="aCB-sK-ZCP"/>
<constraint firstItem="ARC-xH-0U0" firstAttribute="top" secondItem="Vjt-7c-BJ4" secondAttribute="bottom" constant="40" id="g1T-rA-vOW"/> <constraint firstAttribute="centerX" secondItem="NPg-it-juF" secondAttribute="centerX" id="fGb-Sq-nBz"/>
<constraint firstAttribute="centerX" secondItem="Vjt-7c-BJ4" secondAttribute="centerX" id="j2l-mA-Duf"/> <constraint firstItem="MIJ-rP-BeU" firstAttribute="top" secondItem="gjr-8l-JJ0" secondAttribute="top" constant="20" id="nh6-ca-qKI"/>
<constraint firstItem="Vjt-7c-BJ4" firstAttribute="top" secondItem="y85-0Q-tbK" secondAttribute="top" constant="20" id="v16-PA-82f"/> <constraint firstItem="NPg-it-juF" firstAttribute="top" secondItem="MIJ-rP-BeU" secondAttribute="bottom" constant="40" id="s5N-EF-Rd4"/>
</constraints> </constraints>
</tableViewCellContentView> </tableViewCellContentView>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</tableViewCell> </tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" reuseIdentifier="MPStoreCellFooter" id="UUA-xl-CAh" userLabel="Footer"> <tableViewCell contentMode="scaleToFill" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" reuseIdentifier="MPStoreCellFooter" rowHeight="100" id="jsY-TE-4y5" userLabel="Footer">
<rect key="frame" x="0.0" y="2150" width="375" height="400"/> <rect key="frame" x="0.0" y="952" width="375" height="100"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="UUA-xl-CAh" id="4Zu-Ig-Ws4"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="jsY-TE-4y5" id="guB-Eb-KpI">
<rect key="frame" x="0.0" y="0.0" width="375" height="399.5"/> <rect key="frame" x="0.0" y="0.0" width="375" height="99.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="749" verticalCompressionResistancePriority="751" text="© 2012-2014, Maarten Billemont (lhunath)" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DxV-2L-bxL"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.69999998807907104" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="749" verticalCompressionResistancePriority="751" text="© 2012-2014, Maarten Billemont (lhunath)" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="26U-st-xNs">
<rect key="frame" x="20" y="4" width="335" height="305.5"/> <rect key="frame" x="20" y="4" width="335" height="21.5"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" verticalCompressionResistancePriority="751" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IOk-WZ-HJ8"> <button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" verticalCompressionResistancePriority="751" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="lQ1-b9-Xp4">
<rect key="frame" x="20" y="317.5" width="335" height="27"/> <rect key="frame" x="20" y="33.5" width="335" height="27"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="12"/> <fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="12"/>
<state key="normal" title="Restore Previous Purchases"> <state key="normal" title="Restore Previous Purchases">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state> </state>
<connections> <connections>
<action selector="restorePurchases:" destination="pdl-xv-zjX" eventType="touchUpInside" id="lKu-PL-PfX"/> <action selector="restorePurchases:" destination="pdl-xv-zjX" eventType="touchUpInside" id="Q8M-ud-OHL"/>
</connections> </connections>
</button> </button>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bCe-a3-cDC"> <button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eus-kQ-emn">
<rect key="frame" x="8" y="352.5" width="359" height="27"/> <rect key="frame" x="8" y="68.5" width="359" height="27"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/> <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="12"/>
<state key="normal" title="Send Thanks"> <state key="normal" title="Send Thanks">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state> </state>
<connections> <connections>
<action selector="sendThanks:" destination="pdl-xv-zjX" eventType="touchUpInside" id="r5l-3U-jCA"/> <action selector="sendThanks:" destination="pdl-xv-zjX" eventType="touchUpInside" id="IrU-IR-em1"/>
</connections> </connections>
</button> </button>
</subviews> </subviews>
<constraints> <constraints>
<constraint firstAttribute="bottom" secondItem="bCe-a3-cDC" secondAttribute="bottom" constant="20" symbolic="YES" id="84k-uv-gb7"/> <constraint firstItem="lQ1-b9-Xp4" firstAttribute="leading" secondItem="guB-Eb-KpI" secondAttribute="leading" constant="20" symbolic="YES" id="A2j-I5-HlU"/>
<constraint firstAttribute="trailing" secondItem="IOk-WZ-HJ8" secondAttribute="trailing" constant="20" symbolic="YES" id="CuJ-h7-aGJ"/> <constraint firstAttribute="bottom" secondItem="eus-kQ-emn" secondAttribute="bottom" constant="4" id="Cw6-qh-jbe"/>
<constraint firstAttribute="trailing" secondItem="bCe-a3-cDC" secondAttribute="trailing" constant="8" id="Lfp-Hx-vLN"/> <constraint firstAttribute="trailing" secondItem="lQ1-b9-Xp4" secondAttribute="trailing" constant="20" symbolic="YES" id="I2K-u3-cOn"/>
<constraint firstAttribute="trailing" secondItem="DxV-2L-bxL" secondAttribute="trailing" constant="20" symbolic="YES" id="QHU-Jw-LPy"/> <constraint firstItem="26U-st-xNs" firstAttribute="top" secondItem="guB-Eb-KpI" secondAttribute="top" constant="4" id="I2p-hX-EOW"/>
<constraint firstItem="bCe-a3-cDC" firstAttribute="top" secondItem="IOk-WZ-HJ8" secondAttribute="bottom" constant="8" symbolic="YES" id="XJh-dK-J4C"/> <constraint firstItem="lQ1-b9-Xp4" firstAttribute="top" secondItem="26U-st-xNs" secondAttribute="bottom" constant="8" symbolic="YES" id="IXV-6Z-hyF"/>
<constraint firstItem="DxV-2L-bxL" firstAttribute="leading" secondItem="4Zu-Ig-Ws4" secondAttribute="leading" constant="20" symbolic="YES" id="fAD-At-gqS"/> <constraint firstItem="eus-kQ-emn" firstAttribute="leading" secondItem="guB-Eb-KpI" secondAttribute="leading" constant="8" id="Kkk-AE-iWm"/>
<constraint firstItem="DxV-2L-bxL" firstAttribute="top" secondItem="4Zu-Ig-Ws4" secondAttribute="top" constant="4" id="hTN-Cx-hOS"/> <constraint firstItem="eus-kQ-emn" firstAttribute="top" secondItem="lQ1-b9-Xp4" secondAttribute="bottom" constant="8" symbolic="YES" id="UMw-e1-O9o"/>
<constraint firstItem="bCe-a3-cDC" firstAttribute="leading" secondItem="4Zu-Ig-Ws4" secondAttribute="leading" constant="8" id="l4A-fH-9fA"/> <constraint firstAttribute="trailing" secondItem="26U-st-xNs" secondAttribute="trailing" constant="20" symbolic="YES" id="eOG-B5-WwQ"/>
<constraint firstItem="IOk-WZ-HJ8" firstAttribute="top" secondItem="DxV-2L-bxL" secondAttribute="bottom" constant="8" symbolic="YES" id="lq6-sj-DVg"/> <constraint firstAttribute="trailing" secondItem="eus-kQ-emn" secondAttribute="trailing" constant="8" id="hYo-sy-8wY"/>
<constraint firstItem="IOk-WZ-HJ8" firstAttribute="leading" secondItem="4Zu-Ig-Ws4" secondAttribute="leading" constant="20" symbolic="YES" id="mx2-Jk-5jo"/> <constraint firstItem="26U-st-xNs" firstAttribute="leading" secondItem="guB-Eb-KpI" secondAttribute="leading" constant="20" symbolic="YES" id="xfW-Sz-Kuz"/>
</constraints> </constraints>
</tableViewCellContentView> </tableViewCellContentView>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</tableViewCell> </tableViewCell>
</cells> </prototypes>
</tableViewSection> <sections/>
</sections>
<connections> <connections>
<outlet property="dataSource" destination="pdl-xv-zjX" id="ZOr-qJ-zl2"/> <outlet property="dataSource" destination="pdl-xv-zjX" id="ZOr-qJ-zl2"/>
<outlet property="delegate" destination="pdl-xv-zjX" id="xkn-ZJ-BPX"/> <outlet property="delegate" destination="pdl-xv-zjX" id="xkn-ZJ-BPX"/>
@@ -3002,17 +2819,6 @@ invested: 3.7 work hours</string>
<tabBarItem key="tabBarItem" title="Store" image="icon_star-hollow.png" id="j4c-Kp-fnH"/> <tabBarItem key="tabBarItem" title="Store" image="icon_star-hollow.png" id="j4c-Kp-fnH"/>
<nil key="simulatedStatusBarMetrics"/> <nil key="simulatedStatusBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/> <nil key="simulatedBottomBarMetrics"/>
<connections>
<outlet property="fuelCell" destination="le3-Q5-MSO" id="oAk-6g-cFj"/>
<outlet property="fuelMeterConstraint" destination="eMa-Gj-BUc" id="9iF-EO-UU6"/>
<outlet property="fuelSpeedButton" destination="dsR-fr-dY4" id="XGI-PE-9mh"/>
<outlet property="fuelStatusLabel" destination="kYb-j4-32C" id="o5R-0u-kGL"/>
<outlet property="generateAnswersCell" destination="l1g-Ul-Vg8" id="GlG-iZ-7FP"/>
<outlet property="generateLoginCell" destination="JVW-tG-xxe" id="PXM-WX-8Qe"/>
<outlet property="iOSIntegrationCell" destination="9Na-CL-jBq" id="LSO-OV-9KA"/>
<outlet property="loadingCell" destination="u3P-ng-YHc" id="gzb-K3-nKN"/>
<outlet property="touchIDCell" destination="8en-6R-GvR" id="edt-KM-iU1"/>
</connections>
</tableViewController> </tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="WvF-bk-cgx" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="WvF-bk-cgx" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects> </objects>
@@ -3317,11 +3123,11 @@ You can temporarily reveal a password by holding your finger down on the site's
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" barStyle="black" translatesAutoresizingMaskIntoConstraints="NO" id="aNU-Nq-clY"> <toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" barStyle="black" translatesAutoresizingMaskIntoConstraints="NO" id="aNU-Nq-clY">
<rect key="frame" x="0.0" y="342.5" width="375" height="324.5"/> <rect key="frame" x="0.0" y="341.5" width="375" height="325.5"/>
<items/> <items/>
</toolbar> </toolbar>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="You have sites that can be upgraded." lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Qi-GN-vhQ" userLabel="Title Label"> <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="Lorem ipsum dolor sit amet." lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Qi-GN-vhQ" userLabel="Title Label">
<rect key="frame" x="16" y="362.5" width="343" height="20"/> <rect key="frame" x="16" y="361.5" width="343" height="21"/>
<fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/> <fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@@ -3330,49 +3136,13 @@ You can temporarily reveal a password by holding your finger down on the site's
<rect key="frame" x="16" y="390.5" width="343" height="216.5"/> <rect key="frame" x="16" y="390.5" width="343" height="216.5"/>
<attributedString key="attributedText"> <attributedString key="attributedText">
<fragment> <fragment>
<string key="content">Upgrading a site allows it to take advantage of <string key="content">Prodest, inquit, mihi eo esse animo. Sed residamus, inquit, si placet. Istam voluptatem, inquit, Epicurus ignorat?
the latest improvements in the Master Password algorithm.
</string>
<attributes>
<color key="NSBackgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="NSColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<font key="NSFont" size="12" name="Exo2.0-Regular"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
<fragment>
<string key="content">
When you upgrade a site, </string>
<attributes>
<color key="NSBackgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="NSColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<font key="NSFont" size="12" name="Exo2.0-Regular"/>
<paragraphStyle key="NSParagraphStyle" alignment="justified" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
<fragment content="a new and stronger password will be generated for it">
<attributes>
<color key="NSBackgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="NSColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<font key="NSFont" size="12" name="Exo2.0-Bold"/>
<paragraphStyle key="NSParagraphStyle" alignment="justified" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
<fragment>
<string key="content">. To upgrade a site, first log into the site, navigate to your account preferences where you can change the site's password. Make sure you fill in any "current password" fields on the website first, then press the upgrade button here to get your new site password.
</string>
<attributes>
<color key="NSBackgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="NSColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<font key="NSFont" size="12" name="Exo2.0-Regular"/>
<paragraphStyle key="NSParagraphStyle" alignment="justified" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
<fragment>
<string key="content">
You can then update your site's account with the new and stronger password.
</string> Qualem igitur hominem natura inchoavit? Duo Reges: constructio interrete. Iam id ipsum absurdum, maximum malum neglegi. Quod cum dixissent, ille contra.
Nihil sane. Erat enim Polemonis. Sumenda potius quam expetenda. Sed ad illum redeo. Equidem e Cn.
Ut in geometria, prima si dederis, danda sunt omnia. Nonne igitur tibi videntur, inquit, mala? Quasi vero, inquit, perpetua oratio rhetorum solum, non etiam philosophorum sit. Tuo vero id quidem, inquam, arbitratu. A mene tu? Quid enim ab antiquis ex eo genere, quod ad disserendum valet, praetermissum est.</string>
<attributes> <attributes>
<color key="NSBackgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="NSBackgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="NSColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="NSColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -3380,14 +3150,6 @@ You can then update your site's account with the new and stronger password.
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural"/> <paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes> </attributes>
</fragment> </fragment>
<fragment content="The upgrade button can be found in the site's settings and looks like this:">
<attributes>
<color key="NSBackgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="NSColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<font key="NSFont" size="12" name="Exo2.0-Regular"/>
<paragraphStyle key="NSParagraphStyle" alignment="center" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
</attributedString> </attributedString>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
@@ -3500,6 +3262,7 @@ You can then update your site's account with the new and stronger password.
<resources> <resources>
<image name="avatar-0.png" width="110" height="110"/> <image name="avatar-0.png" width="110" height="110"/>
<image name="background.png" width="736" height="736"/> <image name="background.png" width="736" height="736"/>
<image name="icon_action.png" width="32" height="32"/>
<image name="icon_book.png" width="32" height="32"/> <image name="icon_book.png" width="32" height="32"/>
<image name="icon_cancel.png" width="32" height="32"/> <image name="icon_cancel.png" width="32" height="32"/>
<image name="icon_edit.png" width="32" height="32"/> <image name="icon_edit.png" width="32" height="32"/>
@@ -3516,12 +3279,8 @@ You can then update your site's account with the new and stronger password.
<image name="icon_up.png" width="32" height="32"/> <image name="icon_up.png" width="32" height="32"/>
<image name="identity.png" width="82" height="79"/> <image name="identity.png" width="82" height="79"/>
<image name="initial.png" width="320" height="568"/> <image name="initial.png" width="320" height="568"/>
<image name="meter_fuel.png" width="11" height="9"/>
<image name="thumb_fuel.png" width="198" height="198"/> <image name="thumb_fuel.png" width="198" height="198"/>
<image name="thumb_generated_answers.png" width="198" height="198"/> <image name="thumb_generated_answers.png" width="198" height="198"/>
<image name="thumb_generated_login.png" width="198" height="198"/>
<image name="thumb_ios_integration.png" width="198" height="198"/>
<image name="thumb_touch_id.png" width="198" height="198"/>
<image name="tip_basic_black.png" width="210" height="60"/> <image name="tip_basic_black.png" width="210" height="60"/>
<image name="tip_basic_black_top.png" width="210" height="60"/> <image name="tip_basic_black_top.png" width="210" height="60"/>
<image name="ui_spinner.png" width="75" height="75"/> <image name="ui_spinner.png" width="75" height="75"/>
@@ -3534,8 +3293,8 @@ You can then update your site's account with the new and stronger password.
<simulatedScreenMetrics key="destination" type="retina4_7.fullscreen"/> <simulatedScreenMetrics key="destination" type="retina4_7.fullscreen"/>
</simulatedMetricsContainer> </simulatedMetricsContainer>
<inferredMetricsTieBreakers> <inferredMetricsTieBreakers>
<segue reference="GZk-I4-JyH"/> <segue reference="gtb-zE-u9H"/>
<segue reference="Ql4-wf-T8u"/> <segue reference="Ql4-wf-T8u"/>
</inferredMetricsTieBreakers> </inferredMetricsTieBreakers>
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</document> </document>