Add support for Answers and improved Fabric integration.
This commit is contained in:
		@@ -42,7 +42,6 @@
 | 
			
		||||
		93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */; };
 | 
			
		||||
		93D399246DC90F50913A1287 /* UIResponder+PearlFirstResponder.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A1DDFA09AE2E14D26DC /* UIResponder+PearlFirstResponder.m */; };
 | 
			
		||||
		93D3992FA1546E01F498F665 /* PearlNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */; };
 | 
			
		||||
		93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; };
 | 
			
		||||
		93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D396C311C3725870343EE0 /* mpw-util.c */; };
 | 
			
		||||
		93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D395105935859D71679931 /* MPOverlayViewController.m */; };
 | 
			
		||||
		93D399E4BC1E092A8C8B12AE /* NSOrderedSetOrArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39FBF8FCEB4C106272334 /* NSOrderedSetOrArray.h */; };
 | 
			
		||||
@@ -87,6 +86,9 @@
 | 
			
		||||
		DA0CC5361EAB99BA009A8ED9 /* IASKTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5241EAB99BA009A8ED9 /* IASKTextField.m */; };
 | 
			
		||||
		DA0CC5371EAB99BA009A8ED9 /* IASKTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5261EAB99BA009A8ED9 /* IASKTextView.m */; };
 | 
			
		||||
		DA0CC5381EAB99BA009A8ED9 /* IASKTextViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5281EAB99BA009A8ED9 /* IASKTextViewCell.m */; };
 | 
			
		||||
		DA0CC53B1EB57B5C009A8ED9 /* Fabric.plist in Resources */ = {isa = PBXBuildFile; fileRef = DA0CC53A1EB57B5C009A8ED9 /* Fabric.plist */; };
 | 
			
		||||
		DA0CC5411EB57BD4009A8ED9 /* Fabric.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA0CC53F1EB57B91009A8ED9 /* Fabric.framework */; };
 | 
			
		||||
		DA0CC5421EB57BD4009A8ED9 /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAA141191922FED80032B392 /* Crashlytics.framework */; };
 | 
			
		||||
		DA24EBAE19DAD08900FF010B /* tip_basic_black_top.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38941711E29700CF925C /* tip_basic_black_top.png */; };
 | 
			
		||||
		DA24EBAF19DAD08C00FF010B /* tip_basic_black_top@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38951711E29700CF925C /* tip_basic_black_top@2x.png */; };
 | 
			
		||||
		DA24EBE819DAD6DE00FF010B /* Icon-320.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBE619DAD6DE00FF010B /* Icon-320.png */; };
 | 
			
		||||
@@ -114,7 +116,6 @@
 | 
			
		||||
		DA29993219C9132F00AF7DF1 /* thumb_generated_login@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA29993119C9132F00AF7DF1 /* thumb_generated_login@3x.png */; };
 | 
			
		||||
		DA29993319C9214600AF7DF1 /* icon_star-hollow.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD382A1711E29600CF925C /* icon_star-hollow.png */; };
 | 
			
		||||
		DA29993419C9214600AF7DF1 /* icon_star-hollow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD382B1711E29600CF925C /* icon_star-hollow@2x.png */; };
 | 
			
		||||
		DA2C3D611BD95EEE001137B3 /* Fabric.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA2C3D601BD95EEE001137B3 /* Fabric.framework */; };
 | 
			
		||||
		DA2C3D631BD96126001137B3 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA2C3D621BD96126001137B3 /* libc++.tbd */; };
 | 
			
		||||
		DA2C3D651BD9612F001137B3 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA2C3D641BD9612F001137B3 /* libz.tbd */; };
 | 
			
		||||
		DA2CA4DD18D28859007798F8 /* NSArray+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4D918D28859007798F8 /* NSArray+Pearl.m */; };
 | 
			
		||||
@@ -165,7 +166,6 @@
 | 
			
		||||
		DA45224A190628A1008F650A /* icon_wrench@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD386B1711E29700CF925C /* icon_wrench@2x.png */; };
 | 
			
		||||
		DA45224B190628B2008F650A /* icon_gear.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37821711E29500CF925C /* icon_gear.png */; };
 | 
			
		||||
		DA45224C190628B2008F650A /* icon_gear@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37831711E29500CF925C /* icon_gear@2x.png */; };
 | 
			
		||||
		DA48856019A5A82E000C2D79 /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAA141191922FED80032B392 /* Crashlytics.framework */; };
 | 
			
		||||
		DA4DA1D91564471A00F6F596 /* libjrswizzle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6326C148680650075AEA5 /* libjrswizzle.a */; };
 | 
			
		||||
		DA4DA1DA1564471F00F6F596 /* libuicolor-utilities.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6325D1486805C0075AEA5 /* libuicolor-utilities.a */; };
 | 
			
		||||
		DA5A09DF171A70E4005284AB /* play.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5A09DD171A70E4005284AB /* play.png */; };
 | 
			
		||||
@@ -335,7 +335,6 @@
 | 
			
		||||
		DAC77CAE148291A600BCF976 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
 | 
			
		||||
		DAC8DF47192831E100BA7D71 /* icon_key.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD379A1711E29600CF925C /* icon_key.png */; };
 | 
			
		||||
		DAC8DF48192831E100BA7D71 /* icon_key@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD379B1711E29600CF925C /* icon_key@2x.png */; };
 | 
			
		||||
		DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA269A1705DF81002C6C22 /* Crashlytics.plist */; };
 | 
			
		||||
		DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA29711705E1A8002C6C22 /* ciphers.plist */; };
 | 
			
		||||
		DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */ = {isa = PBXBuildFile; fileRef = DACA29721705E1A8002C6C22 /* dictionary.lst */; };
 | 
			
		||||
		DACA298D1705E2BD002C6C22 /* JRSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA29771705E2BD002C6C22 /* JRSwizzle.h */; };
 | 
			
		||||
@@ -667,6 +666,8 @@
 | 
			
		||||
		DA0CC5261EAB99BA009A8ED9 /* IASKTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKTextView.m; sourceTree = "<group>"; };
 | 
			
		||||
		DA0CC5271EAB99BA009A8ED9 /* IASKTextViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKTextViewCell.h; sourceTree = "<group>"; };
 | 
			
		||||
		DA0CC5281EAB99BA009A8ED9 /* IASKTextViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKTextViewCell.m; sourceTree = "<group>"; };
 | 
			
		||||
		DA0CC53A1EB57B5C009A8ED9 /* Fabric.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Fabric.plist; sourceTree = "<group>"; };
 | 
			
		||||
		DA0CC53F1EB57B91009A8ED9 /* Fabric.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Fabric.framework; sourceTree = "<group>"; };
 | 
			
		||||
		DA24EBB219DAD4D000FF010B /* Icon-60.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60.png"; sourceTree = "<group>"; };
 | 
			
		||||
		DA24EBB319DAD4D000FF010B /* Icon-60@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60@2x.png"; sourceTree = "<group>"; };
 | 
			
		||||
		DA24EBB419DAD4D000FF010B /* Icon-60@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60@3x.png"; sourceTree = "<group>"; };
 | 
			
		||||
@@ -1496,7 +1497,6 @@
 | 
			
		||||
		DAC632871486D95D0075AEA5 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
 | 
			
		||||
		DAC77CAD148291A600BCF976 /* libPearl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPearl.a; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
			
		||||
		DAC77CB1148291A600BCF976 /* Pearl-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "Pearl-Prefix.pch"; path = "../../Source/Pearl/Pearl-Prefix.pch"; sourceTree = "<group>"; };
 | 
			
		||||
		DACA269A1705DF81002C6C22 /* Crashlytics.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Crashlytics.plist; sourceTree = "<group>"; };
 | 
			
		||||
		DACA29711705E1A8002C6C22 /* ciphers.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ciphers.plist; sourceTree = "<group>"; };
 | 
			
		||||
		DACA29721705E1A8002C6C22 /* dictionary.lst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dictionary.lst; sourceTree = "<group>"; };
 | 
			
		||||
		DACA29771705E2BD002C6C22 /* JRSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JRSwizzle.h; sourceTree = "<group>"; };
 | 
			
		||||
@@ -1623,6 +1623,7 @@
 | 
			
		||||
				DA2C3D651BD9612F001137B3 /* libz.tbd in Frameworks */,
 | 
			
		||||
				DA2C3D631BD96126001137B3 /* libc++.tbd in Frameworks */,
 | 
			
		||||
				DAA1761B19D86D0D0044227B /* libAttributedMarkdown.a in Frameworks */,
 | 
			
		||||
				DA0CC5421EB57BD4009A8ED9 /* Crashlytics.framework in Frameworks */,
 | 
			
		||||
				DA32D03E19D11293004F3F0E /* libKCOrderedAccessorFix.a in Frameworks */,
 | 
			
		||||
				DA04E33E14B1E70400ECA4F3 /* MobileCoreServices.framework in Frameworks */,
 | 
			
		||||
				DAE2725A19C93B8E007C5262 /* StoreKit.framework in Frameworks */,
 | 
			
		||||
