2
0

Convert store into template cells for products.

This commit is contained in:
Maarten Billemont 2017-04-30 17:48:03 -04:00
parent 834e94ebd5
commit cf2c30cfe6
7 changed files with 432 additions and 713 deletions

@ -1 +1 @@
Subproject commit 284cd041f8d48301044cf85a7ac4a9abf030e170 Subproject commit 1a3e1ea0379fe8d0f1b06ce98cd6394654679682

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:(NSDictionary<NSString *, 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

@ -27,6 +27,16 @@ 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)
@ -34,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];
} }
@ -108,18 +118,6 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
if (payment) { if (payment) {
payment.quantity = quantity; payment.quantity = quantity;
[[self paymentQueue] addPayment:payment]; [[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; return;
} }
@ -138,7 +136,7 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
self.products = products; 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 {
@ -166,6 +164,7 @@ 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 );
@ -190,9 +189,10 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
if ([[MPConfig get].sendInfo boolValue]) { if ([[MPConfig get].sendInfo boolValue]) {
#ifdef CRASHLYTICS #ifdef CRASHLYTICS
SKProduct *product = self.products[transaction.payment.productIdentifier]; SKProduct *product = self.products[transaction.payment.productIdentifier];
[Answers logPurchaseWithPrice:product.price currency:product.priceLocale.currencyCode success:@YES for (int q = 0; q < transaction.payment.quantity; ++q)
itemName:product.localizedTitle itemType:@"InApp" itemId:product.productIdentifier [Answers logPurchaseWithPrice:product.price currency:[product.priceLocale objectForKey:NSLocaleCurrencyCode]
customAttributes:attributes]; success:@YES itemName:product.localizedTitle itemType:@"InApp"
itemId:product.productIdentifier customAttributes:attributes];
#endif #endif
} }
break; break;
@ -208,28 +208,33 @@ 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]) { if ([[MPConfig get].sendInfo boolValue]) {
#ifdef CRASHLYTICS #ifdef CRASHLYTICS
SKProduct *product = self.products[transaction.payment.productIdentifier]; SKProduct *product = self.products[transaction.payment.productIdentifier];
[Answers logPurchaseWithPrice:product.price currency:product.priceLocale.currencyCode success:@YES [Answers logPurchaseWithPrice:product.price currency:[product.priceLocale objectForKey:NSLocaleCurrencyCode]
itemName:product.localizedTitle itemType:@"InApp" itemId:product.productIdentifier success:@NO itemName:product.localizedTitle itemType:@"InApp" itemId:product.productIdentifier
customAttributes:@{ customAttributes:@{
@"state" : @"Failed", @"state" : @"Failed",
@"reason": [transaction.error fullDescription], @"quantity": @(transaction.payment.quantity),
@"reason" : [transaction.error localizedFailureReason]?: [transaction.error localizedDescription],
}]; }];
#endif #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 {

View File

@ -28,7 +28,7 @@
@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) {
@ -38,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
@ -47,7 +47,7 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
(__bridge id)kSecAttrService : @"Saved Master Password", (__bridge id)kSecAttrService : @"Saved Master Password",
(__bridge id)kSecAttrAccount : user.name?: @"", (__bridge id)kSecAttrAccount : user.name?: @"",
(__bridge id)kSecAttrAccessControl : accessControl, (__bridge id)kSecAttrAccessControl : accessControl,
(__bridge id)kSecUseAuthenticationUI : (__bridge id)kSecUseAuthenticationUIAllow, (__bridge id)kSecUseAuthenticationUI: (__bridge id)kSecUseAuthenticationUIAllow,
(__bridge id)kSecUseOperationPrompt : (__bridge id)kSecUseOperationPrompt :
strf( @"Access %@'s master password.", user.name ), strf( @"Access %@'s master password.", user.name ),
} }
@ -60,10 +60,10 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
attributes:@{ attributes:@{
(__bridge id)kSecAttrService: @"Saved Master Password", (__bridge id)kSecAttrService : @"Saved Master Password",
(__bridge id)kSecAttrAccount: user.name?: @"", (__bridge id)kSecAttrAccount : user.name?: @"",
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
(__bridge id)kSecAttrAccessible : (__bridge id)(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly?: kSecAttrAccessibleWhenUnlockedThisDeviceOnly), (__bridge id)kSecAttrAccessible: (__bridge id)(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly?: kSecAttrAccessibleWhenUnlockedThisDeviceOnly),
#endif #endif
} }
matches:nil]; matches:nil];
@ -72,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:
@ -100,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 }];
} }
} }
@ -108,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 );

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;
@ -44,4 +35,17 @@
@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) NSDictionary<NSString *, SKProduct *> *products;
@property(nonatomic, strong) NSDictionary<NSString *, SKPaymentTransaction *> *transactions;
@property(nonatomic, strong) NSMutableArray<NSArray *> *dataSource;
@end @end
@ -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
- (MPStoreProductCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
MPStoreProductCell *cell = (MPStoreProductCell *)[super tableView:tableView cellForRowAtIndexPath:indexPath]; return [self.dataSource count];
if (indexPath.section == 0)
cell.selectionStyle = [[MPiOSAppDelegate get] isFeatureUnlocked:[self productForCell:cell].productIdentifier]?
UITableViewCellSelectionStyleNone: UITableViewCellSelectionStyleDefault;
if (cell.selectionStyle != UITableViewCellSelectionStyleNone) {
cell.selectedBackgroundView = [[UIView alloc] initWithFrame:cell.bounds];
cell.selectedBackgroundView.backgroundColor = [UIColor colorWithRGBAHex:0x78DDFB33];
}
return cell;
} }
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return NO; return [self.dataSource[(NSUInteger)section] count];
}
- (MPStoreProductCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
id content = self.dataSource[(NSUInteger)indexPath.section][(NSUInteger)indexPath.row];
if ([content isKindOfClass:[SKProduct class]]) {
SKProduct *product = content;
MPStoreProductCell *cell;
if ([product.productIdentifier isEqualToString:MPProductFuel])
cell = [MPStoreFuelProductCell dequeueCellFromTableView:tableView indexPath:indexPath];
else
cell = [MPStoreProductCell dequeueCellFromTableView:tableView indexPath:indexPath];
[cell updateWithProduct:product transaction:self.transactions[product.productIdentifier]];
return cell;
}
return [tableView dequeueReusableCellWithIdentifier:content forIndexPath:indexPath];
} }
#pragma mark - UITableViewDelegate #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 {
@ -170,36 +165,28 @@ PearlEnum( MPDevelopmentFuelConsumption,
#pragma mark - MPInAppDelegate #pragma mark - MPInAppDelegate
- (void)updateWithProducts:(NSDictionary<NSString *, SKProduct *> *)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,63 @@ PearlEnum( MPDevelopmentFuelConsumption,
return nil; return nil;
} }
- (SKProduct *)productForCell:(MPStoreProductCell *)cell { @end
for (SKProduct *product in [self.products allValues]) @implementation MPStoreProductCell
if ([self cellForProductIdentifier:product.productIdentifier] == cell)
return product;
return nil; - (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;
if (transaction && (transaction.transactionState == SKPaymentTransactionStateDeferred ||
transaction.transactionState == SKPaymentTransactionStatePurchasing))
[self.activityIndicator startAnimating];
else
[self.activityIndicator stopAnimating];
} }
- (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 allValues]) {
[self showCellForProductWithIdentifier:MPProductGenerateLogins ifProduct:product showingCells:showCells];
[self showCellForProductWithIdentifier:MPProductGenerateAnswers ifProduct:product showingCells:showCells];
[self showCellForProductWithIdentifier:MPProductOSIntegration ifProduct:product showingCells:showCells];
[self showCellForProductWithIdentifier:MPProductTouchID ifProduct:product showingCells:showCells];
[self showCellForProductWithIdentifier:MPProductFuel ifProduct:product showingCells:showCells];
}
[hideCells removeObjectsInArray:showCells];
[self updateCellsHiding:hideCells showing:showCells];
} }
- (void)updateFuel { @end
@implementation MPStoreFuelProductCell
- (void)updateWithProduct:(SKProduct *)product transaction:(SKPaymentTransaction *)transaction {
[super updateWithProduct:product transaction:transaction];
CGFloat weeklyFuelConsumption = [self weeklyFuelConsumption]; /* consume x fuel / week */ 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,56 +267,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";
case MPDevelopmentFuelConsumptionMonthly:
return @"1h / month";
case MPDevelopmentFuelWeekly:
return @"1h / week";
}
MPStoreProductCell *cell = [self cellForProductIdentifier:productIdentifier]; return nil;
[showCells addObject:cell];
self.currencyFormatter.locale = product.priceLocale;
BOOL purchased = [[MPiOSAppDelegate get] isFeatureUnlocked:productIdentifier];
NSInteger quantity = [self quantityForProductIdentifier:productIdentifier];
cell.priceLabel.text = purchased? @"": [self.currencyFormatter stringFromNumber:@([product.price floatValue] * quantity)];
cell.titleLabel.text = product.localizedTitle;
cell.descriptionLabel.text = product.localizedDescription;
cell.purchasedIndicator.visible = purchased;
}
- (NSInteger)quantityForProductIdentifier:(NSString *)productIdentifier {
if ([productIdentifier isEqualToString:MPProductFuel])
return (NSInteger)(MP_FUEL_HOURLY_RATE * [self weeklyFuelConsumption] + .5f);
return 1;
} }
@end @end
@implementation MPStoreProductCell
@end

View File

@ -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="250" 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="250" 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"/>
@ -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.5" width="355" height="44"/>
<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="32"/>
<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="25"/>
<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="32" 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"/>
@ -1611,7 +1610,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">
@ -2550,31 +2549,29 @@ 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"/>
@ -2592,7 +2589,7 @@ See </string>
<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="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@ -2626,212 +2623,11 @@ See </string>
<outlet property="titleLabel" destination="Pax-1J-IZi" id="kVH-n6-mDs"/> <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="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">
<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="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">
<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="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">
<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">
@ -2841,14 +2637,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"/>
@ -2857,7 +2647,7 @@ 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"/>
@ -2882,9 +2672,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"/>
@ -2917,93 +2707,91 @@ invested: 3.7 work hours</string>
<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="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="titleLabel" destination="Jnv-uN-xeg" id="Kxi-RE-ipa"/> <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"/>
@ -3012,17 +2800,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>
@ -3327,11 +3104,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"/>
@ -3340,49 +3117,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 <mutableString 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.</mutableString>
<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"/>
@ -3390,14 +3131,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>
@ -3528,10 +3261,7 @@ You can then update your site's account with the new and stronger password.
<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="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_login.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"/>