Fixed key disappearing from NSCache after suspension and not being reloaded from keychain.
This commit is contained in:
		
							
								
								
									
										2
									
								
								platform-darwin/External/Pearl
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								platform-darwin/External/Pearl
									
									
									
									
										vendored
									
									
								
							 Submodule platform-darwin/External/Pearl updated: 3ceb601ba4...6f3efd7abd
									
								
							@@ -430,58 +430,38 @@ NSOperationQueue *_mpwQueue = nil;
 | 
			
		||||
 | 
			
		||||
- (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
 | 
			
		||||
 | 
			
		||||
    dispatch_group_t group = dispatch_group_create();
 | 
			
		||||
    dispatch_group_enter( group );
 | 
			
		||||
    __block NSString *result = nil;
 | 
			
		||||
    [self resolveLoginForSite:site usingKey:siteKey result:^(NSString *result_) {
 | 
			
		||||
        result = result_;
 | 
			
		||||
        dispatch_group_leave( group );
 | 
			
		||||
    }];
 | 
			
		||||
    dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
    return PearlAwait( ^(void (^setResult)(id)) {
 | 
			
		||||
        [self resolveLoginForSite:site usingKey:siteKey result:^(NSString *result_) {
 | 
			
		||||
            setResult( result_ );
 | 
			
		||||
        }];
 | 
			
		||||
    } );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
 | 
			
		||||
 | 
			
		||||
    dispatch_group_t group = dispatch_group_create();
 | 
			
		||||
    dispatch_group_enter( group );
 | 
			
		||||
    __block NSString *result = nil;
 | 
			
		||||
    [self resolvePasswordForSite:site usingKey:siteKey result:^(NSString *result_) {
 | 
			
		||||
        result = result_;
 | 
			
		||||
        dispatch_group_leave( group );
 | 
			
		||||
    }];
 | 
			
		||||
    dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
    return PearlAwait( ^(void (^setResult)(id)) {
 | 
			
		||||
        [self resolvePasswordForSite:site usingKey:siteKey result:^(NSString *result_) {
 | 
			
		||||
            setResult( result_ );
 | 
			
		||||
        }];
 | 
			
		||||
    } );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
 | 
			
		||||
 | 
			
		||||
    dispatch_group_t group = dispatch_group_create();
 | 
			
		||||
    dispatch_group_enter( group );
 | 
			
		||||
    __block NSString *result = nil;
 | 
			
		||||
    [self resolveAnswerForSite:site usingKey:siteKey result:^(NSString *result_) {
 | 
			
		||||
        result = result_;
 | 
			
		||||
        dispatch_group_leave( group );
 | 
			
		||||
    }];
 | 
			
		||||
    dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
    return PearlAwait( ^(void (^setResult)(id)) {
 | 
			
		||||
        [self resolveAnswerForSite:site usingKey:siteKey result:^(NSString *result_) {
 | 
			
		||||
            setResult( result_ );
 | 
			
		||||
        }];
 | 
			
		||||
    } );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey {
 | 
			
		||||
 | 
			
		||||
    dispatch_group_t group = dispatch_group_create();
 | 
			
		||||
    dispatch_group_enter( group );
 | 
			
		||||
    __block NSString *result = nil;
 | 
			
		||||
    [self resolveAnswerForQuestion:question usingKey:siteKey result:^(NSString *result_) {
 | 
			
		||||
        result = result_;
 | 
			
		||||
        dispatch_group_leave( group );
 | 
			
		||||
    }];
 | 
			
		||||
    dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
    return PearlAwait( ^(void (^setResult)(id)) {
 | 
			
		||||
        [self resolveAnswerForQuestion:question usingKey:siteKey result:^(NSString *result_) {
 | 
			
		||||
            setResult( result_ );
 | 
			
		||||
        }];
 | 
			
		||||
    } );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
 | 
			
		||||
 
 | 
			
		||||
@@ -72,14 +72,20 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
 | 
			
		||||
 | 
			
		||||
    MPKeyOrigin keyOrigin;
 | 
			
		||||
    NSDictionary *keyQuery = createKeyQuery( user, NO, &keyOrigin );
 | 
			
		||||
    NSData *keyData = [PearlKeyChain dataOfItemForQuery:keyQuery];
 | 
			
		||||
    if (!keyData) {
 | 
			
		||||
        inf( @"No key found in keychain for user: %@", user.userID );
 | 
			
		||||
        return nil;
 | 
			
		||||
    }
 | 
			
		||||
    id<MPAlgorithm> keyAlgorithm = user.algorithm;
 | 
			
		||||
    MPKey *key = [[MPKey alloc] initForFullName:user.name withKeyResolver:^NSData *(id<MPAlgorithm> algorithm) {
 | 
			
		||||
        return ![algorithm isEqual:keyAlgorithm]? nil:
 | 
			
		||||
               PearlMainQueueAwait( (id)^{
 | 
			
		||||
                   return [PearlKeyChain dataOfItemForQuery:keyQuery];
 | 
			
		||||
               } );
 | 
			
		||||
    }                                 keyOrigin:keyOrigin];
 | 
			
		||||
 | 
			
		||||
    inf( @"Found key in keychain for user: %@", user.userID );
 | 
			
		||||
    return [[MPKey alloc] initForFullName:user.name withKeyData:keyData forAlgorithm:user.algorithm keyOrigin:keyOrigin];
 | 
			
		||||
    if ([key keyIDForAlgorithm:user.algorithm])
 | 
			
		||||
        inf( @"Found key in keychain for user: %@", user.userID );
 | 
			
		||||
    else
 | 
			
		||||
        inf( @"No key found in keychain for user: %@", user.userID );
 | 
			
		||||
 | 
			
		||||
    return key;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (void)storeSavedKeyFor:(MPUserEntity *)user {
 | 
			
		||||
@@ -230,28 +236,22 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
 | 
			
		||||
            NSString *content;
 | 
			
		||||
            while (!(content = [site.algorithm storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:recoverKey])) {
 | 
			
		||||
                // Failed to decrypt site with the current recoveryKey.  Ask user for a new one to use.
 | 
			
		||||
                __block NSString *masterPassword = nil;
 | 
			
		||||
                NSString *masterPassword = nil;
 | 
			
		||||
 | 
			
		||||
#ifdef PEARL_UIKIT
 | 
			
		||||
                dispatch_group_t recoverPasswordGroup = dispatch_group_create();
 | 
			
		||||
                dispatch_group_enter( recoverPasswordGroup );
 | 
			
		||||
                [PearlAlert showAlertWithTitle:@"Enter Old Master Password"
 | 
			
		||||
                                       message:PearlString( @"Your old master password is required to migrate the stored password for %@",
 | 
			
		||||
                                                       site.name )
 | 
			
		||||
                                     viewStyle:UIAlertViewStyleSecureTextInput
 | 
			
		||||
                                     initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
 | 
			
		||||
                            @try {
 | 
			
		||||
                masterPassword = PearlAwait( ^(void (^setResult)(id)) {
 | 
			
		||||
                    [PearlAlert showAlertWithTitle:@"Enter Old Master Password"
 | 
			
		||||
                                           message:PearlString(
 | 
			
		||||
                                                           @"Your old master password is required to migrate the stored password for %@",
 | 
			
		||||
                                                           site.name )
 | 
			
		||||
                                         viewStyle:UIAlertViewStyleSecureTextInput
 | 
			
		||||
                                         initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
 | 
			
		||||
                                if (buttonIndex_ == [alert_ cancelButtonIndex])
 | 
			
		||||
                                    // Don't Migrate
 | 
			
		||||
                                    return;
 | 
			
		||||
 | 
			
		||||
                                masterPassword = [alert_ textFieldAtIndex:0].text;
 | 
			
		||||
                            }
 | 
			
		||||
                            @finally {
 | 
			
		||||
                                dispatch_group_leave( recoverPasswordGroup );
 | 
			
		||||
                            }
 | 
			
		||||
                        }          cancelTitle:@"Don't Migrate" otherTitles:@"Migrate", nil];
 | 
			
		||||
                dispatch_group_wait( recoverPasswordGroup, DISPATCH_TIME_FOREVER );
 | 
			
		||||
                                    setResult( nil );
 | 
			
		||||
                                else
 | 
			
		||||
                                    setResult( [alert_ textFieldAtIndex:0].text );
 | 
			
		||||
                            }          cancelTitle:@"Don't Migrate" otherTitles:@"Migrate", nil];
 | 
			
		||||
                } );
 | 
			
		||||
#endif
 | 
			
		||||
                if (!masterPassword)
 | 
			
		||||
                    // Don't Migrate
 | 
			
		||||
 
 | 
			
		||||
@@ -28,12 +28,12 @@ typedef NS_ENUM( NSUInteger, MPKeyOrigin ) {
 | 
			
		||||
 | 
			
		||||
@interface MPKey : NSObject
 | 
			
		||||
 | 
			
		||||
@property(nonatomic, readonly) NSString *fullName;
 | 
			
		||||
@property(nonatomic, readonly) MPKeyOrigin origin;
 | 
			
		||||
@property(nonatomic, readonly, copy) NSString *fullName;
 | 
			
		||||
 | 
			
		||||
- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword;
 | 
			
		||||
- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData
 | 
			
		||||
                   forAlgorithm:(id<MPAlgorithm>)algorithm keyOrigin:(MPKeyOrigin)origin;
 | 
			
		||||
- (instancetype)initForFullName:(NSString *)fullName withKeyResolver:(NSData *( ^ )(id<MPAlgorithm>))keyResolver
 | 
			
		||||
                      keyOrigin:(MPKeyOrigin)origin;
 | 
			
		||||
 | 
			
		||||
- (NSData *)keyIDForAlgorithm:(id<MPAlgorithm>)algorithm;
 | 
			
		||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm;
 | 
			
		||||
 
 | 
			
		||||
@@ -19,9 +19,9 @@
 | 
			
		||||
 | 
			
		||||
@interface MPKey()
 | 
			
		||||
 | 
			
		||||
@property(nonatomic) NSString *fullName;
 | 
			
		||||
@property(nonatomic) MPKeyOrigin origin;
 | 
			
		||||
@property(nonatomic) NSString *masterPassword;
 | 
			
		||||
@property(nonatomic, copy) NSString *fullName;
 | 
			
		||||
@property(nonatomic, copy) NSData *( ^keyResolver )(id<MPAlgorithm>);
 | 
			
		||||
 | 
			
		||||
@end
 | 
			
		||||
 | 
			
		||||
@@ -31,25 +31,22 @@
 | 
			
		||||
 | 
			
		||||
- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword {
 | 
			
		||||
 | 
			
		||||
    return [self initForFullName:fullName withKeyResolver:^NSData *(id<MPAlgorithm> algorithm) {
 | 
			
		||||
        return [algorithm keyDataForFullName:self.fullName withMasterPassword:masterPassword];
 | 
			
		||||
    }                  keyOrigin:MPKeyOriginMasterPassword];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (instancetype)initForFullName:(NSString *)fullName withKeyResolver:(NSData *( ^ )(id<MPAlgorithm>))keyResolver
 | 
			
		||||
                      keyOrigin:(MPKeyOrigin)origin {
 | 
			
		||||
 | 
			
		||||
    if (!(self = [super init]))
 | 
			
		||||
        return nil;
 | 
			
		||||
 | 
			
		||||
    _keyCache = [NSCache new];
 | 
			
		||||
    self.fullName = fullName;
 | 
			
		||||
    self.origin = MPKeyOriginMasterPassword;
 | 
			
		||||
    self.masterPassword = masterPassword;
 | 
			
		||||
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData
 | 
			
		||||
                   forAlgorithm:(id<MPAlgorithm>)algorithm keyOrigin:(MPKeyOrigin)origin {
 | 
			
		||||
 | 
			
		||||
    if (!(self = [self initForFullName:fullName withMasterPassword:nil]))
 | 
			
		||||
        return nil;
 | 
			
		||||
 | 
			
		||||
    self.origin = origin;
 | 
			
		||||
    [_keyCache setObject:keyData forKey:algorithm];
 | 
			
		||||
    self.fullName = fullName;
 | 
			
		||||
    self.keyResolver = keyResolver;
 | 
			
		||||
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
@@ -61,15 +58,17 @@
 | 
			
		||||
 | 
			
		||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm {
 | 
			
		||||
 | 
			
		||||
    NSData *keyData = [_keyCache objectForKey:algorithm];
 | 
			
		||||
    if (keyData)
 | 
			
		||||
    @synchronized (self) {
 | 
			
		||||
        NSData *keyData = [_keyCache objectForKey:algorithm];
 | 
			
		||||
        if (keyData)
 | 
			
		||||
            return keyData;
 | 
			
		||||
 | 
			
		||||
        keyData = self.keyResolver( algorithm );
 | 
			
		||||
        if (keyData)
 | 
			
		||||
            [_keyCache setObject:keyData forKey:algorithm];
 | 
			
		||||
 | 
			
		||||
        return keyData;
 | 
			
		||||
 | 
			
		||||
    keyData = [algorithm keyDataForFullName:self.fullName withMasterPassword:self.masterPassword];
 | 
			
		||||
    if (keyData)
 | 
			
		||||
        [_keyCache setObject:keyData forKey:algorithm];
 | 
			
		||||
 | 
			
		||||
    return keyData;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm trimmedLength:(NSUInteger)subKeyLength {
 | 
			
		||||
@@ -80,7 +79,7 @@
 | 
			
		||||
 | 
			
		||||
- (BOOL)isEqualToKey:(MPKey *)key {
 | 
			
		||||
 | 
			
		||||
    return [self.fullName isEqualToString:key.fullName] && [self.masterPassword isEqualToString:self.masterPassword];
 | 
			
		||||
    return [[self keyIDForAlgorithm:MPAlgorithmDefault] isEqualToData:[key keyIDForAlgorithm:MPAlgorithmDefault]];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
- (BOOL)isEqual:(id)object {
 | 
			
		||||
 
 | 
			
		||||
@@ -361,6 +361,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
 | 
			
		||||
    if (mainContext)
 | 
			
		||||
        PearlAddNotificationObserver( NSManagedObjectContextDidSaveNotification, mainContext, nil,
 | 
			
		||||
                ^(MPPasswordsViewController *self, NSNotification *note) {
 | 
			
		||||
                    // TODO: either move this into the app delegate or remove the duplicate signOutAnimated: call from the app delegate.
 | 
			
		||||
                    if (![[MPiOSAppDelegate get] activeUserInContext:note.object])
 | 
			
		||||
                        [[MPiOSAppDelegate get] signOutAnimated:YES];
 | 
			
		||||
                } );
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,6 @@
 | 
			
		||||
#import "MPiOSAppDelegate.h"
 | 
			
		||||
#import "MPAppDelegate_Key.h"
 | 
			
		||||
#import "MPAppDelegate_Store.h"
 | 
			
		||||
#import "IASKSettingsReader.h"
 | 
			
		||||
#import "MPStoreViewController.h"
 | 
			
		||||
 | 
			
		||||
@interface MPiOSAppDelegate()<UIDocumentInteractionControllerDelegate>
 | 
			
		||||
@@ -199,57 +198,33 @@
 | 
			
		||||
 | 
			
		||||
    PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:@"Importing"];
 | 
			
		||||
    MPImportResult result = [self importSites:importedSitesString askImportPassword:^NSString *(NSString *userName) {
 | 
			
		||||
        __block NSString *masterPassword = nil;
 | 
			
		||||
 | 
			
		||||
        dispatch_group_t importPasswordGroup = dispatch_group_create();
 | 
			
		||||
        dispatch_group_enter( importPasswordGroup );
 | 
			
		||||
        dispatch_async( dispatch_get_main_queue(), ^{
 | 
			
		||||
        return PearlAwait( ^(void (^setResult)(id)) {
 | 
			
		||||
            [PearlAlert showAlertWithTitle:@"Import File's Master Password"
 | 
			
		||||
                                   message:strf( @"%@'s export was done using a different master password.\n"
 | 
			
		||||
                                           @"Enter that master password to unlock the exported data.", userName )
 | 
			
		||||
                                 viewStyle:UIAlertViewStyleSecureTextInput
 | 
			
		||||
                                 initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
 | 
			
		||||
                        @try {
 | 
			
		||||
                            if (buttonIndex_ == [alert_ cancelButtonIndex])
 | 
			
		||||
                                return;
 | 
			
		||||
 | 
			
		||||
                            masterPassword = [alert_ textFieldAtIndex:0].text;
 | 
			
		||||
                        }
 | 
			
		||||
                        @finally {
 | 
			
		||||
                            dispatch_group_leave( importPasswordGroup );
 | 
			
		||||
                        }
 | 
			
		||||
                        if (buttonIndex_ == [alert_ cancelButtonIndex])
 | 
			
		||||
                            setResult( nil );
 | 
			
		||||
                        else
 | 
			
		||||
                            setResult( [alert_ textFieldAtIndex:0].text );
 | 
			
		||||
                    }
 | 
			
		||||
                               cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Unlock Import", nil];
 | 
			
		||||
        } );
 | 
			
		||||
        dispatch_group_wait( importPasswordGroup, DISPATCH_TIME_FOREVER );
 | 
			
		||||
 | 
			
		||||
        return masterPassword;
 | 
			
		||||
    }                         askUserPassword:^NSString *(NSString *userName, NSUInteger importCount, NSUInteger deleteCount) {
 | 
			
		||||
        __block NSString *masterPassword = nil;
 | 
			
		||||
 | 
			
		||||
        dispatch_group_t userPasswordGroup = dispatch_group_create();
 | 
			
		||||
        dispatch_group_enter( userPasswordGroup );
 | 
			
		||||
        dispatch_async( dispatch_get_main_queue(), ^{
 | 
			
		||||
        return PearlAwait( (id)^(void (^setResult)(id)) {
 | 
			
		||||
            [PearlAlert showAlertWithTitle:strf( @"Master Password for\n%@", userName )
 | 
			
		||||
                                   message:strf( @"Imports %lu sites, overwriting %lu.",
 | 
			
		||||
                                           (unsigned long)importCount, (unsigned long)deleteCount )
 | 
			
		||||
                                 viewStyle:UIAlertViewStyleSecureTextInput
 | 
			
		||||
                                 initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
 | 
			
		||||
                        @try {
 | 
			
		||||
                            if (buttonIndex_ == [alert_ cancelButtonIndex])
 | 
			
		||||
                                return;
 | 
			
		||||
 | 
			
		||||
                            masterPassword = [alert_ textFieldAtIndex:0].text;
 | 
			
		||||
                        }
 | 
			
		||||
                        @finally {
 | 
			
		||||
                            dispatch_group_leave( userPasswordGroup );
 | 
			
		||||
                        }
 | 
			
		||||
                        if (buttonIndex_ == [alert_ cancelButtonIndex])
 | 
			
		||||
                            setResult( nil );
 | 
			
		||||
                        else
 | 
			
		||||
                            setResult( [alert_ textFieldAtIndex:0].text );
 | 
			
		||||
                    }
 | 
			
		||||
                               cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Import", nil];
 | 
			
		||||
        } );
 | 
			
		||||
        dispatch_group_wait( userPasswordGroup, DISPATCH_TIME_FOREVER );
 | 
			
		||||
 | 
			
		||||
        return masterPassword;
 | 
			
		||||
    }];
 | 
			
		||||
 | 
			
		||||
    switch (result) {
 | 
			
		||||
 
 | 
			
		||||
@@ -328,7 +328,7 @@
 | 
			
		||||
                                        </userDefinedRuntimeAttributes>
 | 
			
		||||
                                    </view>
 | 
			
		||||
                                    <view userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="cF4-TE-GEj" userLabel="Avatar Tip">
 | 
			
		||||
                                        <rect key="frame" x="23" y="86" width="276" height="60"/>
 | 
			
		||||
                                        <rect key="frame" x="22" y="86" width="276" height="60"/>
 | 
			
		||||
                                        <subviews>
 | 
			
		||||
                                            <imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="tip_basic_black.png" translatesAutoresizingMaskIntoConstraints="NO" id="V4W-bK-age">
 | 
			
		||||
                                                <rect key="frame" x="0.0" y="0.0" width="276" height="60"/>
 | 
			
		||||
@@ -3041,7 +3041,7 @@ invested: 3.7 work hours</string>
 | 
			
		||||
                                            <autoresizingMask key="autoresizingMask"/>
 | 
			
		||||
                                            <subviews>
 | 
			
		||||
                                                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="Send the answer(s) to my email" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AAV-yg-dfK">
 | 
			
		||||
                                                    <rect key="frame" x="8" y="8" width="326" height="27"/>
 | 
			
		||||
                                                    <rect key="frame" x="8" y="8" width="271" height="27"/>
 | 
			
		||||
                                                    <fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="12"/>
 | 
			
		||||
                                                    <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
 | 
			
		||||
                                                    <nil key="highlightedColor"/>
 | 
			
		||||
@@ -3064,7 +3064,7 @@ invested: 3.7 work hours</string>
 | 
			
		||||
                                            <autoresizingMask key="autoresizingMask"/>
 | 
			
		||||
                                            <subviews>
 | 
			
		||||
                                                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="This site needs different answers for each question" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="een-0g-CMy">
 | 
			
		||||
                                                    <rect key="frame" x="8" y="8" width="320" height="27"/>
 | 
			
		||||
                                                    <rect key="frame" x="8" y="8" width="265" height="27"/>
 | 
			
		||||
                                                    <fontDescription key="fontDescription" name="Exo2.0-Bold" family="Exo 2.0" pointSize="12"/>
 | 
			
		||||
                                                    <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
 | 
			
		||||
                                                    <nil key="highlightedColor"/>
 | 
			
		||||
@@ -3087,7 +3087,7 @@ invested: 3.7 work hours</string>
 | 
			
		||||
                                            <autoresizingMask key="autoresizingMask"/>
 | 
			
		||||
                                            <subviews>
 | 
			
		||||
                                                <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="mother" textAlignment="center" minimumFontSize="14" clearButtonMode="unlessEditing" translatesAutoresizingMaskIntoConstraints="NO" id="T2F-PD-Nw8" userLabel="Question Field">
 | 
			
		||||
                                                    <rect key="frame" x="8" y="18" width="359" height="30"/>
 | 
			
		||||
                                                    <rect key="frame" x="8" y="18" width="304" height="30"/>
 | 
			
		||||
                                                    <color key="backgroundColor" red="0.37254901959999998" green="0.3921568627" blue="0.42745098040000001" alpha="0.5" colorSpace="custom" customColorSpace="sRGB"/>
 | 
			
		||||
                                                    <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
 | 
			
		||||
                                                    <fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="28"/>
 | 
			
		||||
@@ -3098,13 +3098,13 @@ invested: 3.7 work hours</string>
 | 
			
		||||
                                                    </connections>
 | 
			
		||||
                                                </textField>
 | 
			
		||||
                                                <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="pifm gup balvabi yiz" textAlignment="center" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="3xA-ez-efa" userLabel="Answer Field">
 | 
			
		||||
                                                    <rect key="frame" x="20" y="90" width="335" height="31"/>
 | 
			
		||||
                                                    <rect key="frame" x="20" y="90" width="280" height="31"/>
 | 
			
		||||
                                                    <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"/>
 | 
			
		||||
                                                    <textInputTraits key="textInputTraits" autocorrectionType="no" keyboardType="alphabet" keyboardAppearance="alert" returnKeyType="next"/>
 | 
			
		||||
                                                </textField>
 | 
			
		||||
                                                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="Enter the single most significant word in the question above." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Qqg-Ny-7Po">
 | 
			
		||||
                                                    <rect key="frame" x="8" y="56" width="359" height="14"/>
 | 
			
		||||
                                                    <rect key="frame" x="8" y="56" width="304" height="14"/>
 | 
			
		||||
                                                    <fontDescription key="fontDescription" name="Exo2.0-Thin" family="Exo 2.0" pointSize="11"/>
 | 
			
		||||
                                                    <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
 | 
			
		||||
                                                    <nil key="highlightedColor"/>
 | 
			
		||||
@@ -3484,7 +3484,7 @@ You can then update your site's account with the new and stronger password.
 | 
			
		||||
    </simulatedMetricsContainer>
 | 
			
		||||
    <inferredMetricsTieBreakers>
 | 
			
		||||
        <segue reference="GZk-I4-JyH"/>
 | 
			
		||||
        <segue reference="k2G-nL-x3l"/>
 | 
			
		||||
        <segue reference="Ql4-wf-T8u"/>
 | 
			
		||||
    </inferredMetricsTieBreakers>
 | 
			
		||||
    <color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
 | 
			
		||||
</document>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user