@@ -1634,15 +1635,13 @@
 | 
			
		||||
				DA672D3014F9413D004A189C /* libPearl.a in Frameworks */,
 | 
			
		||||
				DAEBC45314F6364500987BF6 /* QuartzCore.framework in Frameworks */,
 | 
			
		||||
				DA95D5F214DF0B2C008D1B94 /* MessageUI.framework in Frameworks */,
 | 
			
		||||
				DA48856019A5A82E000C2D79 /* Crashlytics.framework in Frameworks */,
 | 
			
		||||
				DAC632891486D9690075AEA5 /* Security.framework in Frameworks */,
 | 
			
		||||
				DA5BFA49147E415C00F98B1E /* UIKit.framework in Frameworks */,
 | 
			
		||||
				DA0979171E9A81EE00F0BFE8 /* libsodium.a in Frameworks */,
 | 
			
		||||
				DA5BFA4B147E415C00F98B1E /* Foundation.framework in Frameworks */,
 | 
			
		||||
				DA5BFA4D147E415C00F98B1E /* CoreGraphics.framework in Frameworks */,
 | 
			
		||||
				DA5BFA4F147E415C00F98B1E /* CoreData.framework in Frameworks */,
 | 
			
		||||
				93D399433EA75E50656040CB /* Twitter.framework in Frameworks */,
 | 
			
		||||
				DA2C3D611BD95EEE001137B3 /* Fabric.framework in Frameworks */,
 | 
			
		||||
				DA0CC5411EB57BD4009A8ED9 /* Fabric.framework in Frameworks */,
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
		};
 | 
			
		||||
@@ -1861,6 +1860,14 @@
 | 
			
		||||
			path = Views;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		DA0CC5391EB57B5C009A8ED9 /* Fabric */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				DA0CC53A1EB57B5C009A8ED9 /* Fabric.plist */,
 | 
			
		||||
			);
 | 
			
		||||
			path = Fabric;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		DA24EBB019DAD4D000FF010B /* ios */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
@@ -2022,6 +2029,7 @@
 | 
			
		||||
		DAA141181922FED80032B392 /* iOS */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				DA0CC53F1EB57B91009A8ED9 /* Fabric.framework */,
 | 
			
		||||
				DAA141191922FED80032B392 /* Crashlytics.framework */,
 | 
			
		||||
			);
 | 
			
		||||
			path = iOS;
 | 
			
		||||
@@ -2908,7 +2916,7 @@
 | 
			
		||||
		DACA23B41705DF7D002C6C22 /* Resources */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				DACA26991705DF81002C6C22 /* Crashlytics */,
 | 
			
		||||
				DA0CC5391EB57B5C009A8ED9 /* Fabric */,
 | 
			
		||||
				DACA29701705E1A8002C6C22 /* Data */,
 | 
			
		||||
				DAE1EF2417E942DE00BC0086 /* Localizable.strings */,
 | 
			
		||||
				DABD360D1711E29400CF925C /* Media */,
 | 
			
		||||
@@ -2916,14 +2924,6 @@
 | 
			
		||||
			path = Resources;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		DACA26991705DF81002C6C22 /* Crashlytics */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				DACA269A1705DF81002C6C22 /* Crashlytics.plist */,
 | 
			
		||||
			);
 | 
			
		||||
			path = Crashlytics;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		DACA29701705E1A8002C6C22 /* Data */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
@@ -3287,7 +3287,7 @@
 | 
			
		||||
				DA5BFA41147E415C00F98B1E /* Frameworks */,
 | 
			
		||||
				DA5BFA42147E415C00F98B1E /* Resources */,
 | 
			
		||||
				DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */,
 | 
			
		||||
				DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */,
 | 
			
		||||
				DAD3125D155288AA00A3F9ED /* Run Script: Fabric */,
 | 
			
		||||
			);
 | 
			
		||||
			buildRules = (
 | 
			
		||||
			);
 | 
			
		||||
@@ -3548,7 +3548,7 @@
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
				DAFE4A5A1503982E003ABA7C /* Pearl.strings in Resources */,
 | 
			
		||||
				DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */,
 | 
			
		||||
				DA0CC53B1EB57B5C009A8ED9 /* Fabric.plist in Resources */,
 | 
			
		||||
				DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */,
 | 
			
		||||
				DA32D04F19D2F59B004F3F0E /* meter_fuel@2x.png in Resources */,
 | 
			
		||||
				DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */,
 | 
			
		||||
@@ -3778,19 +3778,19 @@
 | 
			
		||||
			shellScript = "exec Scripts/genassets";
 | 
			
		||||
			showEnvVarsInLog = 0;
 | 
			
		||||
		};
 | 
			
		||||
		DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */ = {
 | 
			
		||||
		DAD3125D155288AA00A3F9ED /* Run Script: Fabric */ = {
 | 
			
		||||
			isa = PBXShellScriptBuildPhase;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
			);
 | 
			
		||||
			inputPaths = (
 | 
			
		||||
			);
 | 
			
		||||
			name = "Run Script: Crashlytics";
 | 
			
		||||
			name = "Run Script: Fabric";
 | 
			
		||||
			outputPaths = (
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
			shellPath = "/bin/bash -e";
 | 
			
		||||
			shellScript = "[[ $DEPLOYMENT_LOCATION != YES ]] && exit\n\napiKey=$(/usr/libexec/PlistBuddy -c \"Print :'API Key'\" Resources/Crashlytics/Crashlytics.plist)\n[[ $apiKey ]] && External/iOS/Crashlytics.framework/run \"$apiKey\"";
 | 
			
		||||
			shellScript = "[[ $DEPLOYMENT_LOCATION != YES ]] && exit\n\napiKey=$(/usr/libexec/PlistBuddy -c \"Print :'API Key'\" Resources/Fabric/Fabric.plist)\n[[ $apiKey ]] && External/iOS/Fabric.framework/run \"$apiKey\"";
 | 
			
		||||
			showEnvVarsInLog = 0;
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXShellScriptBuildPhase section */
 | 
			
		||||
@@ -4147,7 +4147,6 @@
 | 
			
		||||
				);
 | 
			
		||||
				GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
 | 
			
		||||
				INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
 | 
			
		||||
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
 | 
			
		||||
				LIBRARY_SEARCH_PATHS = (
 | 
			
		||||
					"$(inherited)",
 | 
			
		||||
					"$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib",
 | 
			
		||||
@@ -4263,6 +4262,7 @@
 | 
			
		||||
				GCC_PREPROCESSOR_DEFINITIONS = (
 | 
			
		||||
					"DEBUG=1",
 | 
			
		||||
					"$(inherited)",
 | 
			
		||||
					"CRASHLYTICS=1",
 | 
			
		||||
				);
 | 
			
		||||
				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
 | 
			
		||||
				GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
 | 
			
		||||
@@ -4412,7 +4412,6 @@
 | 
			
		||||
				);
 | 
			
		||||
				GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
 | 
			
		||||
				INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
 | 
			
		||||
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
 | 
			
		||||
				LIBRARY_SEARCH_PATHS = (
 | 
			
		||||
					"$(inherited)",
 | 
			
		||||
					"$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib",
 | 
			
		||||
@@ -4440,7 +4439,6 @@
 | 
			
		||||
				);
 | 
			
		||||
				GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
 | 
			
		||||
				INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
 | 
			
		||||
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
 | 
			
		||||
				LIBRARY_SEARCH_PATHS = (
 | 
			
		||||
					"$(inherited)",
 | 
			
		||||
					"$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib",
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@
 | 
			
		||||
		DA09745B1E99582900F0BFE8 /* mpw-tests.c in Sources */ = {isa = PBXBuildFile; fileRef = DA0974571E99582200F0BFE8 /* mpw-tests.c */; };
 | 
			
		||||
		DA09745E1E99586600F0BFE8 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA09745D1E99586600F0BFE8 /* libxml2.tbd */; };
 | 
			
		||||
		DA0979681E9A834C00F0BFE8 /* libsodium.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA0979571E9A824700F0BFE8 /* libsodium.a */; };
 | 
			
		||||
		DA0CC53E1EB57B69009A8ED9 /* Fabric.plist in Resources */ = {isa = PBXBuildFile; fileRef = DA0CC53D1EB57B69009A8ED9 /* Fabric.plist */; };
 | 
			
		||||
		DA1000801998A4C6002B873F /* openssl in Headers */ = {isa = PBXBuildFile; fileRef = DAE8E65719867AF500416A0F /* openssl */; settings = {ATTRIBUTES = (Public, ); }; };
 | 
			
		||||
		DA16B341170661DB000A0EAB /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA16B340170661DB000A0EAB /* Carbon.framework */; };
 | 
			
		||||
		DA16B342170661E0000A0EAB /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC632871486D95D0075AEA5 /* Security.framework */; };
 | 
			
		||||
@@ -148,7 +149,6 @@
 | 
			
		||||
		DACA27381705DF81002C6C22 /* menu-icon.png in Resources */ = {isa = PBXBuildFile; fileRef = DACA24581705DF7D002C6C22 /* menu-icon.png */; };
 | 
			
		||||
		DACA29671705DF81002C6C22 /* SourceCodePro-ExtraLight.otf in Resources */ = {isa = PBXBuildFile; fileRef = DACA268E1705DF81002C6C22 /* SourceCodePro-ExtraLight.otf */; };
 | 
			
		||||
		DACA29681705DF81002C6C22 /* SourceCodePro-Black.otf in Resources */ = {isa = PBXBuildFile; fileRef = DACA268F1705DF81002C6C22 /* SourceCodePro-Black.otf */; };
 | 
			
		||||
		DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA269A1705DF81002C6C22 /* Crashlytics.plist */; };
 | 
			
		||||
		DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA29711705E1A8002C6C22 /* ciphers.plist */; };
 | 
			
		||||
		DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */ = {isa = PBXBuildFile; fileRef = DACA29721705E1A8002C6C22 /* dictionary.lst */; };
 | 
			
		||||
		DACA298D1705E2BD002C6C22 /* JRSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA29771705E2BD002C6C22 /* JRSwizzle.h */; };
 | 
			
		||||
@@ -350,6 +350,7 @@
 | 
			
		||||
		DA0979531E9A824700F0BFE8 /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = "<group>"; };
 | 
			
		||||
		DA0979541E9A824700F0BFE8 /* sodium.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sodium.h; sourceTree = "<group>"; };
 | 
			
		||||
		DA0979571E9A824700F0BFE8 /* libsodium.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsodium.a; sourceTree = "<group>"; };
 | 
			
		||||
		DA0CC53D1EB57B69009A8ED9 /* Fabric.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Fabric.plist; sourceTree = "<group>"; };
 | 
			
		||||
		DA16B340170661DB000A0EAB /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
 | 
			
		||||
		DA16B343170661EE000A0EAB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
 | 
			
		||||
		DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPPasswordWindowController.xib; sourceTree = "<group>"; };
 | 
			
		||||
@@ -911,7 +912,6 @@
 | 
			
		||||
		DACA24581705DF7D002C6C22 /* menu-icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu-icon.png"; sourceTree = "<group>"; };
 | 
			
		||||
		DACA268E1705DF81002C6C22 /* SourceCodePro-ExtraLight.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-ExtraLight.otf"; sourceTree = "<group>"; };
 | 
			
		||||
		DACA268F1705DF81002C6C22 /* SourceCodePro-Black.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-Black.otf"; sourceTree = "<group>"; };
 | 
			
		||||
		DACA269A1705DF81002C6C22 /* Crashlytics.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Crashlytics.plist; sourceTree = "<group>"; };
 | 
			
		||||
		DACA29711705E1A8002C6C22 /* ciphers.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ciphers.plist; sourceTree = "<group>"; };
 | 
			
		||||
		DACA29721705E1A8002C6C22 /* dictionary.lst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dictionary.lst; sourceTree = "<group>"; };
 | 
			
		||||
		DACA29771705E2BD002C6C22 /* JRSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JRSwizzle.h; sourceTree = "<group>"; };
 | 
			
		||||
@@ -1132,6 +1132,14 @@
 | 
			
		||||
			path = lib;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		DA0CC53C1EB57B69009A8ED9 /* Fabric */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				DA0CC53D1EB57B69009A8ED9 /* Fabric.plist */,
 | 
			
		||||
			);
 | 
			
		||||
			path = Fabric;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		DA2508F819513C1400AC23F1 /* Other Frameworks */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
@@ -1772,8 +1780,8 @@
 | 
			
		||||
		DACA23B41705DF7D002C6C22 /* Resources */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				DA0CC53C1EB57B69009A8ED9 /* Fabric */,
 | 
			
		||||
				DA09745F1E995EB500F0BFE8 /* mpw_tests.xml */,
 | 
			
		||||
				DACA26991705DF81002C6C22 /* Crashlytics */,
 | 
			
		||||
				DACA29701705E1A8002C6C22 /* Data */,
 | 
			
		||||
				DACA23B51705DF7D002C6C22 /* Media */,
 | 
			
		||||
			);
 | 
			
		||||
@@ -1856,14 +1864,6 @@
 | 
			
		||||
			path = Fonts;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		DACA26991705DF81002C6C22 /* Crashlytics */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				DACA269A1705DF81002C6C22 /* Crashlytics.plist */,
 | 
			
		||||
			);
 | 
			
		||||
			path = Crashlytics;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		DACA29701705E1A8002C6C22 /* Data */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
@@ -2086,7 +2086,7 @@
 | 
			
		||||
				DA5BFA42147E415C00F98B1E /* Resources */,
 | 
			
		||||
				DAD9B5EE1762CA3A001835F9 /* Copy LoginHelper */,
 | 
			
		||||
				DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */,
 | 
			
		||||
				DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */,
 | 
			
		||||
				DAD3125D155288AA00A3F9ED /* Run Script: Fabric */,
 | 
			
		||||
			);
 | 
			
		||||
			buildRules = (
 | 
			
		||||
			);
 | 
			
		||||
@@ -2295,13 +2295,13 @@
 | 
			
		||||
				DACA27331705DF81002C6C22 /* avatar-12@2x.png in Resources */,
 | 
			
		||||
				DACA27341705DF81002C6C22 /* avatar-2@2x.png in Resources */,
 | 
			
		||||
				DA60717D195D040500CA98B5 /* icon_gear@2x.png in Resources */,
 | 
			
		||||
				DA0CC53E1EB57B69009A8ED9 /* Fabric.plist in Resources */,
 | 
			
		||||
				DACA27351705DF81002C6C22 /* avatar-11.png in Resources */,
 | 
			
		||||
				DACA27361705DF81002C6C22 /* avatar-0@2x.png in Resources */,
 | 
			
		||||
				DACA27371705DF81002C6C22 /* avatar-10@2x.png in Resources */,
 | 
			
		||||
				DACA27381705DF81002C6C22 /* menu-icon.png in Resources */,
 | 
			
		||||
				DACA29671705DF81002C6C22 /* SourceCodePro-ExtraLight.otf in Resources */,
 | 
			
		||||
				DACA29681705DF81002C6C22 /* SourceCodePro-Black.otf in Resources */,
 | 
			
		||||
				DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */,
 | 
			
		||||
				DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */,
 | 
			
		||||
				DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */,
 | 
			
		||||
				DA5E5D081724A667003798D8 /* MasterPassword.entitlements in Resources */,
 | 
			
		||||
@@ -2359,19 +2359,19 @@
 | 
			
		||||
			shellPath = "/bin/sh -e";
 | 
			
		||||
			shellScript = "exec Scripts/updatePlist";
 | 
			
		||||
		};
 | 
			
		||||
		DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */ = {
 | 
			
		||||
		DAD3125D155288AA00A3F9ED /* Run Script: Fabric */ = {
 | 
			
		||||
			isa = PBXShellScriptBuildPhase;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
			);
 | 
			
		||||
			inputPaths = (
 | 
			
		||||
			);
 | 
			
		||||
			name = "Run Script: Crashlytics";
 | 
			
		||||
			name = "Run Script: Fabric";
 | 
			
		||||
			outputPaths = (
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
			shellPath = "/bin/sh -e";
 | 
			
		||||
			shellScript = "[[ $DEPLOYMENT_LOCATION != YES ]] && exit\n\napiKey=$(/usr/libexec/PlistBuddy -c \"Print :'API Key'\" Resources/Crashlytics/Crashlytics.plist)\n[[ $apiKey ]] && External/Mac/Fabric.framework/run \"$apiKey\" 410fb41450e3a2e50fa8357682d812ecd3e1846f2141a99bdb9d3a6a981ad69c";
 | 
			
		||||
			shellScript = "[[ $DEPLOYMENT_LOCATION != YES ]] && exit\n\napiKey=$(/usr/libexec/PlistBuddy -c \"Print :'API Key'\" Resources/Fabric/Fabric.plist)\n[[ $apiKey ]] && External/Mac/Fabric.framework/run \"$apiKey\" 410fb41450e3a2e50fa8357682d812ecd3e1846f2141a99bdb9d3a6a981ad69c";
 | 
			
		||||
			showEnvVarsInLog = 0;
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXShellScriptBuildPhase section */
 | 
			
		||||
 
 | 
			
		||||
@@ -55,14 +55,14 @@ setSettingWithTitle "Copyright" "$(getPlistWithKey NSHumanReadableCopyright)"
 | 
			
		||||
 | 
			
		||||
if [[ $DEPLOYMENT_LOCATION = YES ]]; then
 | 
			
		||||
    # This build is a release.  Do some release checks.
 | 
			
		||||
    crashlyticsPlist="$BUILT_PRODUCTS_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/Crashlytics.plist"
 | 
			
		||||
    fabricPlist="$BUILT_PRODUCTS_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/Fabric.plist"
 | 
			
		||||
    passed=1
 | 
			
		||||
    [[ $description != *-dirty ]] || \
 | 
			
		||||
        { passed=0; err 'ERROR: Cannot release a dirty version, first commit any changes.'; }
 | 
			
		||||
    [[ $build == 0 ]] || \
 | 
			
		||||
        { passed=0; err 'ERROR: Commit is not tagged for release, first tag accordingly.'; }
 | 
			
		||||
    [[ -r "$crashlyticsPlist" && $(PlistBuddy -c "Print :'API Key'" "$crashlyticsPlist" 2>/dev/null) ]] || \
 | 
			
		||||
        { passed=0; err 'ERROR: Cannot release: Crashlytics API key is missing.'; }
 | 
			
		||||
    [[ -r "$fabricPlist" && $(PlistBuddy -c "Print :'API Key'" "$fabricPlist" 2>/dev/null) ]] || \
 | 
			
		||||
        { passed=0; err 'ERROR: Cannot release: Fabric API key is missing.'; }
 | 
			
		||||
    (( passed )) || \
 | 
			
		||||
        { ftl "Failed to pass release checks.  Fix the above errors and re-try.  Aborting."; exit 1; }
 | 
			
		||||
fi
 | 
			
		||||
 
 | 
			
		||||
@@ -93,7 +93,7 @@ NSOperationQueue *_mpwQueue = nil;
 | 
			
		||||
    migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d AND user == %@", self.version, user];
 | 
			
		||||
    NSArray *migrationSites = [moc executeFetchRequest:migrationRequest error:&error];
 | 
			
		||||
    if (!migrationSites) {
 | 
			
		||||
        err( @"While looking for sites to migrate: %@", [error fullDescription] );
 | 
			
		||||
        MPError( error, @"While looking for sites to migrate." );
 | 
			
		||||
        return NO;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@
 | 
			
		||||
 | 
			
		||||
@protocol MPInAppDelegate
 | 
			
		||||
 | 
			
		||||
- (void)updateWithProducts:(NSArray /* SKProduct */ *)products;
 | 
			
		||||
- (void)updateWithProducts:(NSDictionary<NSString *, SKProduct *> *)products;
 | 
			
		||||
- (void)updateWithTransaction:(SKPaymentTransaction *)transaction;
 | 
			
		||||
 | 
			
		||||
@end
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,8 @@
 | 
			
		||||
 | 
			
		||||
@implementation MPAppDelegate_Shared(InApp)
 | 
			
		||||
 | 
			
		||||
PearlAssociatedObjectProperty( NSArray*, Products, products );
 | 
			
		||||
PearlAssociatedObjectProperty( NSDictionary*, Products, products );
 | 
			
		||||
 | 
			
		||||
PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObservers );
 | 
			
		||||
 | 
			
		||||
- (void)registerProductsObserver:(id<MPInAppDelegate>)delegate {
 | 
			
		||||
@@ -101,11 +102,25 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    for (SKProduct *product in self.products)
 | 
			
		||||
    for (SKProduct *product in [self.products allValues])
 | 
			
		||||
        if ([product.productIdentifier isEqualToString:productIdentifier]) {
 | 
			
		||||
            SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
 | 
			
		||||
            payment.quantity = quantity;
 | 
			
		||||
            [[self paymentQueue] addPayment:payment];
 | 
			
		||||
            if (payment) {
 | 
			
		||||
                payment.quantity = quantity;
 | 
			
		||||
                [[self paymentQueue] addPayment:payment];
 | 
			
		||||
 | 
			
		||||
                if ([[MPConfig get].sendInfo boolValue]) {
 | 
			
		||||
#ifdef CRASHLYTICS
 | 
			
		||||
                    [Answers logAddToCartWithPrice:product.price currency:product.priceLocale.currencyCode itemName:product.localizedTitle
 | 
			
		||||
                                          itemType:@"InApp" itemId:product.productIdentifier
 | 
			
		||||
                                  customAttributes:nil];
 | 
			
		||||
                    [Answers logStartCheckoutWithPrice:product.price currency:product.priceLocale.currencyCode itemCount:@(quantity)
 | 
			
		||||
                                      customAttributes:@{
 | 
			
		||||
                                              @"products": @[ productIdentifier ],
 | 
			
		||||
                                      }];
 | 
			
		||||
#endif
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
@@ -114,8 +129,13 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
 | 
			
		||||
 | 
			
		||||
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
 | 
			
		||||
 | 
			
		||||
    inf( @"products: %@, invalid: %@", response.products, response.invalidProductIdentifiers );
 | 
			
		||||
    self.products = response.products;
 | 
			
		||||
    if ([response.invalidProductIdentifiers count])
 | 
			
		||||
        inf( @"Invalid products: %@", response.invalidProductIdentifiers );
 | 
			
		||||
 | 
			
		||||
    NSMutableDictionary *products = [NSMutableDictionary dictionaryWithCapacity:[response.products count]];
 | 
			
		||||
    for (SKProduct *product in response.products)
 | 
			
		||||
        products[product.productIdentifier] = product;
 | 
			
		||||
    self.products = products;
 | 
			
		||||
 | 
			
		||||
    for (id<MPInAppDelegate> productObserver in self.productObservers)
 | 
			
		||||
        [productObserver updateWithProducts:self.products];
 | 
			
		||||
@@ -123,6 +143,8 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
 | 
			
		||||
 | 
			
		||||
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
 | 
			
		||||
 | 
			
		||||
    MPError( error, @"StoreKit request (%@) failed.", request );
 | 
			
		||||
 | 
			
		||||
#if TARGET_OS_IPHONE
 | 
			
		||||
    [PearlAlert showAlertWithTitle:@"Purchase Failed" message:
 | 
			
		||||
                    strf( @"%@\n\n%@", error.localizedDescription,
 | 
			
		||||
@@ -131,7 +153,6 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
 | 
			
		||||
                       cancelTitle:@"OK" otherTitles:nil];
 | 
			
		||||
#else
 | 
			
		||||
#endif
 | 
			
		||||
    err( @"StoreKit request (%@) failed: %@", request, [error fullDescription] );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)requestDidFinish:(SKRequest *)request {
 | 
			
		||||
@@ -147,21 +168,37 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
 | 
			
		||||
        dbg( @"transaction updated: %@ -> %d", transaction.payment.productIdentifier, (int)(transaction.transactionState) );
 | 
			
		||||
        switch (transaction.transactionState) {
 | 
			
		||||
            case SKPaymentTransactionStatePurchased: {
 | 
			
		||||
                inf( @"purchased: %@", transaction.payment.productIdentifier );
 | 
			
		||||
                inf( @"Purchased: %@", transaction.payment.productIdentifier );
 | 
			
		||||
                NSMutableDictionary *attributes = [NSMutableDictionary new];
 | 
			
		||||
 | 
			
		||||
                if ([transaction.payment.productIdentifier isEqualToString:MPProductFuel]) {
 | 
			
		||||
                    float currentFuel = [[MPiOSConfig get].developmentFuelRemaining floatValue];
 | 
			
		||||
                    float purchasedFuel = transaction.payment.quantity / MP_FUEL_HOURLY_RATE;
 | 
			
		||||
                    [MPiOSConfig get].developmentFuelRemaining = @(currentFuel + purchasedFuel);
 | 
			
		||||
                    if (![MPiOSConfig get].developmentFuelChecked || currentFuel < DBL_EPSILON)
 | 
			
		||||
                        [MPiOSConfig get].developmentFuelChecked = [NSDate date];
 | 
			
		||||
                    [attributes addEntriesFromDictionary:@{
 | 
			
		||||
                            @"currentFuel"  : @(currentFuel),
 | 
			
		||||
                            @"purchasedFuel": @(purchasedFuel),
 | 
			
		||||
                    }];
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                [[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier
 | 
			
		||||
                                                          forKey:transaction.payment.productIdentifier];
 | 
			
		||||
                [queue finishTransaction:transaction];
 | 
			
		||||
 | 
			
		||||
                if ([[MPConfig get].sendInfo boolValue]) {
 | 
			
		||||
#ifdef CRASHLYTICS
 | 
			
		||||
                    SKProduct *product = self.products[transaction.payment.productIdentifier];
 | 
			
		||||
                    [Answers logPurchaseWithPrice:product.price currency:product.priceLocale.currencyCode success:@YES
 | 
			
		||||
                                         itemName:product.localizedTitle itemType:@"InApp" itemId:product.productIdentifier
 | 
			
		||||
                                 customAttributes:attributes];
 | 
			
		||||
#endif
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case SKPaymentTransactionStateRestored: {
 | 
			
		||||
                inf( @"restored: %@", transaction.payment.productIdentifier );
 | 
			
		||||
                inf( @"Restored: %@", transaction.payment.productIdentifier );
 | 
			
		||||
                [[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier
 | 
			
		||||
                                                          forKey:transaction.payment.productIdentifier];
 | 
			
		||||
                [queue finishTransaction:transaction];
 | 
			
		||||
@@ -173,6 +210,18 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
 | 
			
		||||
            case SKPaymentTransactionStateFailed:
 | 
			
		||||
                err( @"Transaction failed: %@, reason: %@", transaction.payment.productIdentifier, [transaction.error fullDescription] );
 | 
			
		||||
                [queue finishTransaction:transaction];
 | 
			
		||||
 | 
			
		||||
                if ([[MPConfig get].sendInfo boolValue]) {
 | 
			
		||||
#ifdef CRASHLYTICS
 | 
			
		||||
                    SKProduct *product = self.products[transaction.payment.productIdentifier];
 | 
			
		||||
                    [Answers logPurchaseWithPrice:product.price currency:product.priceLocale.currencyCode success:@YES
 | 
			
		||||
                                         itemName:product.localizedTitle itemType:@"InApp" itemId:product.productIdentifier
 | 
			
		||||
                                 customAttributes:@{
 | 
			
		||||
                                         @"state" : @"Failed",
 | 
			
		||||
                                         @"reason": [transaction.error fullDescription],
 | 
			
		||||
                                 }];
 | 
			
		||||
#endif
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -185,7 +234,7 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
 | 
			
		||||
 | 
			
		||||
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
 | 
			
		||||
 | 
			
		||||
    err( @"StoreKit restore failed: %@", [error fullDescription] );
 | 
			
		||||
    MPError( error, @"StoreKit restore failed." );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@end
 | 
			
		||||
 
 | 
			
		||||
@@ -16,12 +16,13 @@
 | 
			
		||||
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#import <Crashlytics/Answers.h>
 | 
			
		||||
#import "MPAppDelegate_Key.h"
 | 
			
		||||
#import "MPAppDelegate_Store.h"
 | 
			
		||||
 | 
			
		||||
@interface MPAppDelegate_Shared()
 | 
			
		||||
 | 
			
		||||
@property(strong, nonatomic) MPKey *key;
 | 
			
		||||
@property(strong, atomic) MPKey *key;
 | 
			
		||||
 | 
			
		||||
@end
 | 
			
		||||
 | 
			
		||||
@@ -177,6 +178,14 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
 | 
			
		||||
        else
 | 
			
		||||
            dbg( @"Automatic login failed for user: %@", user.userID );
 | 
			
		||||
 | 
			
		||||
        if ([[MPConfig get].sendInfo boolValue]) {
 | 
			
		||||
#ifdef CRASHLYTICS
 | 
			
		||||
            [Answers logLoginWithMethod:password? @"Password": @"Automatic" success:@NO customAttributes:@{
 | 
			
		||||
                    @"algorithm": @(user.algorithm.version),
 | 
			
		||||
            }];
 | 
			
		||||
#endif
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return NO;
 | 
			
		||||
    }
 | 
			
		||||
    inf( @"Logged in user: %@", user.userID );
 | 
			
		||||
@@ -198,8 +207,11 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
 | 
			
		||||
    @try {
 | 
			
		||||
        if ([[MPConfig get].sendInfo boolValue]) {
 | 
			
		||||
#ifdef CRASHLYTICS
 | 
			
		||||
            [[Crashlytics sharedInstance] setObjectValue:user.userID forKey:@"username"];
 | 
			
		||||
            [[Crashlytics sharedInstance] setUserName:user.userID];
 | 
			
		||||
 | 
			
		||||
            [Answers logLoginWithMethod:password? @"Password": @"Automatic" success:@YES customAttributes:@{
 | 
			
		||||
                    @"algorithm": @(user.algorithm.version),
 | 
			
		||||
            }];
 | 
			
		||||
#endif
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -26,9 +26,9 @@
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@property(strong, nonatomic, readonly) MPKey *key;
 | 
			
		||||
@property(strong, nonatomic, readonly) NSManagedObjectID *activeUserOID;
 | 
			
		||||
@property(strong, nonatomic, readonly) NSPersistentStoreCoordinator *storeCoordinator;
 | 
			
		||||
@property(strong, atomic, readonly) MPKey *key;
 | 
			
		||||
@property(strong, atomic, readonly) NSManagedObjectID *activeUserOID;
 | 
			
		||||
@property(strong, atomic, readonly) NSPersistentStoreCoordinator *storeCoordinator;
 | 
			
		||||
 | 
			
		||||
+ (instancetype)get;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,9 @@
 | 
			
		||||
 | 
			
		||||
@interface MPAppDelegate_Shared()
 | 
			
		||||
 | 
			
		||||
@property(strong, nonatomic) MPKey *key;
 | 
			
		||||
@property(strong, nonatomic) NSManagedObjectID *activeUserOID;
 | 
			
		||||
@property(strong, nonatomic) NSPersistentStoreCoordinator *storeCoordinator;
 | 
			
		||||
@property(strong, atomic) MPKey *key;
 | 
			
		||||
@property(strong, atomic) NSManagedObjectID *activeUserOID;
 | 
			
		||||
@property(strong, atomic) NSPersistentStoreCoordinator *storeCoordinator;
 | 
			
		||||
 | 
			
		||||
@end
 | 
			
		||||
 | 
			
		||||
@@ -76,12 +76,13 @@
 | 
			
		||||
 | 
			
		||||
    NSError *error;
 | 
			
		||||
    if (activeUser.objectID.isTemporaryID && ![activeUser.managedObjectContext obtainPermanentIDsForObjects:@[ activeUser ] error:&error])
 | 
			
		||||
        err( @"Failed to obtain a permanent object ID after setting active user: %@", [error fullDescription] );
 | 
			
		||||
        MPError( error, @"Failed to obtain a permanent object ID after setting active user." );
 | 
			
		||||
 | 
			
		||||
    self.activeUserOID = activeUser.objectID;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)handleCoordinatorError:(NSError *)error {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@end
 | 
			
		||||
 
 | 
			
		||||
@@ -237,7 +237,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
        NSURL *localStoreURL = [self localStoreURL];
 | 
			
		||||
        if (![[NSFileManager defaultManager] createDirectoryAtURL:[localStoreURL URLByDeletingLastPathComponent]
 | 
			
		||||
                                      withIntermediateDirectories:YES attributes:nil error:&error]) {
 | 
			
		||||
            err( @"Couldn't create our application support directory: %@", [error fullDescription] );
 | 
			
		||||
            MPError( error, @"Couldn't create our application support directory." );
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (![self.storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self localStoreURL]
 | 
			
		||||
@@ -246,7 +246,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
                                                               NSInferMappingModelAutomaticallyOption      : @YES,
 | 
			
		||||
                                                               STORE_OPTIONS
 | 
			
		||||
                                                       } error:&error]) {
 | 
			
		||||
            err( @"Failed to open store: %@", [error fullDescription] );
 | 
			
		||||
            MPError( error, @"Failed to open store." );
 | 
			
		||||
            self.storeCorrupted = @YES;
 | 
			
		||||
            [self handleCoordinatorError:error];
 | 
			
		||||
            return;
 | 
			
		||||
@@ -255,12 +255,15 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
 | 
			
		||||
#if TARGET_OS_IPHONE
 | 
			
		||||
        PearlAddNotificationObserver( UIApplicationWillResignActiveNotification, UIApp, [NSOperationQueue mainQueue],
 | 
			
		||||
                ^(MPAppDelegate_Shared *self, NSNotification *note) {
 | 
			
		||||
                    [self.mainManagedObjectContext saveToStore];
 | 
			
		||||
                } );
 | 
			
		||||
#else
 | 
			
		||||
        PearlAddNotificationObserver( NSApplicationWillResignActiveNotification, NSApp, [NSOperationQueue mainQueue],
 | 
			
		||||
#endif
 | 
			
		||||
                ^(MPAppDelegate_Shared *self, NSNotification *note) {
 | 
			
		||||
        [self.mainManagedObjectContext saveToStore];
 | 
			
		||||
        } );
 | 
			
		||||
                    [self.mainManagedObjectContext saveToStore];
 | 
			
		||||
                } );
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        // Perform a data sanity check on the newly loaded store to find and fix any issues.
 | 
			
		||||
        if ([[MPConfig get].checkInconsistency boolValue])
 | 
			
		||||
@@ -286,10 +289,10 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
        NSError *error = nil;
 | 
			
		||||
        for (NSPersistentStore *store in self.storeCoordinator.persistentStores) {
 | 
			
		||||
            if (![self.storeCoordinator removePersistentStore:store error:&error])
 | 
			
		||||
                err( @"Couldn't remove persistence store from coordinator: %@", [error fullDescription] );
 | 
			
		||||
                MPError( error, @"Couldn't remove persistence store from coordinator." );
 | 
			
		||||
        }
 | 
			
		||||
        if (![[NSFileManager defaultManager] removeItemAtURL:self.localStoreURL error:&error])
 | 
			
		||||
            err( @"Couldn't remove persistence store at URL %@: %@", self.localStoreURL, [error fullDescription] );
 | 
			
		||||
            MPError( error, @"Couldn't remove persistence store at URL %@.", self.localStoreURL );
 | 
			
		||||
 | 
			
		||||
        [self loadStore];
 | 
			
		||||
    }
 | 
			
		||||
@@ -307,7 +310,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
            fetchRequest.entity = entity;
 | 
			
		||||
            NSArray *objects = [context executeFetchRequest:fetchRequest error:&error];
 | 
			
		||||
            if (!objects) {
 | 
			
		||||
                err( @"Failed to fetch %@ objects: %@", entity, [error fullDescription] );
 | 
			
		||||
                MPError( error, @"Failed to fetch %@ objects.", entity );
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -332,7 +335,8 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
 | 
			
		||||
- (void)migrateStore {
 | 
			
		||||
 | 
			
		||||
    MPStoreMigrationLevel migrationLevel = (signed)[[NSUserDefaults standardUserDefaults] integerForKey:MPMigrationLevelLocalStoreKey];
 | 
			
		||||
    MPStoreMigrationLevel migrationLevel = (MPStoreMigrationLevel)
 | 
			
		||||
            [[NSUserDefaults standardUserDefaults] integerForKey:MPMigrationLevelLocalStoreKey];
 | 
			
		||||
    if (migrationLevel >= MPStoreMigrationLevelCurrent)
 | 
			
		||||
        // Local store up-to-date.
 | 
			
		||||
        return;
 | 
			
		||||
@@ -375,7 +379,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
    if (![NSPersistentStore migrateStore:oldLocalStoreURL withOptions:@{ STORE_OPTIONS }
 | 
			
		||||
                                 toStore:newLocalStoreURL withOptions:@{ STORE_OPTIONS }
 | 
			
		||||
                                   model:nil error:&error]) {
 | 
			
		||||
        err( @"Couldn't migrate the old store to the new location: %@", [error fullDescription] );
 | 
			
		||||
        MPError( error, @"Couldn't migrate the old store to the new location." );
 | 
			
		||||
        return NO;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -422,7 +426,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
            NSInferMappingModelAutomaticallyOption      : @YES,
 | 
			
		||||
            STORE_OPTIONS
 | 
			
		||||
    }                              model:nil 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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -459,7 +463,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
 | 
			
		||||
        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] );
 | 
			
		||||
            MPError( error, @"Failed to obtain a permanent object ID after creating new site." );
 | 
			
		||||
 | 
			
		||||
        [context saveToStore];
 | 
			
		||||
 | 
			
		||||
@@ -491,7 +495,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
 | 
			
		||||
        NSError *error = nil;
 | 
			
		||||
        if (![context obtainPermanentIDsForObjects:@[ newSite ] error:&error])
 | 
			
		||||
            err( @"Failed to obtain a permanent object ID after changing object type: %@", [error fullDescription] );
 | 
			
		||||
            MPError( error, @"Failed to obtain a permanent object ID after changing object type." );
 | 
			
		||||
 | 
			
		||||
        [context deleteObject:site];
 | 
			
		||||
        [context saveToStore];
 | 
			
		||||
@@ -537,7 +541,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
                initWithPattern:@"^#[[:space:]]*([^:]+): (.*)"
 | 
			
		||||
                        options:(NSRegularExpressionOptions)0 error:&error];
 | 
			
		||||
        if (error) {
 | 
			
		||||
            err( @"Error loading the header pattern: %@", [error fullDescription] );
 | 
			
		||||
            MPError( error, @"Error loading the header pattern." );
 | 
			
		||||
            return MPImportResultInternalError;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -551,7 +555,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
                                options:(NSRegularExpressionOptions)0 error:&error]
 | 
			
		||||
        ];
 | 
			
		||||
        if (error) {
 | 
			
		||||
            err( @"Error loading the site patterns: %@", [error fullDescription] );
 | 
			
		||||
            MPError( error, @"Error loading the site patterns." );
 | 
			
		||||
            return MPImportResultInternalError;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -610,7 +614,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
                userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", importUserName];
 | 
			
		||||
                NSArray *users = [context executeFetchRequest:userFetchRequest error:&error];
 | 
			
		||||
                if (!users) {
 | 
			
		||||
                    err( @"While looking for user: %@, error: %@", importUserName, [error fullDescription] );
 | 
			
		||||
                    MPError( error, @"While looking for user: %@.", importUserName );
 | 
			
		||||
                    return MPImportResultInternalError;
 | 
			
		||||
                }
 | 
			
		||||
                if ([users count] > 1) {
 | 
			
		||||
@@ -694,7 +698,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
 | 
			
		||||
            siteFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", siteName, user];
 | 
			
		||||
            NSArray *existingSites = [context executeFetchRequest:siteFetchRequest error:&error];
 | 
			
		||||
            if (!existingSites) {
 | 
			
		||||
                err( @"Lookup of existing sites failed for site: %@, user: %@, error: %@", siteName, user.userID, [error fullDescription] );
 | 
			
		||||
                MPError( error, @"Lookup of existing sites failed for site: %@, user: %@.", siteName, user.userID );
 | 
			
		||||
                return MPImportResultInternalError;
 | 
			
		||||
            }
 | 
			
		||||
            if ([existingSites count]) {
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
    [self.defaults registerDefaults:@{
 | 
			
		||||
            NSStringFromSelector( @selector( askForReviews ) )     : @YES,
 | 
			
		||||
 | 
			
		||||
            NSStringFromSelector( @selector( sendInfo ) )          : @NO,
 | 
			
		||||
            NSStringFromSelector( @selector( sendInfo ) )          : @YES,
 | 
			
		||||
            NSStringFromSelector( @selector( rememberLogin ) )     : @NO,
 | 
			
		||||
            NSStringFromSelector( @selector( hidePasswords ) )     : @NO,
 | 
			
		||||
            NSStringFromSelector( @selector( checkInconsistency ) ): @NO,
 | 
			
		||||
 
 | 
			
		||||
@@ -21,11 +21,11 @@
 | 
			
		||||
            @try {
 | 
			
		||||
                NSError *error = nil;
 | 
			
		||||
                if (!(success = [self save:&error]))
 | 
			
		||||
                    err( @"While saving: %@", [error fullDescription] );
 | 
			
		||||
                    MPError( error, @"While saving." );
 | 
			
		||||
            }
 | 
			
		||||
            @catch (NSException *exception) {
 | 
			
		||||
                success = NO;
 | 
			
		||||
                err( @"While saving: %@", [exception fullDescription] );
 | 
			
		||||
                err( @"While saving.\n%@", [exception fullDescription] );
 | 
			
		||||
            }
 | 
			
		||||
        }];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,9 @@
 | 
			
		||||
//  Copyright (c) 2012 Lyndir. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#import <Crashlytics/Crashlytics.h>
 | 
			
		||||
#import <Crashlytics/Answers.h>
 | 
			
		||||
 | 
			
		||||
__BEGIN_DECLS
 | 
			
		||||
extern NSString *const MPErrorDomain;
 | 
			
		||||
 | 
			
		||||
@@ -19,4 +22,21 @@ extern NSString *const MPFoundInconsistenciesNotification;
 | 
			
		||||
 | 
			
		||||
extern NSString *const MPSitesImportedNotificationUserKey;
 | 
			
		||||
extern NSString *const MPInconsistenciesFixResultUserKey;
 | 
			
		||||
 | 
			
		||||
__END_DECLS
 | 
			
		||||
 | 
			
		||||
#ifdef CRASHLYTICS
 | 
			
		||||
#define MPError(error_, message, ...) ({ \
 | 
			
		||||
    err( message @"%@%@", ##__VA_ARGS__, error_? @"\n": @"", [error_ fullDescription]?: @"" ); \
 | 
			
		||||
    \
 | 
			
		||||
    if ([[MPConfig get].sendInfo boolValue]) { \
 | 
			
		||||
        [[Crashlytics sharedInstance] recordError:error_ withAdditionalUserInfo:@{ \
 | 
			
		||||
                @"location": strf( @"%@:%d %@", @(basename((char *)__FILE__)), __LINE__, NSStringFromSelector(_cmd) ), \
 | 
			
		||||
        }]; \
 | 
			
		||||
    } \
 | 
			
		||||
})
 | 
			
		||||
#else
 | 
			
		||||
#define MPError(error_, ...) ({ \
 | 
			
		||||
    err( message @"%@%@", ##__VA_ARGS__, error_? @"\n": @"", [error_ fullDescription]?: @"" ); \
 | 
			
		||||
})
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -78,7 +78,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
 | 
			
		||||
        [[Crashlytics sharedInstance] setUserIdentifier:[PearlKeyChain deviceIdentifier]];
 | 
			
		||||
        [[Crashlytics sharedInstance] setObjectValue:[PearlKeyChain deviceIdentifier] forKey:@"deviceIdentifier"];
 | 
			
		||||
        [[Crashlytics sharedInstance] setUserName:@"Anonymous"];
 | 
			
		||||
        [[Crashlytics sharedInstance] setObjectValue:@"Anonymous" forKey:@"username"];
 | 
			
		||||
        [Crashlytics startWithAPIKey:crashlyticsAPIKey];
 | 
			
		||||
        [[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
 | 
			
		||||
            PearlLogLevel level = PearlLogLevelInfo;
 | 
			
		||||
@@ -272,7 +271,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
 | 
			
		||||
    [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:
 | 
			
		||||
            ^(NSData *importedSitesData, NSURLResponse *response, NSError *error) {
 | 
			
		||||
                if (error)
 | 
			
		||||
                    err( @"While reading imported sites from %@: %@", url, [error fullDescription] );
 | 
			
		||||
                    MPError( error, @"While reading imported sites from %@.", url );
 | 
			
		||||
                if (!importedSitesData)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
@@ -394,9 +393,9 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
 | 
			
		||||
                                                              inManagedObjectContext:moc];
 | 
			
		||||
        newUser.name = name;
 | 
			
		||||
        [moc saveToStore];
 | 
			
		||||
        NSError *error = nil;
 | 
			
		||||
        if (![moc obtainPermanentIDsForObjects:@[ newUser ] error:&error])
 | 
			
		||||
            err( @"Failed to obtain permanent object ID for new user: %@", [error fullDescription] );
 | 
			
		||||
//        NSError *error = nil;
 | 
			
		||||
//        if (![moc obtainPermanentIDsForObjects:@[ newUser ] error:&error])
 | 
			
		||||
//            MPError( error, @"Failed to obtain permanent object ID for new user." );
 | 
			
		||||
 | 
			
		||||
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
 | 
			
		||||
            [self updateUsers];
 | 
			
		||||
@@ -516,20 +515,23 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
 | 
			
		||||
 | 
			
		||||
    NSError *coordinateError = nil;
 | 
			
		||||
    NSString *exportedSites = [self exportSitesRevealPasswords:revealPasswords];
 | 
			
		||||
    [[[NSFileCoordinator alloc] initWithFilePresenter:nil] coordinateWritingItemAtURL:savePanel.URL options:0 error:&coordinateError
 | 
			
		||||
                                                                           byAccessor:^(NSURL *newURL) {
 | 
			
		||||
                                                                               NSError *writeError = nil;
 | 
			
		||||
                                                                               if (![exportedSites writeToURL:newURL atomically:NO
 | 
			
		||||
                                                                                                     encoding:NSUTF8StringEncoding
 | 
			
		||||
                                                                                                        error:&writeError])
 | 
			
		||||
                                                                                   PearlMainQueue( ^{
 | 
			
		||||
                                                                                       [[NSAlert alertWithError:writeError] runModal];
 | 
			
		||||
                                                                                   } );
 | 
			
		||||
                                                                           }];
 | 
			
		||||
    if (coordinateError)
 | 
			
		||||
    [[[NSFileCoordinator alloc] initWithFilePresenter:nil] coordinateWritingItemAtURL:savePanel.URL options:0
 | 
			
		||||
                                                                                error:&coordinateError byAccessor:
 | 
			
		||||
                    ^(NSURL *newURL) {
 | 
			
		||||
                        NSError *writeError = nil;
 | 
			
		||||
                        if (![exportedSites writeToURL:newURL atomically:NO encoding:NSUTF8StringEncoding error:&writeError])
 | 
			
		||||
                            MPError( writeError, @"Could not write to the export file." );
 | 
			
		||||
 | 
			
		||||
                        PearlMainQueue( ^{
 | 
			
		||||
                            [[NSAlert alertWithError:writeError] runModal];
 | 
			
		||||
                        } );
 | 
			
		||||
                    }];
 | 
			
		||||
    if (coordinateError) {
 | 
			
		||||
        MPError( coordinateError, @"Write access to the export file could not be obtained." );
 | 
			
		||||
        PearlMainQueue( ^{
 | 
			
		||||
            [[NSAlert alertWithError:coordinateError] runModal];
 | 
			
		||||
        } );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)updateUsers {
 | 
			
		||||
@@ -567,7 +569,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
 | 
			
		||||
    fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
 | 
			
		||||
    NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
 | 
			
		||||
    if (!users)
 | 
			
		||||
        err( @"Failed to load users: %@", [error fullDescription] );
 | 
			
		||||
        MPError( error, @"Failed to load users." );
 | 
			
		||||
 | 
			
		||||
    if (![users count]) {
 | 
			
		||||
        NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""];
 | 
			
		||||
 
 | 
			
		||||
@@ -555,7 +555,7 @@
 | 
			
		||||
        NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error];
 | 
			
		||||
        if (!siteResults) {
 | 
			
		||||
            prof_finish( @"executeFetchRequest: %@ // %@", fetchRequest.predicate, [error fullDescription] );
 | 
			
		||||
            err( @"While fetching sites for completion: %@", [error fullDescription] );
 | 
			
		||||
            MPError( error, @"While fetching sites for completion." );
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        prof_rewind( @"executeFetchRequest: %@", fetchRequest.predicate );
 | 
			
		||||
 
 | 
			
		||||
@@ -115,7 +115,7 @@
 | 
			
		||||
    NSError *error;
 | 
			
		||||
    MPSiteEntity *entity = (MPSiteEntity *)[moc existingObjectWithID:_entityOID error:&error];
 | 
			
		||||
    if (!entity)
 | 
			
		||||
        err( @"Couldn't retrieve active site: %@", [error fullDescription] );
 | 
			
		||||
        MPError( error, @"Couldn't retrieve active site." );
 | 
			
		||||
 | 
			
		||||
    return entity;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -333,12 +333,12 @@
 | 
			
		||||
        question.keyword = keyword;
 | 
			
		||||
 | 
			
		||||
        if ([context saveToStore]) {
 | 
			
		||||
            if ([question.objectID isTemporaryID]) {
 | 
			
		||||
                NSError *error = nil;
 | 
			
		||||
                [context obtainPermanentIDsForObjects:@[ question ] error:&error];
 | 
			
		||||
                if (error)
 | 
			
		||||
                    err( @"Failed to obtain permanent object ID: %@", [error fullDescription] );
 | 
			
		||||
            }
 | 
			
		||||
//            if ([question.objectID isTemporaryID]) {
 | 
			
		||||
//                NSError *error = nil;
 | 
			
		||||
//                [context obtainPermanentIDsForObjects:@[ question ] error:&error];
 | 
			
		||||
//                if (error)
 | 
			
		||||
//                    MPError( error, @"Failed to obtain permanent object ID: %@" );
 | 
			
		||||
//            }
 | 
			
		||||
 | 
			
		||||
            _questionOID = question.objectID;
 | 
			
		||||
            [self updateAnswerForQuestion:question ofSite:site];
 | 
			
		||||
 
 | 
			
		||||
@@ -395,7 +395,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
 | 
			
		||||
        self.fetchedResultsController.fetchRequest.predicate =
 | 
			
		||||
                [NSPredicate predicateWithFormat:@"name LIKE[cd] %@ AND user == %@", queryPattern, [MPiOSAppDelegate get].activeUserOID];
 | 
			
		||||
        if (![self.fetchedResultsController performFetch:&error])
 | 
			
		||||
            err( @"Couldn't fetch sites: %@", [error fullDescription] );
 | 
			
		||||
            MPError( error, @"Couldn't fetch sites." );
 | 
			
		||||
 | 
			
		||||
        PearlMainQueue( ^{
 | 
			
		||||
            [self.passwordCollectionView updateDataSource:_passwordCollectionSections
 | 
			
		||||
 
 | 
			
		||||
@@ -39,6 +39,8 @@
 | 
			
		||||
@interface MPStoreProductCell : UITableViewCell
 | 
			
		||||
 | 
			
		||||
@property(nonatomic) IBOutlet UILabel *priceLabel;
 | 
			
		||||
@property(nonatomic) IBOutlet UILabel *titleLabel;
 | 
			
		||||
@property(nonatomic) IBOutlet UILabel *descriptionLabel;
 | 
			
		||||
@property(nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
 | 
			
		||||
@property(nonatomic) IBOutlet UIView *purchasedIndicator;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ PearlEnum( MPDevelopmentFuelConsumption,
 | 
			
		||||
@interface MPStoreViewController()<MPInAppDelegate>
 | 
			
		||||
 | 
			
		||||
@property(nonatomic, strong) NSNumberFormatter *currencyFormatter;
 | 
			
		||||
@property(nonatomic, strong) NSArray *products;
 | 
			
		||||
@property(nonatomic, strong) NSDictionary<NSString *, SKProduct *> *products;
 | 
			
		||||
 | 
			
		||||
@end
 | 
			
		||||
 | 
			
		||||
@@ -43,7 +43,7 @@ PearlEnum( MPDevelopmentFuelConsumption,
 | 
			
		||||
    ];
 | 
			
		||||
    NSInteger storeVersion = [[NSUserDefaults standardUserDefaults] integerForKey:@"storeVersion"];
 | 
			
		||||
    for (; storeVersion < [storeVersions count]; ++storeVersion)
 | 
			
		||||
        [features appendFormat:@"%@\n", storeVersions[storeVersion]];
 | 
			
		||||
        [features appendFormat:@"%@\n", storeVersions[(NSUInteger)storeVersion]];
 | 
			
		||||
    if (![features length])
 | 
			
		||||
        return nil;
 | 
			
		||||
 | 
			
		||||
@@ -170,7 +170,7 @@ PearlEnum( MPDevelopmentFuelConsumption,
 | 
			
		||||
 | 
			
		||||
#pragma mark - MPInAppDelegate
 | 
			
		||||
 | 
			
		||||
- (void)updateWithProducts:(NSArray *)products {
 | 
			
		||||
- (void)updateWithProducts:(NSDictionary<NSString *, SKProduct *> *)products {
 | 
			
		||||
 | 
			
		||||
    self.products = products;
 | 
			
		||||
 | 
			
		||||
@@ -218,7 +218,7 @@ PearlEnum( MPDevelopmentFuelConsumption,
 | 
			
		||||
 | 
			
		||||
- (SKProduct *)productForCell:(MPStoreProductCell *)cell {
 | 
			
		||||
 | 
			
		||||
    for (SKProduct *product in self.products)
 | 
			
		||||
    for (SKProduct *product in [self.products allValues])
 | 
			
		||||
        if ([self cellForProductIdentifier:product.productIdentifier] == cell)
 | 
			
		||||
            return product;
 | 
			
		||||
 | 
			
		||||
@@ -248,7 +248,7 @@ PearlEnum( MPDevelopmentFuelConsumption,
 | 
			
		||||
    [hideCells addObjectsFromArray:[self.allCellsBySection[0] array]];
 | 
			
		||||
    [hideCells addObject:self.loadingCell];
 | 
			
		||||
 | 
			
		||||
    for (SKProduct *product in self.products) {
 | 
			
		||||
    for (SKProduct *product in [self.products allValues]) {
 | 
			
		||||
        [self showCellForProductWithIdentifier:MPProductGenerateLogins ifProduct:product showingCells:showCells];
 | 
			
		||||
        [self showCellForProductWithIdentifier:MPProductGenerateAnswers ifProduct:product showingCells:showCells];
 | 
			
		||||
        [self showCellForProductWithIdentifier:MPProductOSIntegration ifProduct:product showingCells:showCells];
 | 
			
		||||
@@ -313,6 +313,8 @@ PearlEnum( MPDevelopmentFuelConsumption,
 | 
			
		||||
    BOOL purchased = [[MPiOSAppDelegate get] isFeatureUnlocked:productIdentifier];
 | 
			
		||||
    NSInteger quantity = [self quantityForProductIdentifier:productIdentifier];
 | 
			
		||||
    cell.priceLabel.text = purchased? @"": [self.currencyFormatter stringFromNumber:@([product.price floatValue] * quantity)];
 | 
			
		||||
    cell.titleLabel.text = product.localizedTitle;
 | 
			
		||||
    cell.descriptionLabel.text = product.localizedDescription;
 | 
			
		||||
    cell.purchasedIndicator.visible = purchased;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,7 @@
 | 
			
		||||
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
//==============================================================================
 | 
			
		||||
 | 
			
		||||
#import <Crashlytics/Answers.h>
 | 
			
		||||
#import "MPUsersViewController.h"
 | 
			
		||||
#import "MPEntities.h"
 | 
			
		||||
#import "MPAvatarCell.h"
 | 
			
		||||
@@ -224,6 +225,17 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
 | 
			
		||||
                        user.defaultType = user.algorithm.defaultType;
 | 
			
		||||
                        user.avatar = newUserAvatar;
 | 
			
		||||
                        user.name = newUserName;
 | 
			
		||||
 | 
			
		||||
                        if ([[MPConfig get].sendInfo boolValue]) {
 | 
			
		||||
#ifdef CRASHLYTICS
 | 
			
		||||
                            [Answers logSignUpWithMethod:@"Manual"
 | 
			
		||||
                                                 success:@YES
 | 
			
		||||
                                        customAttributes:@{
 | 
			
		||||
                                                @"algorithm": @(user.algorithm.version),
 | 
			
		||||
                                                @"avatar"   : @(user.avatar),
 | 
			
		||||
                                        }];
 | 
			
		||||
#endif
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context
 | 
			
		||||
@@ -719,7 +731,7 @@ referenceSizeForFooterInSection:(NSInteger)section {
 | 
			
		||||
            ];
 | 
			
		||||
            NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
 | 
			
		||||
            if (!users) {
 | 
			
		||||
                err( @"Failed to load users: %@", [error fullDescription] );
 | 
			
		||||
                MPError( error, @"Failed to load users." );
 | 
			
		||||
                self.userIDs = nil;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,6 @@
 | 
			
		||||
 | 
			
		||||
@property(nonatomic, strong) UIDocumentInteractionController *interactionController;
 | 
			
		||||
 | 
			
		||||
@property(nonatomic) UIBackgroundTaskIdentifier task;
 | 
			
		||||
@end
 | 
			
		||||
 | 
			
		||||
@implementation MPiOSAppDelegate
 | 
			
		||||
@@ -59,10 +58,9 @@
 | 
			
		||||
            [[Crashlytics sharedInstance] setUserIdentifier:[PearlKeyChain deviceIdentifier]];
 | 
			
		||||
            [[Crashlytics sharedInstance] setObjectValue:[PearlKeyChain deviceIdentifier] forKey:@"deviceIdentifier"];
 | 
			
		||||
            [[Crashlytics sharedInstance] setUserName:@"Anonymous"];
 | 
			
		||||
            [[Crashlytics sharedInstance] setObjectValue:@"Anonymous" forKey:@"username"];
 | 
			
		||||
            [Crashlytics startWithAPIKey:crashlyticsAPIKey];
 | 
			
		||||
            [[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
 | 
			
		||||
                PearlLogLevel level = PearlLogLevelInfo;
 | 
			
		||||
                PearlLogLevel level = PearlLogLevelWarn;
 | 
			
		||||
                if ([[MPConfig get].sendInfo boolValue])
 | 
			
		||||
                    level = PearlLogLevelDebug;
 | 
			
		||||
 | 
			
		||||
@@ -83,9 +81,6 @@
 | 
			
		||||
        PearlAddNotificationObserver( MPCheckConfigNotification, nil, [NSOperationQueue mainQueue], ^(id self, NSNotification *note) {
 | 
			
		||||
            [self updateConfigKey:note.object];
 | 
			
		||||
        } );
 | 
			
		||||
//      PearlAddNotificationObserver( kIASKAppSettingChanged, nil, nil, ^(id self, NSNotification *note) {
 | 
			
		||||
//          [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:note.object];
 | 
			
		||||
//      } );
 | 
			
		||||
        PearlAddNotificationObserver( NSUserDefaultsDidChangeNotification, nil, nil, ^(id self, NSNotification *note) {
 | 
			
		||||
            [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
 | 
			
		||||
        } );
 | 
			
		||||
@@ -155,7 +150,8 @@
 | 
			
		||||
    [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:
 | 
			
		||||
            ^(NSData *importedSitesData, NSURLResponse *response, NSError *error) {
 | 
			
		||||
                if (error)
 | 
			
		||||
                    err( @"While reading imported sites from %@: %@", url, [error fullDescription] );
 | 
			
		||||
                    MPError( error, @"While reading imported sites from %@.", url );
 | 
			
		||||
 | 
			
		||||
                if (!importedSitesData) {
 | 
			
		||||
                    [PearlAlert showError:strf( @"Master Password couldn't read the import sites.\n\n%@",
 | 
			
		||||
                            [error localizedDescription]?: error )];
 | 
			
		||||
@@ -483,7 +479,7 @@
 | 
			
		||||
                     NSError *error = nil;
 | 
			
		||||
                     if (![[exportedSites dataUsingEncoding:NSUTF8StringEncoding]
 | 
			
		||||
                             writeToURL:exportURL options:NSDataWritingFileProtectionComplete error:&error])
 | 
			
		||||
                         err( @"Failed to write export data to URL %@: %@", exportURL, [error fullDescription] );
 | 
			
		||||
                         MPError( error, @"Failed to write export data to URL %@.", exportURL );
 | 
			
		||||
                     else {
 | 
			
		||||
                         self.interactionController = [UIDocumentInteractionController interactionControllerWithURL:exportURL];
 | 
			
		||||
                         self.interactionController.UTI = @"com.lyndir.masterpassword.sites";
 | 
			
		||||
@@ -574,7 +570,7 @@
 | 
			
		||||
    static NSDictionary *crashlyticsInfo = nil;
 | 
			
		||||
    if (crashlyticsInfo == nil)
 | 
			
		||||
        crashlyticsInfo = [[NSDictionary alloc] initWithContentsOfURL:
 | 
			
		||||
                [[NSBundle mainBundle] URLForResource:@"Crashlytics" withExtension:@"plist"]];
 | 
			
		||||
                [[NSBundle mainBundle] URLForResource:@"Fabric" withExtension:@"plist"]];
 | 
			
		||||
 | 
			
		||||
    return crashlyticsInfo;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,7 @@
 | 
			
		||||
	<key>LSRequiresIPhoneOS</key>
 | 
			
		||||
	<true/>
 | 
			
		||||
	<key>NSHumanReadableCopyright</key>
 | 
			
		||||
	<string>© 2011-2016 Lyndir</string>
 | 
			
		||||
	<string>© 2011-2017</string>
 | 
			
		||||
	<key>UIAppFonts</key>
 | 
			
		||||
	<array>
 | 
			
		||||
		<string>Exo2.0-Bold.otf</string>
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
	<array>
 | 
			
		||||
		<dict>
 | 
			
		||||
			<key>FooterText</key>
 | 
			
		||||
			<string>Enable this setting to send us carefully anonymized information to help us diagnose and resolve issues you might experience in the future.</string>
 | 
			
		||||
			<string>Enable this setting to send carefully anonymized information to help us diagnose and resolve any issues you encounter in a future update.</string>
 | 
			
		||||
			<key>Title</key>
 | 
			
		||||
			<string></string>
 | 
			
		||||
			<key>Type</key>
 | 
			
		||||
@@ -50,7 +50,7 @@
 | 
			
		||||
			<key>Key</key>
 | 
			
		||||
			<string>sendInfo</string>
 | 
			
		||||
			<key>DefaultValue</key>
 | 
			
		||||
			<false/>
 | 
			
		||||
			<true/>
 | 
			
		||||
		</dict>
 | 
			
		||||
		<dict>
 | 
			
		||||
			<key>FooterText</key>
 | 
			
		||||
 
 | 
			
		||||
@@ -2620,8 +2620,10 @@ See </string>
 | 
			
		||||
                                        <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
 | 
			
		||||
                                        <connections>
 | 
			
		||||
                                            <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="purchasedIndicator" destination="FWu-V6-mLT" id="Zqt-GG-e5v"/>
 | 
			
		||||
                                            <outlet property="titleLabel" destination="Pax-1J-IZi" id="kVH-n6-mDs"/>
 | 
			
		||||
                                        </connections>
 | 
			
		||||
                                    </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">
 | 
			
		||||
@@ -2685,8 +2687,10 @@ See </string>
 | 
			
		||||
                                        <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="descriptionLabel" destination="yRH-27-edZ" id="pfv-na-UM3"/>
 | 
			
		||||
                                            <outlet property="priceLabel" destination="9ct-IM-QKR" id="UTQ-L7-vbu"/>
 | 
			
		||||
                                            <outlet property="purchasedIndicator" destination="N9y-ue-L8d" id="Ppv-ay-M1J"/>
 | 
			
		||||
                                            <outlet property="titleLabel" destination="Kkl-gT-YAO" id="cHy-iU-oHr"/>
 | 
			
		||||
                                        </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">
 | 
			
		||||
@@ -2750,8 +2754,10 @@ See </string>
 | 
			
		||||
                                        <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="descriptionLabel" destination="riF-bB-x5g" id="3v9-6H-PAK"/>
 | 
			
		||||
                                            <outlet property="priceLabel" destination="3jH-eX-9N2" id="agT-az-dVU"/>
 | 
			
		||||
                                            <outlet property="purchasedIndicator" destination="ec8-P9-KPY" id="M39-bc-Ksp"/>
 | 
			
		||||
                                            <outlet property="titleLabel" destination="Zch-DS-J3I" id="cY2-FQ-q69"/>
 | 
			
		||||
                                        </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">
 | 
			
		||||
@@ -2815,8 +2821,10 @@ See </string>
 | 
			
		||||
                                        <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="descriptionLabel" destination="Yxc-Lr-382" id="47I-83-lgP"/>
 | 
			
		||||
                                            <outlet property="priceLabel" destination="ZGg-O6-rsg" id="dAn-xu-gut"/>
 | 
			
		||||
                                            <outlet property="purchasedIndicator" destination="yZX-ns-8oV" id="7x0-eq-oSs"/>
 | 
			
		||||
                                            <outlet property="titleLabel" destination="e1D-jp-GBs" id="jFw-dm-vp6"/>
 | 
			
		||||
                                        </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">
 | 
			
		||||
@@ -2908,7 +2916,9 @@ 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"/>
 | 
			
		||||
                                        <connections>
 | 
			
		||||
                                            <outlet property="activityIndicator" destination="eS4-59-Xny" id="kGW-fn-VqH"/>
 | 
			
		||||
                                            <outlet property="descriptionLabel" destination="fz2-AO-aGW" id="xkc-xV-Z6Y"/>
 | 
			
		||||
                                            <outlet property="priceLabel" destination="EbU-DV-fKF" id="pg2-8o-7We"/>
 | 
			
		||||
                                            <outlet property="titleLabel" destination="Jnv-uN-xeg" id="Kxi-RE-ipa"/>
 | 
			
		||||
                                        </connections>
 | 
			
		||||
                                    </tableViewCell>
 | 
			
		||||
                                </cells>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user