merge default IOS-1615
authorAlejandro Gelos <alejandro@pep-project.org>
Mon, 19 Aug 2019 09:32:27 +0200
branchIOS-1615
changeset 9722b3298e4acbc1
parent 9513 ea936595e09b
parent 9699 45d4a12fdb83
child 9940 463f4850b59c
merge default
pEpForiOS/Base.lproj/Settings.storyboard
pEpForiOS/Models/Folder+Extensions.swift
pEpForiOS/Models/Folder+Threading.swift
pEpForiOS/UI/Settings/Setting/AccountSettings/AccountSettingsTableViewController.swift
pEpForiOS/UI/Settings/SettingsTableViewController.swift
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/Submodules/pEpIOSToolbox/pEpIOSToolboxTests/Foundation/StringTest.swift	Mon Aug 19 09:32:27 2019 +0200
     1.3 @@ -0,0 +1,34 @@
     1.4 +//
     1.5 +//  StringTest.swift
     1.6 +//  pEpIOSToolboxTests
     1.7 +//
     1.8 +//  Created by Xavier Algarra on 12/08/2019.
     1.9 +//  Copyright © 2019 pEp Security SA. All rights reserved.
    1.10 +//
    1.11 +
    1.12 +import XCTest
    1.13 +import pEpIOSToolbox
    1.14 +
    1.15 +class StringTest: XCTestCase {
    1.16 +    let numericString = "0123456789"
    1.17 +    let alphabetString = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    1.18 +    let otherCharacterString = #"ª!"·$%&/()=?¿^*¨_:;,.-´`+¡'"#
    1.19 +
    1.20 +    func testIsDigits() {
    1.21 +        XCTAssertTrue(numericString.isDigits)
    1.22 +        XCTAssertFalse(alphabetString.isDigits)
    1.23 +        XCTAssertFalse(otherCharacterString.isDigits)
    1.24 +        var mixedString = numericString + alphabetString + otherCharacterString
    1.25 +        mixedString = String(mixedString.shuffled())
    1.26 +        XCTAssertFalse(mixedString.isDigits)
    1.27 +    }
    1.28 +    func testIsBackspace() {
    1.29 +
    1.30 +        //this is backspace in swift
    1.31 +        let backspace = "\u{8}"
    1.32 +        XCTAssertFalse(numericString.isBackspace)
    1.33 +        XCTAssertFalse(alphabetString.isBackspace)
    1.34 +        XCTAssertFalse(otherCharacterString.isBackspace)
    1.35 +        XCTAssertTrue(backspace.isBackspace)
    1.36 +    }
    1.37 +}
     2.1 --- a/pEpForiOS.xcodeproj/project.pbxproj	Tue Jul 30 17:36:20 2019 +0200
     2.2 +++ b/pEpForiOS.xcodeproj/project.pbxproj	Mon Aug 19 09:32:27 2019 +0200
     2.3 @@ -142,8 +142,6 @@
     2.4  		155050F21FE95D8A009CEAD2 /* UserNotificationTool+pEp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 155050F11FE95D8A009CEAD2 /* UserNotificationTool+pEp.swift */; };
     2.5  		1554755F2137F6F8005A52D0 /* SwipeActionDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1554755E2137F6F8005A52D0 /* SwipeActionDescriptor.swift */; };
     2.6  		155475642137FD96005A52D0 /* FolderType+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 155475632137FD96005A52D0 /* FolderType+Extensions.swift */; };
     2.7 -		1554756621393036005A52D0 /* Folder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1554756521393036005A52D0 /* Folder+Extensions.swift */; };
     2.8 -		15547568213931BC005A52D0 /* Folder+Threading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15547567213931BC005A52D0 /* Folder+Threading.swift */; };
     2.9  		1555361B207796CE00CDDAFA /* CWInternetAddress+TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1555361A207796CE00CDDAFA /* CWInternetAddress+TestUtils.swift */; };
    2.10  		155F2D9E20530798001B4B1C /* Reusable.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 155F2DA020530798001B4B1C /* Reusable.storyboard */; };
    2.11  		15679F0D2292F4090051DCC3 /* FilterViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15679F0C2292F4090051DCC3 /* FilterViewDelegate.swift */; };
    2.12 @@ -207,6 +205,11 @@
    2.13  		220DCE371E0AB5CC002FE716 /* MessageSubjectCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 220DCE331E0AB5CC002FE716 /* MessageSubjectCell.swift */; };
    2.14  		222B35581DF96389007A1F82 /* Capability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 222B35571DF96389007A1F82 /* Capability.swift */; };
    2.15  		228038681DC9DE6D00F1CB45 /* TextfieldResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 228038671DC9DE6D00F1CB45 /* TextfieldResponder.swift */; };
    2.16 +		3705095D22CF4AC900CB73D6 /* KeySyncHandshakeViewModelTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3705095C22CF4AC900CB73D6 /* KeySyncHandshakeViewModelTest.swift */; };
    2.17 +		3705096622CF688F00CB73D6 /* KeySyncHandshakeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3705096522CF688F00CB73D6 /* KeySyncHandshakeViewModel.swift */; };
    2.18 +		3705096822CF6AD100CB73D6 /* KeySyncHandshakeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3705096722CF6AD100CB73D6 /* KeySyncHandshakeViewController.swift */; };
    2.19 +		3705096A22D4D83B00CB73D6 /* PEPSessionMoc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3705096922D4D83B00CB73D6 /* PEPSessionMoc.swift */; };
    2.20 +		3705096C22DC8C1800CB73D6 /* KeyInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3705096B22DC8C1800CB73D6 /* KeyInputView.swift */; };
    2.21  		3705703B22B7C793002E3AAD /* ActionCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3705703A22B7C793002E3AAD /* ActionCellViewModel.swift */; };
    2.22  		3705703F22B8E3B9002E3AAD /* ActionCellViewModelTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3705703E22B8E3B9002E3AAD /* ActionCellViewModelTest.swift */; };
    2.23  		3705704122B8EB27002E3AAD /* KeySyncDeviceGroupServiceMoc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3705704022B8EB27002E3AAD /* KeySyncDeviceGroupServiceMoc.swift */; };
    2.24 @@ -426,6 +429,7 @@
    2.25  		B78309C81EAA09040051A2E0 /* AccountCreation.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B78309C61EAA09040051A2E0 /* AccountCreation.storyboard */; };
    2.26  		B78CF8251E76D706008C1739 /* FilterTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B78CF8241E76D706008C1739 /* FilterTableViewController.swift */; };
    2.27  		B7A50746224CD27A007B988F /* FilterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7A50745224CD27A007B988F /* FilterViewModel.swift */; };
    2.28 +		B7C487A122E0B7DE0005304B /* nonPasteableUiTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7C487A022E0B7DE0005304B /* nonPasteableUiTextField.swift */; };
    2.29  		B7D1EEC81E8BEC8D00F190E3 /* CollapsibleTableViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7D1EEC71E8BEC8D00F190E3 /* CollapsibleTableViewHeader.swift */; };
    2.30  		B7DB7FC42215C4FF003968DA /* UINavigationController+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7DB7FC32215C4FF003968DA /* UINavigationController+Extensions.swift */; };
    2.31  		B7DB7FC72215C57F003968DA /* UIView+Autolayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7DB7FC52215C57F003968DA /* UIView+Autolayout.swift */; };
    2.32 @@ -615,8 +619,6 @@
    2.33  		155050F11FE95D8A009CEAD2 /* UserNotificationTool+pEp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserNotificationTool+pEp.swift"; sourceTree = "<group>"; };
    2.34  		1554755E2137F6F8005A52D0 /* SwipeActionDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeActionDescriptor.swift; sourceTree = "<group>"; };
    2.35  		155475632137FD96005A52D0 /* FolderType+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FolderType+Extensions.swift"; sourceTree = "<group>"; };
    2.36 -		1554756521393036005A52D0 /* Folder+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Folder+Extensions.swift"; path = "pEpForiOS/Models/Folder+Extensions.swift"; sourceTree = SOURCE_ROOT; };
    2.37 -		15547567213931BC005A52D0 /* Folder+Threading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Folder+Threading.swift"; sourceTree = "<group>"; };
    2.38  		1555361A207796CE00CDDAFA /* CWInternetAddress+TestUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CWInternetAddress+TestUtils.swift"; sourceTree = "<group>"; };
    2.39  		155F2D9F20530798001B4B1C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Reusable.storyboard; sourceTree = "<group>"; };
    2.40  		15679F0C2292F4090051DCC3 /* FilterViewDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FilterViewDelegate.swift; path = Filter/ViewModel/FilterViewDelegate.swift; sourceTree = "<group>"; };
    2.41 @@ -681,6 +683,11 @@
    2.42  		220DCE331E0AB5CC002FE716 /* MessageSubjectCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageSubjectCell.swift; sourceTree = "<group>"; };
    2.43  		222B35571DF96389007A1F82 /* Capability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Capability.swift; sourceTree = "<group>"; };
    2.44  		228038671DC9DE6D00F1CB45 /* TextfieldResponder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextfieldResponder.swift; sourceTree = "<group>"; };
    2.45 +		3705095C22CF4AC900CB73D6 /* KeySyncHandshakeViewModelTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeySyncHandshakeViewModelTest.swift; sourceTree = "<group>"; };
    2.46 +		3705096522CF688F00CB73D6 /* KeySyncHandshakeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeySyncHandshakeViewModel.swift; sourceTree = "<group>"; };
    2.47 +		3705096722CF6AD100CB73D6 /* KeySyncHandshakeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeySyncHandshakeViewController.swift; sourceTree = "<group>"; };
    2.48 +		3705096922D4D83B00CB73D6 /* PEPSessionMoc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PEPSessionMoc.swift; sourceTree = "<group>"; };
    2.49 +		3705096B22DC8C1800CB73D6 /* KeyInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyInputView.swift; sourceTree = "<group>"; };
    2.50  		3705703A22B7C793002E3AAD /* ActionCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionCellViewModel.swift; sourceTree = "<group>"; };
    2.51  		3705703E22B8E3B9002E3AAD /* ActionCellViewModelTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionCellViewModelTest.swift; sourceTree = "<group>"; };
    2.52  		3705704022B8EB27002E3AAD /* KeySyncDeviceGroupServiceMoc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeySyncDeviceGroupServiceMoc.swift; sourceTree = "<group>"; };
    2.53 @@ -936,6 +943,7 @@
    2.54  		B78309C71EAA09040051A2E0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/AccountCreation.storyboard; sourceTree = "<group>"; };
    2.55  		B78CF8241E76D706008C1739 /* FilterTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FilterTableViewController.swift; path = Filter/FilterTableViewController.swift; sourceTree = "<group>"; };
    2.56  		B7A50745224CD27A007B988F /* FilterViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FilterViewModel.swift; path = Filter/ViewModel/FilterViewModel.swift; sourceTree = "<group>"; };
    2.57 +		B7C487A022E0B7DE0005304B /* nonPasteableUiTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = nonPasteableUiTextField.swift; sourceTree = "<group>"; };
    2.58  		B7D1EEC71E8BEC8D00F190E3 /* CollapsibleTableViewHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CollapsibleTableViewHeader.swift; path = Folder/CollapsibleTableViewHeader.swift; sourceTree = "<group>"; };
    2.59  		B7DB7FC32215C4FF003968DA /* UINavigationController+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UINavigationController+Extensions.swift"; sourceTree = "<group>"; };
    2.60  		B7DB7FC52215C57F003968DA /* UIView+Autolayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Autolayout.swift"; sourceTree = "<group>"; };
    2.61 @@ -1035,6 +1043,7 @@
    2.62  		150707DD21006D0200AA213F /* UI */ = {
    2.63  			isa = PBXGroup;
    2.64  			children = (
    2.65 +				3705095B22CF465300CB73D6 /* KeySyncHandshake */,
    2.66  				15D4399D216F703100EB3933 /* Compose */,
    2.67  				4356FFEA21356CB600804089 /* Util */,
    2.68  			);
    2.69 @@ -1598,6 +1607,24 @@
    2.70  			name = Cells;
    2.71  			sourceTree = "<group>";
    2.72  		};
    2.73 +		3705095B22CF465300CB73D6 /* KeySyncHandshake */ = {
    2.74 +			isa = PBXGroup;
    2.75 +			children = (
    2.76 +				3705095C22CF4AC900CB73D6 /* KeySyncHandshakeViewModelTest.swift */,
    2.77 +				3705096922D4D83B00CB73D6 /* PEPSessionMoc.swift */,
    2.78 +			);
    2.79 +			path = KeySyncHandshake;
    2.80 +			sourceTree = "<group>";
    2.81 +		};
    2.82 +		3705096422CF686C00CB73D6 /* KeySyncHandshake */ = {
    2.83 +			isa = PBXGroup;
    2.84 +			children = (
    2.85 +				3705096522CF688F00CB73D6 /* KeySyncHandshakeViewModel.swift */,
    2.86 +				3705096722CF6AD100CB73D6 /* KeySyncHandshakeViewController.swift */,
    2.87 +			);
    2.88 +			path = KeySyncHandshake;
    2.89 +			sourceTree = "<group>";
    2.90 +		};
    2.91  		4304FCFD1EBB8C2C0086DADA /* LanguageList */ = {
    2.92  			isa = PBXGroup;
    2.93  			children = (
    2.94 @@ -1947,6 +1974,7 @@
    2.95  				152A39932190587100D9F8E4 /* TextViewInTableViewScrollUtil.swift */,
    2.96  				B7DFEA51225368670080A2BA /* VirtualFolder.swift */,
    2.97  				B7DFEA5322536D5E0080A2BA /* UnifiedInbox.swift */,
    2.98 +				3705096B22DC8C1800CB73D6 /* KeyInputView.swift */,
    2.99  			);
   2.100  			path = Util;
   2.101  			sourceTree = "<group>";
   2.102 @@ -2016,8 +2044,6 @@
   2.103  			children = (
   2.104  				37C3C0E52260C64D003E290C /* Log.swift */,
   2.105  				155475632137FD96005A52D0 /* FolderType+Extensions.swift */,
   2.106 -				1554756521393036005A52D0 /* Folder+Extensions.swift */,
   2.107 -				15547567213931BC005A52D0 /* Folder+Threading.swift */,
   2.108  				15D7D991219B154000A1A2B9 /* Attachment+Extensions.swift */,
   2.109  				433E7437225B564400B84CD9 /* Account+Extension.swift */,
   2.110  			);
   2.111 @@ -2050,23 +2076,24 @@
   2.112  		43ED53611CC77F95006AB156 /* UI */ = {
   2.113  			isa = PBXGroup;
   2.114  			children = (
   2.115 -				B706C0EF1EA8C378006B2F6C /* StoryboardFiles */,
   2.116 -				432E80FA2191AF5100359879 /* Fonts */,
   2.117 -				43AA82511E9B925000ABD5A8 /* Util */,
   2.118 -				003C0FAA20B57C2E0093A987 /* SplitView */,
   2.119 -				15BA537A20A1F5CA0090F126 /* MoveToFolder */,
   2.120 +				43A6E0491E5726C8005BEE69 /* Background */,
   2.121  				15265942216230B0006A78DF /* Compose */,
   2.122 +				15FE1F741FE122B200CC2D97 /* Credits */,
   2.123  				B70D32B0205BCFBD0094A92A /* EmailDisplay */,
   2.124  				43ED53621CC77F95006AB156 /* EmailDisplayList */,
   2.125 +				B78CF8261E76D70D008C1739 /* Filter */,
   2.126 +				B71EBBB41E55E43100150177 /* Folder */,
   2.127 +				432E80FA2191AF5100359879 /* Fonts */,
   2.128 +				438D5A6F1EA77CFC001A37E1 /* Handshake */,
   2.129 +				3705096422CF686C00CB73D6 /* KeySyncHandshake */,
   2.130 +				B70D32AA205BCCC70094A92A /* Login */,
   2.131 +				B70D32AF205BCF460094A92A /* ManualLogin */,
   2.132 +				15BA537A20A1F5CA0090F126 /* MoveToFolder */,
   2.133 +				15874BA72127493E00A3A4A6 /* Settings */,
   2.134 +				003C0FAA20B57C2E0093A987 /* SplitView */,
   2.135 +				B706C0EF1EA8C378006B2F6C /* StoryboardFiles */,
   2.136  				492EF92B20C69547004EAE14 /* Thread */,
   2.137 -				B70D32AA205BCCC70094A92A /* Login */,
   2.138 -				15FE1F741FE122B200CC2D97 /* Credits */,
   2.139 -				B78CF8261E76D70D008C1739 /* Filter */,
   2.140 -				43A6E0491E5726C8005BEE69 /* Background */,
   2.141 -				B71EBBB41E55E43100150177 /* Folder */,
   2.142 -				15874BA72127493E00A3A4A6 /* Settings */,
   2.143 -				B70D32AF205BCF460094A92A /* ManualLogin */,
   2.144 -				438D5A6F1EA77CFC001A37E1 /* Handshake */,
   2.145 +				43AA82511E9B925000ABD5A8 /* Util */,
   2.146  			);
   2.147  			path = UI;
   2.148  			sourceTree = "<group>";
   2.149 @@ -2409,6 +2436,7 @@
   2.150  				B7DB7FC32215C4FF003968DA /* UINavigationController+Extensions.swift */,
   2.151  				43293EFA1EB9DD6700EEE010 /* UIViewController+Extension.swift */,
   2.152  				434AC3E220A450D700C11B7F /* UIAlertController+Extension.swift */,
   2.153 +				B7C487A022E0B7DE0005304B /* nonPasteableUiTextField.swift */,
   2.154  			);
   2.155  			path = Extensions;
   2.156  			sourceTree = "<group>";
   2.157 @@ -2868,7 +2896,6 @@
   2.158  				4356FFE52135448600804089 /* ReplyAllPossibleChecker.swift in Sources */,
   2.159  				15255B031F825CD100A2CFC9 /* IdentityImageTool.swift in Sources */,
   2.160  				152A39D121905C3E00D9F8E4 /* AttachmentViewModel.swift in Sources */,
   2.161 -				15547568213931BC005A52D0 /* Folder+Threading.swift in Sources */,
   2.162  				4330278E1F7BABFF00D685F8 /* GradientView.swift in Sources */,
   2.163  				434F40961EB0DB5E002FBF0D /* HandshakePartnerTableViewCellViewModel.swift in Sources */,
   2.164  				152A39E121905C3E00D9F8E4 /* RecipientCellViewModel+FieldType.swift in Sources */,
   2.165 @@ -2883,7 +2910,6 @@
   2.166  				49D3BECC20F8F7330043E05D /* LoginViewController.swift in Sources */,
   2.167  				43425EDB1FE3DE6E004A2728 /* OAuth2ProviderProtocol.swift in Sources */,
   2.168  				1526596A216230B1006A78DF /* ComposeDataSource.swift in Sources */,
   2.169 -				1554756621393036005A52D0 /* Folder+Extensions.swift in Sources */,
   2.170  				0069DCFB2110679200846EB1 /* EmailViewController+UIPopoverPresentationControllerDelegate.swift in Sources */,
   2.171  				43106A192045716000693144 /* OAuth2ConfigurationProtocol+Extension.swift in Sources */,
   2.172  				43C322051EA89EED005073FB /* HandshakePartnerTableViewCell.swift in Sources */,
   2.173 @@ -2900,10 +2926,12 @@
   2.174  				43F9D99A1E92725700F78A1C /* AttachmentsViewHelper.swift in Sources */,
   2.175  				F7C16A5D21A59187004B44F2 /* FolderViewModelDelegate.swift in Sources */,
   2.176  				43B10C801EC2EE7F003E849F /* CppDummy.cpp in Sources */,
   2.177 +				B7C487A122E0B7DE0005304B /* nonPasteableUiTextField.swift in Sources */,
   2.178  				15B220501FBF5D6E00CA52BA /* InfoPlist.swift in Sources */,
   2.179  				15874BD421274BD400A3A4A6 /* TrustedServerSettingCell.swift in Sources */,
   2.180  				A1014DA71D1173CD00C472A8 /* UIHelper.swift in Sources */,
   2.181  				43A6E0581E57400E005BEE69 /* RatingReEvaluator.swift in Sources */,
   2.182 +				3705096622CF688F00CB73D6 /* KeySyncHandshakeViewModel.swift in Sources */,
   2.183  				49C34AF620E4F649009D11CC /* CellDetailTransition.swift in Sources */,
   2.184  				492EF92A20C18C6C004EAE14 /* DisplayedMessage.swift in Sources */,
   2.185  				49228A5520D4035100A51E9D /* DetailCellSegue.swift in Sources */,
   2.186 @@ -2912,6 +2940,7 @@
   2.187  				152A39CE21905C3E00D9F8E4 /* ComposeHelpers.swift in Sources */,
   2.188  				43985D0A2044296D0080FA9A /* OAuth2AuthViewModel.swift in Sources */,
   2.189  				49691B1520D7FD0200CA9367 /* MessageViewModelConfigurable.swift in Sources */,
   2.190 +				3705096822CF6AD100CB73D6 /* KeySyncHandshakeViewController.swift in Sources */,
   2.191  				152A39CF21905C3E00D9F8E4 /* ComposeUtil.swift in Sources */,
   2.192  				152A39C821905C3E00D9F8E4 /* ComposeViewModel+InitData.swift in Sources */,
   2.193  				0038494C20D2587F008000EA /* PepPictureComposer.swift in Sources */,
   2.194 @@ -2924,6 +2953,7 @@
   2.195  				43D0702F2133DB3F0013B120 /* AppSettings.swift in Sources */,
   2.196  				00FD0CE62101F7D700BA0C56 /* ScreenComposerProtocol.swift in Sources */,
   2.197  				3705703B22B7C793002E3AAD /* ActionCellViewModel.swift in Sources */,
   2.198 +				3705096C22DC8C1800CB73D6 /* KeyInputView.swift in Sources */,
   2.199  				4351C2D81F4441190053381F /* references.c in Sources */,
   2.200  				152A39DC21905C3E00D9F8E4 /* TextViewContainingTableViewCell.swift in Sources */,
   2.201  				000D3C2F20D12BFD006B11B2 /* MessageViewModel.swift in Sources */,
   2.202 @@ -3028,6 +3058,7 @@
   2.203  				15B483DB1F28E2FC000FB2CF /* SpecialUseMailboxesTest.swift in Sources */,
   2.204  				151F71FB202A06760057C74D /* MockBackgrounder.swift in Sources */,
   2.205  				43D51E891DD5D902008B77A8 /* SimpleOperationsTest.swift in Sources */,
   2.206 +				3705096A22D4D83B00CB73D6 /* PEPSessionMoc.swift in Sources */,
   2.207  				151F71FE202A06760057C74D /* CdMessage+TestUtils.swift in Sources */,
   2.208  				3791A45F22BA6A0300A9E534 /* MessageModelServiceMoc.swift in Sources */,
   2.209  				151F71F9202A06760057C74D /* ReplicationServiceObserver.swift in Sources */,
   2.210 @@ -3042,6 +3073,7 @@
   2.211  				150707DC21006CD000AA213F /* ComposeUtilTest.swift in Sources */,
   2.212  				15D439A5216F7E0E00EB3933 /* AccountPickerViewModelTest.swift in Sources */,
   2.213  				1574D07D2114696B00FEDC93 /* URL+MailToTest.swift in Sources */,
   2.214 +				3705095D22CF4AC900CB73D6 /* KeySyncHandshakeViewModelTest.swift in Sources */,
   2.215  				4356FFEC21356CB600804089 /* ReplyAllPossibleCheckerTest.swift in Sources */,
   2.216  				430C80E01D0EADC200CD4582 /* PepAdapterTests.swift in Sources */,
   2.217  				00DF2C3B2164C53F004EBA6C /* FolderViewModelTest.swift in Sources */,
     3.1 --- a/pEpForiOS.xcodeproj/xcshareddata/xcschemes/pEp.xcscheme	Tue Jul 30 17:36:20 2019 +0200
     3.2 +++ b/pEpForiOS.xcodeproj/xcshareddata/xcschemes/pEp.xcscheme	Mon Aug 19 09:32:27 2019 +0200
     3.3 @@ -259,7 +259,6 @@
     3.4        selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
     3.5        selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
     3.6        enableAddressSanitizer = "YES"
     3.7 -      enableASanStackUseAfterReturn = "YES"
     3.8        enableUBSanitizer = "YES"
     3.9        disableMainThreadChecker = "YES"
    3.10        launchStyle = "0"
     4.1 --- a/pEpForiOS/AppDelegate.swift	Tue Jul 30 17:36:20 2019 +0200
     4.2 +++ b/pEpForiOS/AppDelegate.swift	Mon Aug 19 09:32:27 2019 +0200
     4.3 @@ -80,9 +80,8 @@
     4.4      private func gracefullyShutdownServices() {
     4.5          guard syncUserActionsAndCleanupbackgroundTaskId == UIBackgroundTaskIdentifier.invalid
     4.6              else {
     4.7 -                Log.shared.warn(
     4.8 -                    "Will not start background sync, pending %d",
     4.9 -                    syncUserActionsAndCleanupbackgroundTaskId.rawValue)
    4.10 +                Log.shared.errorAndCrash("Will not start background sync, pending %d",
    4.11 +                                         syncUserActionsAndCleanupbackgroundTaskId.rawValue)
    4.12                  return
    4.13          }
    4.14          syncUserActionsAndCleanupbackgroundTaskId =
    4.15 @@ -115,7 +114,7 @@
    4.16      func deleteManagementDBIfRequired() -> Bool {
    4.17          if AppSettings.shouldReinitializePepOnNextStartup {
    4.18              AppSettings.shouldReinitializePepOnNextStartup = false
    4.19 -            let _ = PEPUtil.pEpClean()
    4.20 +            let _ = PEPUtils.pEpClean()
    4.21              return true
    4.22          }
    4.23          return false
     5.1 --- a/pEpForiOS/Base.lproj/Handshake.storyboard	Tue Jul 30 17:36:20 2019 +0200
     5.2 +++ b/pEpForiOS/Base.lproj/Handshake.storyboard	Mon Aug 19 09:32:27 2019 +0200
     5.3 @@ -1,11 +1,11 @@
     5.4  <?xml version="1.0" encoding="UTF-8"?>
     5.5 -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="PZt-VJ-X5t">
     5.6 +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="PZt-VJ-X5t">
     5.7      <device id="ipad9_7" orientation="landscape">
     5.8          <adaptation id="fullscreen"/>
     5.9      </device>
    5.10      <dependencies>
    5.11          <deployment identifier="iOS"/>
    5.12 -        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
    5.13 +        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
    5.14          <capability name="Stack View standard spacing" minToolsVersion="9.0"/>
    5.15          <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    5.16      </dependencies>
    5.17 @@ -13,7 +13,7 @@
    5.18          <!--Handshake View Controller-->
    5.19          <scene sceneID="IVD-QH-CTl">
    5.20              <objects>
    5.21 -                <tableViewController id="Jbh-LP-Jl1" customClass="HandshakeViewController" customModule="pEpForiOS" customModuleProvider="target" sceneMemberID="viewController">
    5.22 +                <tableViewController storyboardIdentifier="HandshakeViewControllerID" id="Jbh-LP-Jl1" customClass="HandshakeViewController" customModule="pEpForiOS" customModuleProvider="target" sceneMemberID="viewController">
    5.23                      <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="307" sectionHeaderHeight="28" sectionFooterHeight="28" id="oZy-Mt-k2w" userLabel="HandshakeTableView">
    5.24                          <rect key="frame" x="0.0" y="0.0" width="1024" height="698"/>
    5.25                          <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
     6.1 --- a/pEpForiOS/Base.lproj/Main.storyboard	Tue Jul 30 17:36:20 2019 +0200
     6.2 +++ b/pEpForiOS/Base.lproj/Main.storyboard	Mon Aug 19 09:32:27 2019 +0200
     6.3 @@ -27,96 +27,126 @@
     6.4                          <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
     6.5                          <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
     6.6                          <prototypes>
     6.7 -                            <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="EmailListViewCell" rowHeight="80" id="uhL-VL-48i" customClass="EmailListViewCell" customModule="pEpForiOS" customModuleProvider="target">
     6.8 -                                <rect key="frame" x="0.0" y="28" width="375" height="80"/>
     6.9 +                            <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="EmailListViewCell" rowHeight="90" id="uhL-VL-48i" customClass="EmailListViewCell" customModule="pEpForiOS" customModuleProvider="target">
    6.10 +                                <rect key="frame" x="0.0" y="28" width="375" height="90"/>
    6.11                                  <autoresizingMask key="autoresizingMask"/>
    6.12                                  <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="uhL-VL-48i" id="jgQ-nZ-h1r">
    6.13 -                                    <rect key="frame" x="0.0" y="0.0" width="375" height="79.5"/>
    6.14 +                                    <rect key="frame" x="0.0" y="0.0" width="375" height="89.5"/>
    6.15                                      <autoresizingMask key="autoresizingMask"/>
    6.16                                      <subviews>
    6.17 -                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="251" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" text="Sender" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tWZ-jW-sUg">
    6.18 -                                            <rect key="frame" x="81" y="5" width="52" height="19.5"/>
    6.19 -                                            <fontDescription key="fontDescription" style="UICTFontTextStyleCallout"/>
    6.20 -                                            <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
    6.21 -                                            <nil key="highlightedColor"/>
    6.22 -                                        </label>
    6.23 -                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalCompressionResistancePriority="1000" text="Summary" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BvI-Zi-zU6">
    6.24 -                                            <rect key="frame" x="81" y="52" width="286" height="20.5"/>
    6.25 -                                            <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
    6.26 -                                            <color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
    6.27 -                                            <nil key="highlightedColor"/>
    6.28 -                                        </label>
    6.29 -                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalCompressionResistancePriority="1000" text="Subject" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5Py-55-oem">
    6.30 -                                            <rect key="frame" x="81" y="28.5" width="269" height="19.5"/>
    6.31 -                                            <fontDescription key="fontDescription" style="UICTFontTextStyleCallout"/>
    6.32 -                                            <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
    6.33 -                                            <nil key="highlightedColor"/>
    6.34 -                                        </label>
    6.35 -                                        <stackView opaque="NO" contentMode="scaleAspectFit" distribution="fillProportionally" alignment="center" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="MZJ-bg-Dw7">
    6.36 -                                            <rect key="frame" x="353" y="32.5" width="14" height="14"/>
    6.37 +                                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="id9-ga-Yy6">
    6.38 +                                            <rect key="frame" x="5" y="0.0" width="365" height="89.5"/>
    6.39                                              <subviews>
    6.40 -                                                <imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="attachment-list-icon" translatesAutoresizingMaskIntoConstraints="NO" id="3BC-6k-zL0">
    6.41 -                                                    <rect key="frame" x="-14" y="0.0" width="14" height="14"/>
    6.42 -                                                    <constraints>
    6.43 -                                                        <constraint firstAttribute="height" constant="14" id="BQW-DX-8aC"/>
    6.44 -                                                        <constraint firstAttribute="width" secondItem="3BC-6k-zL0" secondAttribute="height" multiplier="1:1" id="xsh-N2-oVw"/>
    6.45 -                                                    </constraints>
    6.46 -                                                </imageView>
    6.47 -                                                <imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="BLq-0g-OgR" userLabel="Flagged icon">
    6.48 -                                                    <rect key="frame" x="0.0" y="0.0" width="14" height="14"/>
    6.49 -                                                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
    6.50 -                                                    <constraints>
    6.51 -                                                        <constraint firstAttribute="height" constant="14" id="f4y-vH-ugI"/>
    6.52 -                                                        <constraint firstAttribute="width" secondItem="BLq-0g-OgR" secondAttribute="height" multiplier="1:1" id="uGj-dI-2ly"/>
    6.53 -                                                    </constraints>
    6.54 -                                                </imageView>
    6.55 -                                            </subviews>
    6.56 -                                        </stackView>
    6.57 -                                        <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="empty-avatar" translatesAutoresizingMaskIntoConstraints="NO" id="YqR-ga-Tf9" userLabel="Contact Image">
    6.58 -                                            <rect key="frame" x="16" y="15" width="50" height="50"/>
    6.59 -                                            <constraints>
    6.60 -                                                <constraint firstAttribute="width" constant="50" id="LB6-wZ-6Bh"/>
    6.61 -                                                <constraint firstAttribute="width" secondItem="YqR-ga-Tf9" secondAttribute="height" multiplier="1:1" id="b42-e4-yZf"/>
    6.62 -                                            </constraints>
    6.63 -                                        </imageView>
    6.64 -                                        <stackView opaque="NO" contentMode="scaleToFill" alignment="center" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="8QD-qP-0hS">
    6.65 -                                            <rect key="frame" x="332.5" y="5" width="34.5" height="19.5"/>
    6.66 -                                            <subviews>
    6.67 -                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="Date" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wkg-wf-Dbd">
    6.68 -                                                    <rect key="frame" x="0.0" y="0.0" width="34.5" height="19.5"/>
    6.69 +                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="251" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" text="Sender" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tWZ-jW-sUg">
    6.70 +                                                    <rect key="frame" x="68" y="5" width="52" height="19.5"/>
    6.71                                                      <fontDescription key="fontDescription" style="UICTFontTextStyleCallout"/>
    6.72 -                                                    <color key="textColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
    6.73 +                                                    <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
    6.74                                                      <nil key="highlightedColor"/>
    6.75                                                  </label>
    6.76 +                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalCompressionResistancePriority="1000" text="Summary" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BvI-Zi-zU6">
    6.77 +                                                    <rect key="frame" x="68" y="52" width="297" height="20.5"/>
    6.78 +                                                    <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
    6.79 +                                                    <color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
    6.80 +                                                    <nil key="highlightedColor"/>
    6.81 +                                                </label>
    6.82 +                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalCompressionResistancePriority="1000" text="Subject" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5Py-55-oem">
    6.83 +                                                    <rect key="frame" x="68" y="28.5" width="280" height="19.5"/>
    6.84 +                                                    <fontDescription key="fontDescription" style="UICTFontTextStyleCallout"/>
    6.85 +                                                    <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
    6.86 +                                                    <nil key="highlightedColor"/>
    6.87 +                                                </label>
    6.88 +                                                <stackView opaque="NO" contentMode="scaleAspectFit" distribution="fillProportionally" alignment="center" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="MZJ-bg-Dw7">
    6.89 +                                                    <rect key="frame" x="351" y="32.5" width="14" height="14"/>
    6.90 +                                                    <subviews>
    6.91 +                                                        <imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="attachment-list-icon" translatesAutoresizingMaskIntoConstraints="NO" id="3BC-6k-zL0">
    6.92 +                                                            <rect key="frame" x="-14" y="0.0" width="14" height="14"/>
    6.93 +                                                            <constraints>
    6.94 +                                                                <constraint firstAttribute="height" constant="14" id="BQW-DX-8aC"/>
    6.95 +                                                                <constraint firstAttribute="width" secondItem="3BC-6k-zL0" secondAttribute="height" multiplier="1:1" id="xsh-N2-oVw"/>
    6.96 +                                                            </constraints>
    6.97 +                                                        </imageView>
    6.98 +                                                        <imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="BLq-0g-OgR" userLabel="Flagged icon">
    6.99 +                                                            <rect key="frame" x="0.0" y="0.0" width="14" height="14"/>
   6.100 +                                                            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
   6.101 +                                                            <constraints>
   6.102 +                                                                <constraint firstAttribute="height" constant="14" id="f4y-vH-ugI"/>
   6.103 +                                                                <constraint firstAttribute="width" secondItem="BLq-0g-OgR" secondAttribute="height" multiplier="1:1" id="uGj-dI-2ly"/>
   6.104 +                                                            </constraints>
   6.105 +                                                        </imageView>
   6.106 +                                                    </subviews>
   6.107 +                                                </stackView>
   6.108 +                                                <stackView opaque="NO" contentMode="scaleToFill" alignment="center" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="8QD-qP-0hS">
   6.109 +                                                    <rect key="frame" x="330.5" y="5" width="34.5" height="19.5"/>
   6.110 +                                                    <subviews>
   6.111 +                                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="Date" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wkg-wf-Dbd">
   6.112 +                                                            <rect key="frame" x="0.0" y="0.0" width="34.5" height="19.5"/>
   6.113 +                                                            <fontDescription key="fontDescription" style="UICTFontTextStyleCallout"/>
   6.114 +                                                            <color key="textColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
   6.115 +                                                            <nil key="highlightedColor"/>
   6.116 +                                                        </label>
   6.117 +                                                    </subviews>
   6.118 +                                                </stackView>
   6.119 +                                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="UPT-Ig-AlN">
   6.120 +                                                    <rect key="frame" x="8" y="20" width="50" height="50"/>
   6.121 +                                                    <subviews>
   6.122 +                                                        <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="empty-avatar" translatesAutoresizingMaskIntoConstraints="NO" id="YqR-ga-Tf9" userLabel="Contact Image">
   6.123 +                                                            <rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
   6.124 +                                                            <constraints>
   6.125 +                                                                <constraint firstAttribute="width" secondItem="YqR-ga-Tf9" secondAttribute="height" multiplier="1:1" id="NF6-d0-Gji"/>
   6.126 +                                                                <constraint firstAttribute="width" constant="50" id="lsJ-yy-dQ3"/>
   6.127 +                                                            </constraints>
   6.128 +                                                            <userDefinedRuntimeAttributes>
   6.129 +                                                                <userDefinedRuntimeAttribute type="boolean" keyPath="translatesAutoresizingMaskIntoConstraints" value="NO"/>
   6.130 +                                                            </userDefinedRuntimeAttributes>
   6.131 +                                                        </imageView>
   6.132 +                                                        <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" fixedFrame="YES" insetsLayoutMarginsFromSafeArea="NO" image="pEp-status-yellow" translatesAutoresizingMaskIntoConstraints="NO" id="m0u-Jn-mQU">
   6.133 +                                                            <rect key="frame" x="15" y="15" width="20" height="20"/>
   6.134 +                                                            <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
   6.135 +                                                            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
   6.136 +                                                            <userDefinedRuntimeAttributes>
   6.137 +                                                                <userDefinedRuntimeAttribute type="boolean" keyPath="translatesAutoresizingMaskIntoConstraints" value="NO"/>
   6.138 +                                                            </userDefinedRuntimeAttributes>
   6.139 +                                                        </imageView>
   6.140 +                                                    </subviews>
   6.141 +                                                    <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
   6.142 +                                                    <constraints>
   6.143 +                                                        <constraint firstItem="YqR-ga-Tf9" firstAttribute="leading" secondItem="UPT-Ig-AlN" secondAttribute="leading" id="89G-HL-lBv"/>
   6.144 +                                                        <constraint firstAttribute="bottom" secondItem="YqR-ga-Tf9" secondAttribute="bottom" id="NOg-aR-jF3"/>
   6.145 +                                                        <constraint firstItem="YqR-ga-Tf9" firstAttribute="top" secondItem="UPT-Ig-AlN" secondAttribute="top" id="oJy-iG-s3V"/>
   6.146 +                                                        <constraint firstAttribute="trailing" secondItem="YqR-ga-Tf9" secondAttribute="trailing" id="vbN-gb-MVF"/>
   6.147 +                                                    </constraints>
   6.148 +                                                    <userDefinedRuntimeAttributes>
   6.149 +                                                        <userDefinedRuntimeAttribute type="boolean" keyPath="translatesAutoresizingMaskIntoConstraints" value="NO"/>
   6.150 +                                                    </userDefinedRuntimeAttributes>
   6.151 +                                                </view>
   6.152                                              </subviews>
   6.153 -                                        </stackView>
   6.154 -                                        <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" image="pEp-status-yellow" translatesAutoresizingMaskIntoConstraints="NO" id="m0u-Jn-mQU">
   6.155 -                                            <rect key="frame" x="56" y="52.5" width="20" height="20"/>
   6.156 -                                            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
   6.157 +                                            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
   6.158                                              <constraints>
   6.159 -                                                <constraint firstAttribute="width" secondItem="m0u-Jn-mQU" secondAttribute="height" multiplier="1:1" id="7pr-8F-BtP"/>
   6.160 +                                                <constraint firstItem="tWZ-jW-sUg" firstAttribute="leading" secondItem="UPT-Ig-AlN" secondAttribute="trailing" constant="10" id="5wE-p5-M3j"/>
   6.161 +                                                <constraint firstItem="UPT-Ig-AlN" firstAttribute="centerY" secondItem="id9-ga-Yy6" secondAttribute="centerY" id="7c0-om-zpn"/>
   6.162 +                                                <constraint firstItem="BvI-Zi-zU6" firstAttribute="trailing" secondItem="8QD-qP-0hS" secondAttribute="trailing" id="Ari-gf-dnZ"/>
   6.163 +                                                <constraint firstItem="8QD-qP-0hS" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="tWZ-jW-sUg" secondAttribute="trailing" constant="8" id="FCP-Gf-ws2"/>
   6.164 +                                                <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="80" id="Fjj-lj-oAH"/>
   6.165 +                                                <constraint firstAttribute="trailing" secondItem="8QD-qP-0hS" secondAttribute="trailing" id="KDc-S9-BfG"/>
   6.166 +                                                <constraint firstItem="5Py-55-oem" firstAttribute="leading" secondItem="tWZ-jW-sUg" secondAttribute="leading" id="KGf-kA-gVS"/>
   6.167 +                                                <constraint firstItem="UPT-Ig-AlN" firstAttribute="leading" secondItem="id9-ga-Yy6" secondAttribute="leading" constant="8" id="Kwq-ot-H9i"/>
   6.168 +                                                <constraint firstItem="MZJ-bg-Dw7" firstAttribute="trailing" secondItem="8QD-qP-0hS" secondAttribute="trailing" id="Nra-mU-9kx"/>
   6.169 +                                                <constraint firstItem="MZJ-bg-Dw7" firstAttribute="top" secondItem="8QD-qP-0hS" secondAttribute="bottom" constant="8" id="W09-Xa-Qin"/>
   6.170 +                                                <constraint firstItem="BvI-Zi-zU6" firstAttribute="top" secondItem="5Py-55-oem" secondAttribute="bottom" constant="4" id="Ymq-ad-cdD"/>
   6.171 +                                                <constraint firstItem="5Py-55-oem" firstAttribute="top" secondItem="tWZ-jW-sUg" secondAttribute="bottom" constant="4" id="dyB-LY-dAR"/>
   6.172 +                                                <constraint firstItem="MZJ-bg-Dw7" firstAttribute="leading" secondItem="5Py-55-oem" secondAttribute="trailing" constant="3" id="fZR-QV-Zi9"/>
   6.173 +                                                <constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="BvI-Zi-zU6" secondAttribute="bottom" constant="5" id="lBT-Xg-QNF"/>
   6.174 +                                                <constraint firstItem="tWZ-jW-sUg" firstAttribute="bottom" secondItem="8QD-qP-0hS" secondAttribute="bottom" id="lmu-KO-CeV"/>
   6.175 +                                                <constraint firstItem="tWZ-jW-sUg" firstAttribute="top" secondItem="id9-ga-Yy6" secondAttribute="top" constant="5" id="prK-py-IfZ"/>
   6.176 +                                                <constraint firstItem="BvI-Zi-zU6" firstAttribute="leading" secondItem="5Py-55-oem" secondAttribute="leading" id="tRf-eY-RHH"/>
   6.177                                              </constraints>
   6.178 -                                        </imageView>
   6.179 +                                        </view>
   6.180                                      </subviews>
   6.181                                      <constraints>
   6.182 -                                        <constraint firstItem="tWZ-jW-sUg" firstAttribute="leading" secondItem="YqR-ga-Tf9" secondAttribute="trailing" constant="15" id="23g-Ki-bp4"/>
   6.183 -                                        <constraint firstItem="MZJ-bg-Dw7" firstAttribute="top" secondItem="8QD-qP-0hS" secondAttribute="bottom" constant="8" id="7d0-XN-5mi"/>
   6.184 -                                        <constraint firstItem="5Py-55-oem" firstAttribute="leading" secondItem="tWZ-jW-sUg" secondAttribute="leading" id="8vT-bf-Xw4"/>
   6.185 -                                        <constraint firstItem="tWZ-jW-sUg" firstAttribute="top" secondItem="jgQ-nZ-h1r" secondAttribute="top" constant="5" id="HLH-Yf-Zeb"/>
   6.186 -                                        <constraint firstItem="BvI-Zi-zU6" firstAttribute="top" secondItem="5Py-55-oem" secondAttribute="bottom" constant="4" id="Hex-Vv-UnE"/>
   6.187 -                                        <constraint firstItem="8QD-qP-0hS" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="tWZ-jW-sUg" secondAttribute="trailing" constant="8" id="Qdc-6I-SDj"/>
   6.188 -                                        <constraint firstItem="YqR-ga-Tf9" firstAttribute="leading" secondItem="jgQ-nZ-h1r" secondAttribute="leadingMargin" id="UBA-wX-ANZ"/>
   6.189 -                                        <constraint firstItem="BvI-Zi-zU6" firstAttribute="leading" secondItem="5Py-55-oem" secondAttribute="leading" id="Wva-Nq-D3D"/>
   6.190 -                                        <constraint firstItem="5Py-55-oem" firstAttribute="top" secondItem="tWZ-jW-sUg" secondAttribute="bottom" constant="4" id="Xp7-Lw-Lqg"/>
   6.191 -                                        <constraint firstItem="MZJ-bg-Dw7" firstAttribute="leading" secondItem="5Py-55-oem" secondAttribute="trailing" constant="3" id="acU-9j-7hJ"/>
   6.192 -                                        <constraint firstItem="BvI-Zi-zU6" firstAttribute="leading" secondItem="m0u-Jn-mQU" secondAttribute="trailing" constant="5" id="co3-bq-uE3"/>
   6.193 -                                        <constraint firstItem="tWZ-jW-sUg" firstAttribute="bottom" secondItem="8QD-qP-0hS" secondAttribute="bottom" id="dxm-I0-Sij"/>
   6.194 -                                        <constraint firstItem="MZJ-bg-Dw7" firstAttribute="trailing" secondItem="8QD-qP-0hS" secondAttribute="trailing" id="hA7-ed-KYt"/>
   6.195 -                                        <constraint firstItem="BvI-Zi-zU6" firstAttribute="bottom" secondItem="m0u-Jn-mQU" secondAttribute="bottom" id="jxk-IK-xXR"/>
   6.196 -                                        <constraint firstItem="YqR-ga-Tf9" firstAttribute="centerY" secondItem="jgQ-nZ-h1r" secondAttribute="centerY" id="kzK-1E-vdU"/>
   6.197 -                                        <constraint firstAttribute="trailing" secondItem="8QD-qP-0hS" secondAttribute="trailing" constant="8" id="n5I-Yb-okr"/>
   6.198 -                                        <constraint firstItem="BvI-Zi-zU6" firstAttribute="trailing" secondItem="8QD-qP-0hS" secondAttribute="trailing" id="tYc-Ao-foD"/>
   6.199 -                                        <constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="BvI-Zi-zU6" secondAttribute="bottom" priority="950" constant="5" id="zul-c0-54S"/>
   6.200 +                                        <constraint firstAttribute="bottom" secondItem="id9-ga-Yy6" secondAttribute="bottom" id="N06-Fr-Vmu"/>
   6.201 +                                        <constraint firstItem="id9-ga-Yy6" firstAttribute="top" secondItem="jgQ-nZ-h1r" secondAttribute="top" id="Yfr-Lm-bxw"/>
   6.202 +                                        <constraint firstItem="id9-ga-Yy6" firstAttribute="leading" secondItem="jgQ-nZ-h1r" secondAttribute="leading" constant="5" id="h3X-97-D9m"/>
   6.203 +                                        <constraint firstAttribute="trailing" secondItem="id9-ga-Yy6" secondAttribute="trailing" constant="5" id="k3z-9G-pVO"/>
   6.204                                      </constraints>
   6.205                                  </tableViewCellContentView>
   6.206                                  <connections>
   6.207 @@ -165,10 +195,11 @@
   6.208                          <segue destination="NY2-HI-4ou" kind="presentation" identifier="segueReply" id="I15-Dm-oo1"/>
   6.209                          <segue destination="NY2-HI-4ou" kind="presentation" identifier="segueEditDraft" id="fJ0-cY-ZEb"/>
   6.210                          <segue destination="WY0-yJ-1SU" kind="presentation" identifier="segueShowMoveToFolder" id="d5O-Wk-gjZ"/>
   6.211 -                        <segue destination="lPr-gi-BrG" kind="showDetail" identifier="segueShowEmail" id="63y-AC-Fyu"/>
   6.212 +                        <segue destination="lPr-gi-BrG" kind="showDetail" identifier="segueShowEmailSplitView" id="63y-AC-Fyu"/>
   6.213                          <segue destination="wwj-Sj-78N" kind="showDetail" identifier="showNoMessage" id="a9I-b0-iqU"/>
   6.214                          <segue destination="eLD-PA-Nmx" kind="showDetail" identifier="segueShowThreadedEmail" id="rUh-Ai-iWA"/>
   6.215                          <segue destination="nc4-kR-K9D" kind="show" identifier="segueShowFilter" id="kQS-uu-oHl"/>
   6.216 +                        <segue destination="3bx-34-vLD" kind="show" identifier="segueShowEmailNotSplitView" id="5Kn-pc-9Jc"/>
   6.217                      </connections>
   6.218                  </tableViewController>
   6.219                  <placeholder placeholderIdentifier="IBFirstResponder" id="d8T-vB-XpT" userLabel="First Responder" sceneMemberID="firstResponder"/>
   6.220 @@ -917,9 +948,9 @@
   6.221          <image name="unread-icon" width="25" height="25"/>
   6.222      </resources>
   6.223      <inferredMetricsTieBreakers>
   6.224 -        <segue reference="RTR-We-Kc1"/>
   6.225 +        <segue reference="d5O-Wk-gjZ"/>
   6.226          <segue reference="0zl-tV-6Kr"/>
   6.227 -        <segue reference="anx-J8-OKE"/>
   6.228 -        <segue reference="Usl-iS-7fo"/>
   6.229 +        <segue reference="5Kn-pc-9Jc"/>
   6.230 +        <segue reference="fJ0-cY-ZEb"/>
   6.231      </inferredMetricsTieBreakers>
   6.232  </document>
     7.1 --- a/pEpForiOS/Base.lproj/Reusable.storyboard	Tue Jul 30 17:36:20 2019 +0200
     7.2 +++ b/pEpForiOS/Base.lproj/Reusable.storyboard	Mon Aug 19 09:32:27 2019 +0200
     7.3 @@ -101,7 +101,173 @@
     7.4                  </viewController>
     7.5                  <placeholder placeholderIdentifier="IBFirstResponder" id="TM4-zV-K5Y" userLabel="First Responder" sceneMemberID="firstResponder"/>
     7.6              </objects>
     7.7 -            <point key="canvasLocation" x="-879" y="-351"/>
     7.8 +            <point key="canvasLocation" x="-1065" y="-351"/>
     7.9 +        </scene>
    7.10 +        <!--Key Sync Handshake View Controller-->
    7.11 +        <scene sceneID="5ce-Hn-mnm">
    7.12 +            <objects>
    7.13 +                <viewController storyboardIdentifier="KeySyncHandshakeViewController" id="GZS-2u-rXb" customClass="KeySyncHandshakeViewController" customModule="pEpForiOS" customModuleProvider="target" sceneMemberID="viewController">
    7.14 +                    <view key="view" contentMode="scaleToFill" id="IdW-oi-ZvZ">
    7.15 +                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
    7.16 +                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
    7.17 +                        <subviews>
    7.18 +                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fiE-pm-mWd" customClass="KeyInputView" customModule="pEpForiOS" customModuleProvider="target">
    7.19 +                                <rect key="frame" x="57.5" y="135" width="260" height="417.5"/>
    7.20 +                                <subviews>
    7.21 +                                    <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="YMa-Kc-JoR">
    7.22 +                                        <rect key="frame" x="0.0" y="10" width="260" height="407.5"/>
    7.23 +                                        <subviews>
    7.24 +                                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="htI-ge-ZQj">
    7.25 +                                                <rect key="frame" x="12" y="0.0" width="236" height="30"/>
    7.26 +                                                <subviews>
    7.27 +                                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ckp-rp-1Pd">
    7.28 +                                                        <rect key="frame" x="96.5" y="4.5" width="43.5" height="20.5"/>
    7.29 +                                                        <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
    7.30 +                                                        <nil key="textColor"/>
    7.31 +                                                        <nil key="highlightedColor"/>
    7.32 +                                                    </label>
    7.33 +                                                    <button opaque="NO" tag="1" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6fr-an-tAC">
    7.34 +                                                        <rect key="frame" x="206" y="0.0" width="30" height="30"/>
    7.35 +                                                        <constraints>
    7.36 +                                                            <constraint firstAttribute="width" secondItem="6fr-an-tAC" secondAttribute="height" multiplier="1:1" id="sB9-cU-tpx"/>
    7.37 +                                                        </constraints>
    7.38 +                                                        <inset key="imageEdgeInsets" minX="10" minY="5" maxX="0.0" maxY="5"/>
    7.39 +                                                        <state key="normal" title="Button" image="pEpForiOS-icon-languagechange"/>
    7.40 +                                                        <connections>
    7.41 +                                                            <action selector="didPress:" destination="GZS-2u-rXb" eventType="touchUpInside" id="XwJ-Yx-Fbk"/>
    7.42 +                                                        </connections>
    7.43 +                                                    </button>
    7.44 +                                                </subviews>
    7.45 +                                                <constraints>
    7.46 +                                                    <constraint firstItem="6fr-an-tAC" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="ckp-rp-1Pd" secondAttribute="trailing" constant="10" id="Ai3-EY-gkI"/>
    7.47 +                                                    <constraint firstItem="6fr-an-tAC" firstAttribute="centerY" secondItem="htI-ge-ZQj" secondAttribute="centerY" id="DKf-9K-sdJ"/>
    7.48 +                                                    <constraint firstItem="6fr-an-tAC" firstAttribute="height" secondItem="htI-ge-ZQj" secondAttribute="height" id="Svc-uO-nhy"/>
    7.49 +                                                    <constraint firstAttribute="trailing" secondItem="6fr-an-tAC" secondAttribute="trailing" id="WuB-Op-Eer"/>
    7.50 +                                                    <constraint firstAttribute="height" constant="30" id="aO3-QP-t0p"/>
    7.51 +                                                    <constraint firstItem="ckp-rp-1Pd" firstAttribute="centerX" secondItem="htI-ge-ZQj" secondAttribute="centerX" id="gaV-xz-tKz"/>
    7.52 +                                                    <constraint firstItem="ckp-rp-1Pd" firstAttribute="centerY" secondItem="htI-ge-ZQj" secondAttribute="centerY" id="wj5-ro-Hwx"/>
    7.53 +                                                </constraints>
    7.54 +                                            </view>
    7.55 +                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rPi-Px-Pyp">
    7.56 +                                                <rect key="frame" x="12" y="40" width="236" height="17"/>
    7.57 +                                                <fontDescription key="fontDescription" type="system" pointSize="14"/>
    7.58 +                                                <nil key="textColor"/>
    7.59 +                                                <nil key="highlightedColor"/>
    7.60 +                                            </label>
    7.61 +                                            <label opaque="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" minimumFontSize="7" translatesAutoresizingMaskIntoConstraints="NO" id="GC3-wG-GHD">
    7.62 +                                                <rect key="frame" x="12" y="67" width="236" height="284.5"/>
    7.63 +                                                <gestureRecognizers/>
    7.64 +                                                <string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
    7.65 +                                                <fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
    7.66 +                                                <nil key="textColor"/>
    7.67 +                                                <nil key="highlightedColor"/>
    7.68 +                                                <connections>
    7.69 +                                                    <outletCollection property="gestureRecognizers" destination="qFT-Cr-cyd" appends="YES" id="Qd8-tn-R83"/>
    7.70 +                                                </connections>
    7.71 +                                            </label>
    7.72 +                                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="FhS-sc-rLm">
    7.73 +                                                <rect key="frame" x="0.0" y="361.5" width="260" height="46"/>
    7.74 +                                                <subviews>
    7.75 +                                                    <stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" spacing="1" translatesAutoresizingMaskIntoConstraints="NO" id="H3i-qp-hfc">
    7.76 +                                                        <rect key="frame" x="0.0" y="1" width="260" height="45"/>
    7.77 +                                                        <subviews>
    7.78 +                                                            <button opaque="NO" tag="2" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="emU-WV-Kap">
    7.79 +                                                                <rect key="frame" x="0.0" y="0.0" width="86" height="45"/>
    7.80 +                                                                <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="15"/>
    7.81 +                                                                <state key="normal" title="Button"/>
    7.82 +                                                                <connections>
    7.83 +                                                                    <action selector="didPress:" destination="GZS-2u-rXb" eventType="touchUpInside" id="Qh9-Xn-6Jk"/>
    7.84 +                                                                </connections>
    7.85 +                                                            </button>
    7.86 +                                                            <button opaque="NO" tag="3" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Rgh-E1-f1w">
    7.87 +                                                                <rect key="frame" x="87" y="0.0" width="86" height="45"/>
    7.88 +                                                                <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="15"/>
    7.89 +                                                                <state key="normal" title="Button"/>
    7.90 +                                                                <connections>
    7.91 +                                                                    <action selector="didPress:" destination="GZS-2u-rXb" eventType="touchUpInside" id="7oe-KK-94F"/>
    7.92 +                                                                </connections>
    7.93 +                                                            </button>
    7.94 +                                                            <button opaque="NO" tag="4" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="w5A-sP-MB2">
    7.95 +                                                                <rect key="frame" x="174" y="0.0" width="86" height="45"/>
    7.96 +                                                                <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="15"/>
    7.97 +                                                                <state key="normal" title="Button"/>
    7.98 +                                                                <connections>
    7.99 +                                                                    <action selector="didPress:" destination="GZS-2u-rXb" eventType="touchUpInside" id="cCN-kJ-4MM"/>
   7.100 +                                                                </connections>
   7.101 +                                                            </button>
   7.102 +                                                        </subviews>
   7.103 +                                                        <constraints>
   7.104 +                                                            <constraint firstAttribute="height" constant="45" id="T11-e3-GwM"/>
   7.105 +                                                        </constraints>
   7.106 +                                                    </stackView>
   7.107 +                                                </subviews>
   7.108 +                                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
   7.109 +                                                <constraints>
   7.110 +                                                    <constraint firstAttribute="bottom" secondItem="H3i-qp-hfc" secondAttribute="bottom" id="CgO-Nc-slt"/>
   7.111 +                                                    <constraint firstItem="H3i-qp-hfc" firstAttribute="leading" secondItem="FhS-sc-rLm" secondAttribute="leading" id="diT-6U-iIz"/>
   7.112 +                                                    <constraint firstAttribute="height" constant="46" id="it5-u7-ocl"/>
   7.113 +                                                    <constraint firstAttribute="trailing" secondItem="H3i-qp-hfc" secondAttribute="trailing" id="xoK-fB-cte"/>
   7.114 +                                                </constraints>
   7.115 +                                            </view>
   7.116 +                                        </subviews>
   7.117 +                                        <constraints>
   7.118 +                                            <constraint firstItem="htI-ge-ZQj" firstAttribute="leading" secondItem="YMa-Kc-JoR" secondAttribute="leading" constant="12" id="BcI-Ei-xIk"/>
   7.119 +                                            <constraint firstAttribute="trailing" secondItem="GC3-wG-GHD" secondAttribute="trailing" constant="12" id="Lxa-Jg-VAB"/>
   7.120 +                                            <constraint firstItem="GC3-wG-GHD" firstAttribute="leading" secondItem="YMa-Kc-JoR" secondAttribute="leading" constant="12" id="QeN-14-1s4"/>
   7.121 +                                            <constraint firstAttribute="trailing" secondItem="rPi-Px-Pyp" secondAttribute="trailing" constant="12" id="Te7-kU-Xea"/>
   7.122 +                                            <constraint firstAttribute="trailing" secondItem="FhS-sc-rLm" secondAttribute="trailing" id="UK3-Z6-QHQ"/>
   7.123 +                                            <constraint firstAttribute="trailing" secondItem="htI-ge-ZQj" secondAttribute="trailing" constant="12" id="Uu6-LM-wji"/>
   7.124 +                                            <constraint firstItem="FhS-sc-rLm" firstAttribute="leading" secondItem="YMa-Kc-JoR" secondAttribute="leading" id="fIH-2B-tWq"/>
   7.125 +                                            <constraint firstItem="rPi-Px-Pyp" firstAttribute="leading" secondItem="YMa-Kc-JoR" secondAttribute="leading" constant="12" id="w6P-ue-dqy"/>
   7.126 +                                        </constraints>
   7.127 +                                    </stackView>
   7.128 +                                </subviews>
   7.129 +                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
   7.130 +                                <constraints>
   7.131 +                                    <constraint firstItem="YMa-Kc-JoR" firstAttribute="leading" secondItem="fiE-pm-mWd" secondAttribute="leading" id="EAC-nr-iun"/>
   7.132 +                                    <constraint firstAttribute="width" constant="260" id="gIO-vA-Pn9"/>
   7.133 +                                    <constraint firstItem="YMa-Kc-JoR" firstAttribute="top" secondItem="fiE-pm-mWd" secondAttribute="top" constant="10" id="neq-VA-ZUu"/>
   7.134 +                                    <constraint firstAttribute="trailing" secondItem="YMa-Kc-JoR" secondAttribute="trailing" id="xBm-Xd-JL5"/>
   7.135 +                                    <constraint firstAttribute="bottom" secondItem="YMa-Kc-JoR" secondAttribute="bottom" id="xu3-OF-yp5"/>
   7.136 +                                </constraints>
   7.137 +                                <userDefinedRuntimeAttributes>
   7.138 +                                    <userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
   7.139 +                                        <integer key="value" value="15"/>
   7.140 +                                    </userDefinedRuntimeAttribute>
   7.141 +                                    <userDefinedRuntimeAttribute type="boolean" keyPath="clipsToBounds" value="YES"/>
   7.142 +                                </userDefinedRuntimeAttributes>
   7.143 +                            </view>
   7.144 +                        </subviews>
   7.145 +                        <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
   7.146 +                        <constraints>
   7.147 +                            <constraint firstItem="fiE-pm-mWd" firstAttribute="centerX" secondItem="O4B-B0-4XF" secondAttribute="centerX" id="48M-KR-Nr8"/>
   7.148 +                            <constraint firstItem="fiE-pm-mWd" firstAttribute="centerY" secondItem="O4B-B0-4XF" secondAttribute="centerY" id="OGZ-D8-d2h"/>
   7.149 +                            <constraint firstItem="O4B-B0-4XF" firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="fiE-pm-mWd" secondAttribute="bottom" constant="35" id="Qq0-YL-fDN"/>
   7.150 +                        </constraints>
   7.151 +                        <viewLayoutGuide key="safeArea" id="O4B-B0-4XF"/>
   7.152 +                    </view>
   7.153 +                    <connections>
   7.154 +                        <outlet property="accept" destination="w5A-sP-MB2" id="Lcb-sF-aRM"/>
   7.155 +                        <outlet property="alertTitle" destination="ckp-rp-1Pd" id="WIa-4m-pJy"/>
   7.156 +                        <outlet property="buttonsView" destination="FhS-sc-rLm" id="5Kd-Yz-Pub"/>
   7.157 +                        <outlet property="cancel" destination="emU-WV-Kap" id="DjI-VP-c4R"/>
   7.158 +                        <outlet property="contentView" destination="fiE-pm-mWd" id="eNm-5G-7Xa"/>
   7.159 +                        <outlet property="decline" destination="Rgh-E1-f1w" id="h3F-j9-O5m"/>
   7.160 +                        <outlet property="keySyncWords" destination="GC3-wG-GHD" id="D6t-PC-XAb"/>
   7.161 +                        <outlet property="message" destination="rPi-Px-Pyp" id="Pyb-o1-Nmp"/>
   7.162 +                    </connections>
   7.163 +                </viewController>
   7.164 +                <placeholder placeholderIdentifier="IBFirstResponder" id="rVu-Ti-LbX" userLabel="First Responder" sceneMemberID="firstResponder"/>
   7.165 +                <pongPressGestureRecognizer allowableMovement="10" minimumPressDuration="0.5" id="qFT-Cr-cyd">
   7.166 +                    <connections>
   7.167 +                        <action selector="didLongPressWords:" destination="GZS-2u-rXb" id="363-2M-vDw"/>
   7.168 +                    </connections>
   7.169 +                </pongPressGestureRecognizer>
   7.170 +            </objects>
   7.171 +            <point key="canvasLocation" x="-338.39999999999998" y="-345.87706146926541"/>
   7.172          </scene>
   7.173      </scenes>
   7.174 +    <resources>
   7.175 +        <image name="pEpForiOS-icon-languagechange" width="100" height="100"/>
   7.176 +    </resources>
   7.177  </document>
     8.1 --- a/pEpForiOS/Base.lproj/Settings.storyboard	Tue Jul 30 17:36:20 2019 +0200
     8.2 +++ b/pEpForiOS/Base.lproj/Settings.storyboard	Mon Aug 19 09:32:27 2019 +0200
     8.3 @@ -423,7 +423,7 @@
     8.4                                                      <rect key="frame" x="65" y="5" width="290" height="33.5"/>
     8.5                                                      <nil key="textColor"/>
     8.6                                                      <fontDescription key="fontDescription" type="system" pointSize="14"/>
     8.7 -                                                    <textInputTraits key="textInputTraits"/>
     8.8 +                                                    <textInputTraits key="textInputTraits" keyboardType="numberPad"/>
     8.9                                                  </textField>
    8.10                                              </subviews>
    8.11                                              <constraints>
    8.12 @@ -449,7 +449,7 @@
    8.13                                                      <nil key="textColor"/>
    8.14                                                      <nil key="highlightedColor"/>
    8.15                                                  </label>
    8.16 -                                                <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="rCJ-UW-Jmn">
    8.17 +                                                <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="rCJ-UW-Jmn" customClass="nonEditMenuUiTextField" customModule="pEpForiOS" customModuleProvider="target">
    8.18                                                      <rect key="frame" x="174.5" y="5" width="180.5" height="33.5"/>
    8.19                                                      <nil key="textColor"/>
    8.20                                                      <fontDescription key="fontDescription" type="system" pointSize="14"/>
    8.21 @@ -517,7 +517,7 @@
    8.22                                                      <rect key="frame" x="65" y="5" width="290" height="33.5"/>
    8.23                                                      <nil key="textColor"/>
    8.24                                                      <fontDescription key="fontDescription" type="system" pointSize="14"/>
    8.25 -                                                    <textInputTraits key="textInputTraits"/>
    8.26 +                                                    <textInputTraits key="textInputTraits" keyboardType="numberPad"/>
    8.27                                                  </textField>
    8.28                                              </subviews>
    8.29                                              <constraints>
    8.30 @@ -543,7 +543,7 @@
    8.31                                                      <nil key="textColor"/>
    8.32                                                      <nil key="highlightedColor"/>
    8.33                                                  </label>
    8.34 -                                                <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="JLc-di-cx2">
    8.35 +                                                <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="JLc-di-cx2" customClass="nonEditMenuUiTextField" customModule="pEpForiOS" customModuleProvider="target">
    8.36                                                      <rect key="frame" x="174.5" y="5" width="180.5" height="33.5"/>
    8.37                                                      <nil key="textColor"/>
    8.38                                                      <fontDescription key="fontDescription" type="system" pointSize="14"/>
     9.1 --- a/pEpForiOS/KeySync/KeySyncHandshakeService.swift	Tue Jul 30 17:36:20 2019 +0200
     9.2 +++ b/pEpForiOS/KeySync/KeySyncHandshakeService.swift	Mon Aug 19 09:32:27 2019 +0200
     9.3 @@ -21,7 +21,7 @@
     9.4                         partner: PEPIdentity,
     9.5                         completion: ((PEPSyncHandshakeResult)->())? = nil) {
     9.6  
     9.7 -        //BUFF: working simple alert version
     9.8 +        //Temp, working simple alert version
     9.9          DispatchQueue.main.async { [weak self] in
    9.10              guard let safeSelf = self else {
    9.11                  Log.shared.errorAndCrash("Lost myself")
    9.12 @@ -33,58 +33,38 @@
    9.13                  return
    9.14              }
    9.15  
    9.16 -            let lang = Locale.current.languageCode
    9.17 -            let trustwords = try! PEPSession().getTrustwordsFpr1(meFPR,
    9.18 -                                                                 fpr2: partnerFPR,
    9.19 -                                                                 language: lang,
    9.20 -                                                                 full: true)
    9.21 -
    9.22 -            // Show Handshake
    9.23 -            let newAlertView = UIAlertController.pEpAlertController(title: "Handshake",
    9.24 -                                                                    message: trustwords,
    9.25 -                                                                    preferredStyle: .alert)
    9.26 -            // Accept Action
    9.27 -            newAlertView.addAction(UIAlertAction(title: "Confirm Trustwords", style: .default) { action in
    9.28 -                completion?(PEPSyncHandshakeResult.accepted)
    9.29 -            })
    9.30 -
    9.31 -            // Reject Action
    9.32 -            newAlertView.addAction(UIAlertAction(title: "Wrong Trustwords", style: .destructive) { action in
    9.33 -                completion?(PEPSyncHandshakeResult.rejected)
    9.34 -            })
    9.35 -
    9.36 -            // Cancel Action
    9.37 -            newAlertView.addAction(UIAlertAction(title: "Cancel", style: .cancel) { action in
    9.38 -                completion?(PEPSyncHandshakeResult.cancel)
    9.39 -            })
    9.40 -            safeSelf.alertView = newAlertView
    9.41 -
    9.42 -            guard let vc = safeSelf.presenter else {
    9.43 +            guard let viewController = safeSelf.presenter else {
    9.44                  Log.shared.errorAndCrash("No Presenter")
    9.45                  return
    9.46              }
    9.47 -            vc.present(newAlertView, animated: true, completion: nil)
    9.48 +
    9.49 +            viewController.presentKeySyncHandShakeAlert(meFPR: meFPR, partnerFPR: partnerFPR)
    9.50 +            { action in
    9.51 +                switch action {
    9.52 +                case .accept:
    9.53 +                    completion?(PEPSyncHandshakeResult.accepted)
    9.54 +                case .cancel:
    9.55 +                    completion?(PEPSyncHandshakeResult.cancel)
    9.56 +                case .decline:
    9.57 +                    completion?(PEPSyncHandshakeResult.rejected)
    9.58 +                }
    9.59 +            }
    9.60          }
    9.61 +    }
    9.62  
    9.63 -        //        DispatchQueue.main.async { [weak self] in
    9.64 -        //            let session = Session()
    9.65 -        //            session.performAndWait {
    9.66 -        //                let meIdentity = Identity.newObject(onSession: session)
    9.67 -        //                meIdentity.fingerprint = me.fingerPrint
    9.68 -        //
    9.69 -        //                let partnerIdentity = Identity.newObject(onSession: session)
    9.70 -        //                partnerIdentity.fingerprint = partner.fingerPrint
    9.71 -        //            }
    9.72 -        //        }
    9.73 +    //!!!: unimplemented stub
    9.74 +    func showCurrentlyGroupingDevices() {
    9.75 +        // When implementing IOS-1712, show the additional (animated) view here.
    9.76 +        Log.shared.warn("Unimplemented stub. \n\n################################\n################################\nshowCurrentlyGroupingDevices called")
    9.77      }
    9.78  
    9.79      func cancelHandshake() {
    9.80 -        DispatchQueue.main.async { [weak self] in
    9.81 -            guard let me = self else {
    9.82 -                Log.shared.errorAndCrash("Lost myself")
    9.83 +        guard let keySyncHandShakeAlert  =
    9.84 +            presenter?.presentedViewController as? KeySyncHandshakeViewController else {
    9.85                  return
    9.86 -            }
    9.87 -            me.alertView?.dismiss(animated: true)
    9.88 +        }
    9.89 +        DispatchQueue.main.async {
    9.90 +            keySyncHandShakeAlert.dismiss(animated: true)
    9.91          }
    9.92      }
    9.93  
    10.1 --- a/pEpForiOS/Models/Folder+Extensions.swift	Tue Jul 30 17:36:20 2019 +0200
    10.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.3 @@ -1,32 +0,0 @@
    10.4 -//
    10.5 -//  Folder+Extensions.swift
    10.6 -//  pEp
    10.7 -//
    10.8 -//  Created by Andreas Buff on 31.08.18.
    10.9 -//  Copyright © 2018 p≡p Security S.A. All rights reserved.
   10.10 -//
   10.11 -
   10.12 -import MessageModel
   10.13 -
   10.14 -//!!!: must be moved to MM
   10.15 -extension Folder {
   10.16 -
   10.17 -    /// Whether or not messages with PEP-Rating_none should be displayed to the user.
   10.18 -    var showsMessagesNeverSeenByEngine : Bool {
   10.19 -        // In certain folder types (e.g.local folders), we want to display messages even they have
   10.20 -        // never met the Engine (and thus can not have a pEp rating).
   10.21 -        return folderType.isLocalFolder
   10.22 -    }
   10.23 -
   10.24 -    public func messageCount() -> Int {
   10.25 -        return  allMessagesNonThreaded().count //allCdMessagesCount(ignoringPepRating: showsMessagesNeverSeenByEngine) //!!!: let CD count please
   10.26 -    }
   10.27 -
   10.28 -    //!!!: should become obsolete
   10.29 -    public func messageAt(index: Int) -> Message? {
   10.30 -        if let message = allMessagesNonThreaded()[safe: index] {
   10.31 -            return message
   10.32 -        }
   10.33 -        return nil
   10.34 -    }
   10.35 -}
    11.1 --- a/pEpForiOS/Models/Folder+Threading.swift	Tue Jul 30 17:36:20 2019 +0200
    11.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.3 @@ -1,23 +0,0 @@
    11.4 -//
    11.5 -//  Folder+Threading.swift
    11.6 -//  pEp
    11.7 -//
    11.8 -//  Created by Andreas Buff on 31.08.18.
    11.9 -//  Copyright © 2018 p≡p Security S.A. All rights reserved.
   11.10 -//
   11.11 -
   11.12 -import MessageModel
   11.13 -
   11.14 -//!!!: uses CD. move to MM. Or better re-move.
   11.15 -extension Folder {
   11.16 -
   11.17 -    /**
   11.18 -     - Returns: All the messages contained in that folder in a flat and linear way,
   11.19 -     that is no threading involved.
   11.20 -     */
   11.21 -    public func allMessagesNonThreaded() -> [Message] {
   11.22 -        return
   11.23 -            allCdMessages(ignoringPepRating: showsMessagesNeverSeenByEngine)
   11.24 -                .compactMap { $0.message() }
   11.25 -    }
   11.26 -}
    12.1 --- a/pEpForiOS/UI/Compose/Cells/BodyCell/BodyCellViewModel.swift	Tue Jul 30 17:36:20 2019 +0200
    12.2 +++ b/pEpForiOS/UI/Compose/Cells/BodyCell/BodyCellViewModel.swift	Mon Aug 19 09:32:27 2019 +0200
    12.3 @@ -141,9 +141,9 @@
    12.4      }
    12.5  
    12.6      private func removeInlinedAttachments(_ removees: [Attachment]) {
    12.7 -        if removees.count > 0 {
    12.8 -            inlinedAttachments = inlinedAttachments.filter { !removees.contains($0) }
    12.9 -        }
   12.10 +        guard !removees.isEmpty else { return }
   12.11 +        inlinedAttachments = inlinedAttachments.filter { !removees.contains($0) } //Delete from message
   12.12 +        removees.forEach { $0.delete() } //Delete from session
   12.13      }
   12.14  }
   12.15  
    13.1 --- a/pEpForiOS/UI/Compose/ComposeTableViewController.swift	Tue Jul 30 17:36:20 2019 +0200
    13.2 +++ b/pEpForiOS/UI/Compose/ComposeTableViewController.swift	Mon Aug 19 09:32:27 2019 +0200
    13.3 @@ -288,8 +288,12 @@
    13.4                      Log.shared.errorAndCrash("Segue issue")
    13.5                      return
    13.6              }
    13.7 +            guard let vm = viewModel else {
    13.8 +                Log.shared.errorAndCrash("No vm")
    13.9 +                return
   13.10 +            }
   13.11              destination.appConfig = appConfig
   13.12 -            viewModel?.setup(handshakeViewController: destination)
   13.13 +            vm.setup(handshakeViewController: destination)
   13.14          }
   13.15      }
   13.16  }
    14.1 --- a/pEpForiOS/UI/Compose/Util/ComposeUtil.swift	Tue Jul 30 17:36:20 2019 +0200
    14.2 +++ b/pEpForiOS/UI/Compose/Util/ComposeUtil.swift	Mon Aug 19 09:32:27 2019 +0200
    14.3 @@ -143,24 +143,81 @@
    14.4  
    14.5      // MARK: - Message to send
    14.6  
    14.7 -    static public func messageToSend(
    14.8 -        withDataFrom state: ComposeViewModel.ComposeViewModelState) -> Message? {
    14.9 -        guard let from = state.from,
   14.10 -            let account = Account.by(address: from.address) else {
   14.11 +    /// Creates a message from the given ComposeView State
   14.12 +    ///
   14.13 +    /// - note: Must only be used on the main Session
   14.14 +    ///
   14.15 +    /// - Parameter state: state to get data from
   14.16 +    /// - Returns: new message with data from given state
   14.17 +    static public func messageToSend(withDataFrom state: ComposeViewModel.ComposeViewModelState) -> Message? {
   14.18 +        guard let from = state.from, let account = Account.by(address: from.address) else {
   14.19                  Log.shared.errorAndCrash(
   14.20                      "We have a problem here getting the senders account.")
   14.21                  return nil
   14.22          }
   14.23 -        guard let f = Folder.by(account: account, folderType: .outbox) else {
   14.24 +        return messageToSend(withDataFrom: state,
   14.25 +                             inAccount: account,
   14.26 +                             from: from,
   14.27 +                             toRecipients: state.toRecipients,
   14.28 +                             ccRecipients: state.ccRecipients,
   14.29 +                             bccRecipients: state.bccRecipients)
   14.30 +    }
   14.31 +
   14.32 +    /// Creates a message from the given ComposeView State
   14.33 +    ///
   14.34 +    /// - note: MUST NOT be used on the main Session. For the maion Session, use
   14.35 +    ///         messageToSend(withDataFrom:) instead.
   14.36 +    ///
   14.37 +    /// - Parameter state: state to get data from
   14.38 +    /// - Parameter session: session to work on. MUST NOT be the main Session.
   14.39 +    /// - Returns: new message with data from given state
   14.40 +    static public func messageToSend(withDataFrom state: ComposeViewModel.ComposeViewModelState,
   14.41 +                                     session: Session) -> Message? {
   14.42 +        var message: Message?
   14.43 +        session.performAndWait {
   14.44 +            guard let from = state.from?.safeForSession(session),
   14.45 +                let account = Account.by(address: from.address)?.safeForSession(session) else {
   14.46 +                    Log.shared.errorAndCrash(
   14.47 +                        "We have a problem here getting the senders account.")
   14.48 +                    return
   14.49 +            }
   14.50 +            let toRecipients = Identity.makeSafe(state.toRecipients, forSession: session)
   14.51 +            let ccRecipients = Identity.makeSafe(state.ccRecipients, forSession: session)
   14.52 +            let bccRecipients = Identity.makeSafe(state.bccRecipients, forSession: session)
   14.53 +            message = messageToSend(withDataFrom: state,
   14.54 +                                    inAccount: account,
   14.55 +                                    from: from,
   14.56 +                                    toRecipients: toRecipients,
   14.57 +                                    ccRecipients: ccRecipients,
   14.58 +                                    bccRecipients: bccRecipients,
   14.59 +                                    session: session)
   14.60 +        }
   14.61 +
   14.62 +        return message
   14.63 +    }
   14.64 +
   14.65 +    static private func messageToSend(withDataFrom state: ComposeViewModel.ComposeViewModelState,
   14.66 +                                      inAccount account: Account,
   14.67 +                                      from: Identity,
   14.68 +                                      toRecipients: [Identity],
   14.69 +                                      ccRecipients: [Identity],
   14.70 +                                      bccRecipients: [Identity],
   14.71 +                                      session: Session? = nil) -> Message? {
   14.72 +        guard let outbox = Folder.by(account: account, folderType: .outbox) else {
   14.73              Log.shared.errorAndCrash("No outbox")
   14.74              return nil
   14.75          }
   14.76 -
   14.77 -        let message = Message(uuid: MessageID.generate(), parentFolder: f)
   14.78 +        let message: Message
   14.79 +        if let safeSession = session {
   14.80 +            message = Message.newObject(onSession: safeSession)
   14.81 +        } else {
   14.82 +            message = Message(uuid: MessageID.generate(), parentFolder: outbox)
   14.83 +        }
   14.84 +        message.parent = outbox
   14.85          message.from = from
   14.86 -        message.replaceTo(with: state.toRecipients)
   14.87 -        message.replaceCc(with: state.ccRecipients)
   14.88 -        message.replaceBcc(with: state.bccRecipients)
   14.89 +        message.replaceTo(with: toRecipients)
   14.90 +        message.replaceCc(with: ccRecipients)
   14.91 +        message.replaceBcc(with: bccRecipients)
   14.92          message.shortMessage = state.subject
   14.93          message.longMessage = state.bodyPlaintext
   14.94          message.longMessageFormatted = !state.bodyHtml.isEmpty ? state.bodyHtml : nil
   14.95 @@ -177,6 +234,7 @@
   14.96  
   14.97          message.imapFlags.seen = imapSeenState(forMessageToSend: message)
   14.98  
   14.99 +
  14.100          return message
  14.101      }
  14.102  
    15.1 --- a/pEpForiOS/UI/Compose/ViewModel/ComposeViewModel.swift	Tue Jul 30 17:36:20 2019 +0200
    15.2 +++ b/pEpForiOS/UI/Compose/ViewModel/ComposeViewModel.swift	Mon Aug 19 09:32:27 2019 +0200
    15.3 @@ -548,6 +548,7 @@
    15.4              Log.shared.errorAndCrash("No data")
    15.5              return
    15.6          }
    15.7 +
    15.8          if data.isOutbox {
    15.9              data.originalMessage?.delete()
   15.10              if let message = data.originalMessage {
   15.11 @@ -566,6 +567,7 @@
   15.12              // Technically we have to create a new one and delete the original message, as the
   15.13              // mail is already synced with the IMAP server and thus we must not modify it.
   15.14              deleteOriginalMessage()
   15.15 +
   15.16              if data.isOutbox {
   15.17                  // Message will be saved (moved from user perspective) to drafts, but we are in
   15.18                  // outbox folder.
   15.19 @@ -604,7 +606,20 @@
   15.20      // There is no view model for HandshakeViewController yet, thus we are setting up the VC itself
   15.21      // as a workaround to avoid letting the VC know MessageModel
   15.22      func setup(handshakeViewController: HandshakeViewController) {
   15.23 -        handshakeViewController.message = ComposeUtil.messageToSend(withDataFrom: state)
   15.24 +        // We MUST use an independent Session here. We do not want the outer world to see it nor to
   15.25 +        //save it when saving the MainSession.
   15.26 +        let session = Session()
   15.27 +        let safeState = state.makeSafe(forSession: session)
   15.28 +        guard let msg = ComposeUtil.messageToSend(withDataFrom: safeState, session: session) else {
   15.29 +                Log.shared.errorAndCrash("No message")
   15.30 +                return
   15.31 +        }
   15.32 +        handshakeViewController.session = session
   15.33 +        session.performAndWait {
   15.34 +            handshakeViewController.message = msg
   15.35 +            let evaluator = RatingReEvaluator(message: msg)
   15.36 +            handshakeViewController.ratingReEvaluator = evaluator
   15.37 +        }
   15.38      }
   15.39  }
   15.40  
   15.41 @@ -613,6 +628,7 @@
   15.42  // MARK: RecipientCellViewModelResultDelegate
   15.43  
   15.44  extension ComposeViewModel: RecipientCellViewModelResultDelegate {
   15.45 +
   15.46      func recipientCellViewModel(_ vm: RecipientCellViewModel,
   15.47                                  didChangeRecipients newRecipients: [Identity]) {
   15.48          switch vm.type {
    16.1 --- a/pEpForiOS/UI/Compose/ViewModel/ComposeViewModelState.swift	Tue Jul 30 17:36:20 2019 +0200
    16.2 +++ b/pEpForiOS/UI/Compose/ViewModel/ComposeViewModelState.swift	Mon Aug 19 09:32:27 2019 +0200
    16.3 @@ -113,6 +113,38 @@
    16.4              edited = false
    16.5          }
    16.6  
    16.7 +        public func makeSafe(forSession session: Session) -> ComposeViewModelState {
    16.8 +            let newValue = ComposeViewModelState(initData: initData, delegate: nil)
    16.9 +
   16.10 +            newValue.toRecipients = Identity.makeSafe(toRecipients, forSession: session)
   16.11 +            newValue.ccRecipients = Identity.makeSafe(ccRecipients, forSession: session)
   16.12 +            newValue.bccRecipients = Identity.makeSafe(bccRecipients, forSession: session)
   16.13 +            if let from = from {
   16.14 +                newValue.from = Identity.makeSafe(from, forSession: session)
   16.15 +            }
   16.16 +            newValue.inlinedAttachments = Attachment.makeSafe(inlinedAttachments,
   16.17 +                                                              forSession: session)
   16.18 +            newValue.nonInlinedAttachments = Attachment.makeSafe(nonInlinedAttachments,
   16.19 +                                                                 forSession: session)
   16.20 +
   16.21 +
   16.22 +            newValue.isValidatedForSending = isValidatedForSending
   16.23 +            newValue.pEpProtection = pEpProtection
   16.24 +            newValue.bccWrapped = bccWrapped
   16.25 +            newValue.subject = subject
   16.26 +            newValue.bodyPlaintext = bodyPlaintext
   16.27 +            newValue.bodyHtml = bodyHtml
   16.28 +
   16.29 +
   16.30 +
   16.31 +            newValue.isValidatedForSending = isValidatedForSending
   16.32 +            newValue.rating = rating
   16.33 +            newValue.edited = edited
   16.34 +            newValue.delegate = delegate
   16.35 +
   16.36 +            return newValue
   16.37 +        }
   16.38 +
   16.39          public func setBccUnwrapped() {
   16.40              bccWrapped = false
   16.41          }
    17.1 --- a/pEpForiOS/UI/EmailDisplay/Background/AttachmentToLocalURLOperation.swift	Tue Jul 30 17:36:20 2019 +0200
    17.2 +++ b/pEpForiOS/UI/EmailDisplay/Background/AttachmentToLocalURLOperation.swift	Mon Aug 19 09:32:27 2019 +0200
    17.3 @@ -17,8 +17,7 @@
    17.4      private var safeAttachment: Attachment?
    17.5  
    17.6      init(attachment: Attachment) {
    17.7 -        let session = Session()
    17.8 -        self.session = session
    17.9 +        self.session = Session()
   17.10          self.safeAttachment = attachment.safeForSession(session)
   17.11          super.init()
   17.12      }
    18.1 --- a/pEpForiOS/UI/EmailDisplay/EmailViewController.swift	Tue Jul 30 17:36:20 2019 +0200
    18.2 +++ b/pEpForiOS/UI/EmailDisplay/EmailViewController.swift	Mon Aug 19 09:32:27 2019 +0200
    18.3 @@ -25,7 +25,7 @@
    18.4      var barItems: [UIBarButtonItem]?
    18.5  
    18.6      var message: Message?
    18.7 -    var folderShow : Folder?
    18.8 +    var folderShow: Folder?
    18.9      var messageId = 0
   18.10  
   18.11      var shouldShowOKButton: Bool = false
   18.12 @@ -98,6 +98,7 @@
   18.13  
   18.14      private func showPepRating() {
   18.15          if let privacyStatusIcon = showPepRating(pEpRating: message?.pEpRating()) {
   18.16 +
   18.17              let handshakeCombos = message?.handshakeActionCombinations() ?? []
   18.18              if !handshakeCombos.isEmpty {
   18.19                  let tapGestureRecognizer = UITapGestureRecognizer(
   18.20 @@ -424,7 +425,6 @@
   18.21          }
   18.22          Message.imapDelete(messages: [message])
   18.23          delegate?.emailDisplayDidDelete(message: message)
   18.24 -        navigationController?.popViewController(animated: true)
   18.25      }
   18.26  
   18.27      /**
   18.28 @@ -432,9 +432,8 @@
   18.29       */
   18.30      @IBAction func segueUnwindTrusted(segue: UIStoryboardSegue) {
   18.31          if let p = partnerIdentity {
   18.32 -            let session = PEPSession()
   18.33              do {
   18.34 -                try PEPUtil.trust(identity: p, session: session)
   18.35 +                try PEPUtils.trust(identity: p)
   18.36              } catch let error as NSError {
   18.37                  assertionFailure("\(error)")
   18.38              }
   18.39 @@ -447,9 +446,8 @@
   18.40       */
   18.41      @IBAction func segueUnwindUnTrusted(segue: UIStoryboardSegue) {
   18.42          if let p = partnerIdentity {
   18.43 -            let session = PEPSession()
   18.44              do {
   18.45 -                try PEPUtil.mistrust(identity: p, session: session)
   18.46 +                try PEPUtils.mistrust(identity: p)
   18.47              } catch let error as NSError {
   18.48                  assertionFailure("\(error)")
   18.49              }
   18.50 @@ -699,7 +697,8 @@
   18.51                  guard let url = attachmentOp.fileURL else {
   18.52                      return
   18.53                  }
   18.54 -                me.didCreateLocally(attachment: attachment,
   18.55 +                let safeAttachment = attachment.safeForSession(Session.main)
   18.56 +                me.didCreateLocally(attachment: safeAttachment,
   18.57                                         url: url,
   18.58                                         cell: cell,
   18.59                                         location: location,
    19.1 --- a/pEpForiOS/UI/EmailDisplayList/EmailListViewCell.swift	Tue Jul 30 17:36:20 2019 +0200
    19.2 +++ b/pEpForiOS/UI/EmailDisplayList/EmailListViewCell.swift	Mon Aug 19 09:32:27 2019 +0200
    19.3 @@ -38,6 +38,15 @@
    19.4  
    19.5      private var viewModel: MessageViewModel?
    19.6  
    19.7 +    /**
    19.8 +     Original selection background color
    19.9 +     - Note: When a cell is selected in edit mode the background color must be the same as
   19.10 +     unselected.
   19.11 +     For that reason we need to store the original selected background color to avoid loosing it
   19.12 +     if we need it in the future.
   19.13 +     */
   19.14 +    private var originalBackgroundSelectionColor: UIColor?
   19.15 +
   19.16      private var hasAttachment:Bool = false {
   19.17          didSet {
   19.18              if hasAttachment {
   19.19 @@ -72,6 +81,7 @@
   19.20  
   19.21      override func awakeFromNib() {
   19.22          super.awakeFromNib()
   19.23 +        originalBackgroundSelectionColor = selectedBackgroundView?.backgroundColor
   19.24          contactImageView.applyContactImageCornerRadius()
   19.25          resetToDefault()
   19.26      }
   19.27 @@ -220,10 +230,28 @@
   19.28          dateLabel.font = font
   19.29      }
   19.30  
   19.31 -    /**
   19.32 -     - Returns: " " (a space) instead of an empty String, otherwise the original String
   19.33 -     unchanged.
   19.34 -     */
   19.35 +
   19.36 +    /// This method highlights the cell that is being pressed.
   19.37 +    /// - Note: We only accept this if the cell is not in edit mode.
   19.38 +    override func setHighlighted(_ highlighted: Bool, animated: Bool) {
   19.39 +        if !isEditing {
   19.40 +            super.setHighlighted(highlighted, animated: animated)
   19.41 +        }
   19.42 +    }
   19.43 +
   19.44 +    override func setSelected(_ selected: Bool, animated: Bool) {
   19.45 +        super.setSelected(selected, animated: false)
   19.46 +        let viewForHighlight = UIView()
   19.47 +        self.selectedBackgroundView = viewForHighlight
   19.48 +        if self.isEditing {
   19.49 +            viewForHighlight.backgroundColor = UIColor.clear
   19.50 +        } else {
   19.51 +            viewForHighlight.backgroundColor = originalBackgroundSelectionColor
   19.52 +        }
   19.53 +    }
   19.54 +
   19.55 +    /// - Returns: " " (a space) instead of an empty String, otherwise the original String
   19.56 +    /// unchanged.
   19.57      private func atLeastOneSpace(possiblyEmptyString: String) -> String {
   19.58          if possiblyEmptyString == "" {
   19.59              return " "
    20.1 --- a/pEpForiOS/UI/EmailDisplayList/EmailListViewController.swift	Tue Jul 30 17:36:20 2019 +0200
    20.2 +++ b/pEpForiOS/UI/EmailDisplayList/EmailListViewController.swift	Mon Aug 19 09:32:27 2019 +0200
    20.3 @@ -20,6 +20,8 @@
    20.4      }
    20.5  
    20.6      public static let storyboardId = "EmailListViewController"
    20.7 +    /// This is used to handle the selection row when it recives an update
    20.8 +    /// and also when swipeCellAction is performed to store from which cell the action is done.
    20.9      private var lastSelectedIndexPath: IndexPath?
   20.10  
   20.11      let searchController = UISearchController(searchResultsController: nil)
   20.12 @@ -80,7 +82,7 @@
   20.13      }
   20.14  
   20.15      override func viewWillDisappear(_ animated: Bool) {
   20.16 -        guard let isIphone = splitViewController?.isCollapsed, let last = lastSelectedIndexPath else {
   20.17 +        guard let isIphone = splitViewController?.isCollapsed else {
   20.18              return
   20.19          }
   20.20          if !isIphone {
   20.21 @@ -217,8 +219,16 @@
   20.22          performSegue(withIdentifier: SegueIdentifier.segueEditDraft, sender: self)
   20.23      }
   20.24  
   20.25 +    /// we have to handle the ipad/iphone segue in a different way. see IOS-1737
   20.26      private func showEmail(forCellAt indexPath: IndexPath) {
   20.27 -        performSegue(withIdentifier: SegueIdentifier.segueShowEmail, sender: self)
   20.28 +        guard let splitViewController = self.splitViewController else {
   20.29 +            return
   20.30 +        }
   20.31 +        if splitViewController.isCollapsed {
   20.32 +            performSegue(withIdentifier: SegueIdentifier.segueShowEmailNotSplitView, sender: self)
   20.33 +        } else {
   20.34 +            performSegue(withIdentifier: SegueIdentifier.segueShowEmailSplitView, sender: self)
   20.35 +        }
   20.36      }
   20.37  
   20.38      private func showNoMessageSelectedIfNeeded() {
   20.39 @@ -427,12 +437,6 @@
   20.40          }
   20.41      }
   20.42  
   20.43 -    private func resetSelectionIfNeeded(for indexPath: IndexPath) {
   20.44 -        if lastSelectedIndexPath == indexPath {
   20.45 -            resetSelection()
   20.46 -        }
   20.47 -    }
   20.48 -
   20.49      private func resetSelection() {
   20.50          tableView.selectRow(at: lastSelectedIndexPath, animated: false, scrollPosition: .none)
   20.51      }
   20.52 @@ -499,11 +503,6 @@
   20.53  
   20.54      override func tableView(_ tableView: UITableView,
   20.55                              cellForRowAt indexPath: IndexPath) -> UITableViewCell {
   20.56 -        if lastSelectedIndexPath == indexPath {
   20.57 -            defer {
   20.58 -                tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
   20.59 -            }
   20.60 -        }
   20.61  
   20.62          let cell = tableView.dequeueReusableCell(withIdentifier: EmailListViewCell.storyboardId,
   20.63                                                   for: indexPath)
   20.64 @@ -518,6 +517,11 @@
   20.65              Log.shared.errorAndCrash("dequeued wrong cell")
   20.66          }
   20.67  
   20.68 +        //restores selection state for updated or replaced cells.
   20.69 +        if lastSelectedIndexPath == indexPath {
   20.70 +            tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
   20.71 +        }
   20.72 +
   20.73          return cell
   20.74      }
   20.75  
   20.76 @@ -800,7 +804,7 @@
   20.77      }
   20.78  
   20.79      func emailListViewModel(viewModel: EmailListViewModel, didInsertDataAt indexPaths: [IndexPath]) {
   20.80 -        lastSelectedIndexPath = tableView.indexPathForSelectedRow
   20.81 +        lastSelectedIndexPath = nil
   20.82          tableView.insertRows(at: indexPaths, with: .automatic)
   20.83      }
   20.84  
   20.85 @@ -818,14 +822,9 @@
   20.86              showNoMessageSelectedIfNeeded()
   20.87          }
   20.88      }
   20.89 -    //!!!: comented code probably not needed anymore. if something strange appears, check this.
   20.90 -    //!!!: the reselection of the cell is performed in the cell for row. 
   20.91      func emailListViewModel(viewModel: EmailListViewModel, didUpdateDataAt indexPaths: [IndexPath]) {
   20.92          lastSelectedIndexPath = tableView.indexPathForSelectedRow
   20.93          tableView.reloadRows(at: indexPaths, with: .none)
   20.94 -//        for indexPath in indexPaths {
   20.95 -//            resetSelectionIfNeeded(for: indexPath)
   20.96 -//        }
   20.97      }
   20.98  
   20.99      func emailListViewModel(viewModel: EmailListViewModel, didMoveData atIndexPath: IndexPath, toIndexPath: IndexPath) {
  20.100 @@ -1055,7 +1054,8 @@
  20.101      
  20.102      enum SegueIdentifier: String {
  20.103          case segueAddNewAccount
  20.104 -        case segueShowEmail
  20.105 +        case segueShowEmailSplitView
  20.106 +        case segueShowEmailNotSplitView
  20.107          case segueCompose
  20.108          case segueReply
  20.109          case segueReplyAll
  20.110 @@ -1078,7 +1078,7 @@
  20.111               .segueCompose,
  20.112               .segueEditDraft:
  20.113              setupComposeViewController(for: segue)
  20.114 -        case .segueShowEmail:
  20.115 +        case .segueShowEmailSplitView:
  20.116              guard let nav = segue.destination as? UINavigationController,
  20.117                  let vc = nav.rootViewController as? EmailViewController,
  20.118                  let indexPath = lastSelectedIndexPath,
  20.119 @@ -1088,11 +1088,30 @@
  20.120              }
  20.121              vc.appConfig = appConfig
  20.122              vc.message = message
  20.123 +            ///This is commented as we "disabled" the feature in the message of
  20.124 +            ///showing next and previous directly from the emailView, that is needed for that feature
  20.125              //vc.folderShow = model?.getFolderToShow()
  20.126              vc.messageId = indexPath.row //!!!: that looks wrong
  20.127              vc.delegate = model
  20.128              model?.currentDisplayedMessage = vc
  20.129              model?.indexPathShown = indexPath
  20.130 +        case .segueShowEmailNotSplitView:
  20.131 +            guard let vc = segue.destination as? EmailViewController,
  20.132 +                let indexPath = lastSelectedIndexPath,
  20.133 +                let message = model?.message(representedByRowAt: indexPath) else {
  20.134 +                    Log.shared.errorAndCrash("Segue issue")
  20.135 +                    return
  20.136 +            }
  20.137 +            vc.appConfig = appConfig
  20.138 +            vc.message = message
  20.139 +            ///This is commented as we "disabled" the feature in the message of
  20.140 +            ///showing next and previous directly from the emailView, that is needed for that feature
  20.141 +            //vc.folderShow = model?.getFolderToShow()
  20.142 +            vc.messageId = indexPath.row //!!!: that looks wrong
  20.143 +            vc.delegate = model
  20.144 +            model?.currentDisplayedMessage = vc
  20.145 +            model?.indexPathShown = indexPath
  20.146 +
  20.147        //  case .segueShowThreadedEmail:
  20.148          /*    guard let nav = segue.destination as? UINavigationController,
  20.149                  let vc = nav.rootViewController as? ThreadViewController,
  20.150 @@ -1140,8 +1159,6 @@
  20.151                  return
  20.152              }
  20.153              vC.appConfig = appConfig
  20.154 -            //!!!: was commented. Is this dead code? if so, rm!
  20.155 -            //vC.hidesBottomBarWhenPushed = true
  20.156              break
  20.157          case .segueShowMoveToFolder:
  20.158              var selectedRows: [IndexPath] = []
    21.1 --- a/pEpForiOS/UI/EmailDisplayList/EmailListViewModel+EmailDisplayDelegate.swift	Tue Jul 30 17:36:20 2019 +0200
    21.2 +++ b/pEpForiOS/UI/EmailDisplayList/EmailListViewModel+EmailDisplayDelegate.swift	Mon Aug 19 09:32:27 2019 +0200
    21.3 @@ -10,6 +10,10 @@
    21.4  import pEpIOSToolbox
    21.5  import MessageModel
    21.6  //!!!: this delegates must be fixed
    21.7 +/*
    21.8 + As far as I can see EmailDisplayDelegate is for communication master <-> detail view.
    21.9 +THis communication is currently disabled. We need to come up with a final concept first and then rewrite it.
   21.10 + */
   21.11  extension EmailListViewModel: EmailDisplayDelegate {
   21.12      func emailDisplayDidFlag(message: Message) {
   21.13          /*guard let index = indexPathShown?.row else {
   21.14 @@ -49,38 +53,37 @@
   21.15      }
   21.16  
   21.17      private func deleteRow(for message: Message) {
   21.18 -        DispatchQueue.main.async { [weak self] in
   21.19 -            guard let index = self?.index(of: message) else {
   21.20 -                return
   21.21 -            }
   21.22 -            self?.informDeleteRow(at: index)
   21.23 -        }
   21.24 -
   21.25 +//        DispatchQueue.main.async { [weak self] in
   21.26 +//            guard let index = self?.index(of: message) else {
   21.27 +//                return
   21.28 +//            }
   21.29 +//            self?.informDeleteRow(at: index)
   21.30 +//        }
   21.31      }
   21.32  
   21.33  
   21.34      private func updateRow(for message: Message, isSeenStateChange: Bool = false) {
   21.35 -        DispatchQueue.main.async { [weak self] in
   21.36 -            guard let index = self?.index(of: message) else {
   21.37 -                return
   21.38 -            }
   21.39 -            if isSeenStateChange {
   21.40 -                self?.informSeenStateChangeForRow(at: index)
   21.41 -            } else {
   21.42 -                self?.informUpdateRow(at: index)
   21.43 -            }
   21.44 -        }
   21.45 +//        DispatchQueue.main.async { [weak self] in
   21.46 +//            guard let index = self?.index(of: message) else {
   21.47 +//                return
   21.48 +//            }
   21.49 +//            if isSeenStateChange {
   21.50 +//                self?.informSeenStateChangeForRow(at: index)
   21.51 +//            } else {
   21.52 +//                self?.informUpdateRow(at: index)
   21.53 +//            }
   21.54 +//        }
   21.55      }
   21.56  
   21.57      ///!!!: change this to work with the proccees like messageQueryResults
   21.58 -    private func informUpdateRow(at index: Int) {
   21.59 -        let indexPath = self.indexPath(for: index)
   21.60 -        //!!!: example of how messageQueryResults communicates with the EmailListVM
   21.61 -        emailListViewModelDelegate?.willReceiveUpdates(viewModel: self)
   21.62 -        emailListViewModelDelegate?.emailListViewModel(viewModel: self,
   21.63 -                                                       didUpdateDataAt: [indexPath])
   21.64 -        emailListViewModelDelegate?.allUpdatesReceived(viewModel: self)
   21.65 -    }
   21.66 +//    private func informUpdateRow(at index: Int) {
   21.67 +//        let indexPath = self.indexPath(for: index)
   21.68 +//        //!!!: example of how messageQueryResults communicates with the EmailListVM
   21.69 +//        emailListViewModelDelegate?.willReceiveUpdates(viewModel: self)
   21.70 +//        emailListViewModelDelegate?.emailListViewModel(viewModel: self,
   21.71 +//                                                       didUpdateDataAt: [indexPath])
   21.72 +//        emailListViewModelDelegate?.allUpdatesReceived(viewModel: self)
   21.73 +//    }
   21.74  
   21.75      private func informSeenStateChangeForRow(at index: Int) {
   21.76          let indexPath = self.indexPath(for: index)
    22.1 --- a/pEpForiOS/UI/EmailDisplayList/EmailListViewModel.swift	Tue Jul 30 17:36:20 2019 +0200
    22.2 +++ b/pEpForiOS/UI/EmailDisplayList/EmailListViewModel.swift	Mon Aug 19 09:32:27 2019 +0200
    22.3 @@ -164,10 +164,6 @@
    22.4  
    22.5      // MARK: - Public Data Access & Manipulation
    22.6  
    22.7 -    func index(of message: Message) -> Int? {
    22.8 -        return nil
    22.9 -    }
   22.10 -
   22.11      func viewModel(for index: Int) -> MessageViewModel? {
   22.12          let messageViewModel = MessageViewModel(with: messageQueryResults[index],
   22.13                                                  queue: queueForHeavyStuff)
   22.14 @@ -193,7 +189,7 @@
   22.15              return nil
   22.16          }
   22.17          let message = messageQueryResults[indexPath.row]
   22.18 -        let color = PEPUtil.pEpColor(pEpRating: message.pEpRating())
   22.19 +        let color = PEPUtils.pEpColor(pEpRating: message.pEpRating())
   22.20          if color != PEPColor.noColor {
   22.21              return color.statusIcon()
   22.22          } else {
    23.1 --- a/pEpForiOS/UI/EmailDisplayList/PepPictureComposer.swift	Tue Jul 30 17:36:20 2019 +0200
    23.2 +++ b/pEpForiOS/UI/EmailDisplayList/PepPictureComposer.swift	Mon Aug 19 09:32:27 2019 +0200
    23.3 @@ -39,7 +39,7 @@
    23.4          let safeMsg = message.safeForSession(session)
    23.5          DispatchQueue.global(qos: .userInitiated).async {
    23.6              session.performAndWait {
    23.7 -                let color = PEPUtil.pEpColor(pEpRating: safeMsg.pEpRating())
    23.8 +                let color = PEPUtils.pEpColor(pEpRating: safeMsg.pEpRating())
    23.9                  var image: UIImage? = nil
   23.10                  if color != PEPColor.noColor {
   23.11                      image = color.statusIconInContactPicture()
    24.1 --- a/pEpForiOS/UI/Handshake/HandshakeViewController.swift	Tue Jul 30 17:36:20 2019 +0200
    24.2 +++ b/pEpForiOS/UI/Handshake/HandshakeViewController.swift	Mon Aug 19 09:32:27 2019 +0200
    24.3 @@ -12,6 +12,9 @@
    24.4  import MessageModel
    24.5  import PEPObjCAdapterFramework
    24.6  
    24.7 +/// READ!!!
    24.8 +/// Only set session, if the message was create on a private session. Else leave it nil.
    24.9 +/// Or i will just crash or maybe deadlock you :)
   24.10  class HandshakeViewController: BaseTableViewController {
   24.11      private var backTitle: String?
   24.12      private var currentLanguageCode = Locale.current.languageCode ?? "en"
   24.13 @@ -21,17 +24,12 @@
   24.14  
   24.15      var message: Message? {
   24.16          didSet {
   24.17 -            handshakeCombinations = message?.handshakeActionCombinations() ?? []
   24.18 +            handshakePartnerTableViewCellViewModel = handshakePartnerTableViewCellViewModels()
   24.19          }
   24.20      }
   24.21  
   24.22 -    var handshakeCombinations = [HandshakeCombination]()
   24.23 -    //    { //BUFF: can we reuse the view by setting the combinations for KeySync? I am afraid it is not a good idea?
   24.24 -//        didSet {
   24.25 -//            tableView.reloadData()
   24.26 -//        }
   24.27 -//    }
   24.28 -
   24.29 +    var session: Session?
   24.30 +    var handshakePartnerTableViewCellViewModel = [HandshakePartnerTableViewCellViewModel]()
   24.31      var ratingReEvaluator: RatingReEvaluator?
   24.32  
   24.33      // MARK: - Life Cycle
   24.34 @@ -96,7 +94,7 @@
   24.35  extension HandshakeViewController {
   24.36  
   24.37      private func updateStatusBadge() {
   24.38 -        self.showPepRating(pEpRating: message?.pEpRating())
   24.39 +        showPepRating(pEpRating: message?.pEpRating())
   24.40      }
   24.41  
   24.42      // MARK: - UI & Layout
   24.43 @@ -136,6 +134,24 @@
   24.44          return buttonLeft
   24.45      }
   24.46  
   24.47 +    private func  handshakePartnerTableViewCellViewModels() -> [HandshakePartnerTableViewCellViewModel] {
   24.48 +        var result = [HandshakePartnerTableViewCellViewModel]()
   24.49 +        guard let message = message else {
   24.50 +            Log.shared.errorAndCrash("Fail to init handshakePartnerTableViewCellViewModel, since message is nil")
   24.51 +            return []
   24.52 +        }
   24.53 +        let handShakeCombinations = message.handshakeActionCombinations()
   24.54 +        for handshakeCombi in handShakeCombinations {
   24.55 +            guard let cellViewModel = createViewModel(partnerIdentity: handshakeCombi.partnerIdentity,
   24.56 +                                                      selfIdentity: handshakeCombi.ownIdentity) else {
   24.57 +                                                        Log.shared.errorAndCrash("Fail to init handshakePartnerTableViewCellViewModel")
   24.58 +                                                        continue
   24.59 +            }
   24.60 +            result.append(cellViewModel)
   24.61 +        }
   24.62 +        return result
   24.63 +    }
   24.64 +
   24.65      /// Adjusts the background color of the given view model depending on its position in the list,
   24.66      /// and the color of the previous one.
   24.67      private func adjustBackgroundColor(viewModel: HandshakePartnerTableViewCellViewModel,
   24.68 @@ -144,9 +160,7 @@
   24.69              viewModel.backgroundColorDark = false
   24.70          } else {
   24.71              let prevRow = indexPath.row - 1
   24.72 -            let handshakeCombo = handshakeCombinations[prevRow]
   24.73 -            let prevViewModel = createViewModel(partnerIdentity: handshakeCombo.partnerIdentity,
   24.74 -                                                selfIdentity: handshakeCombo.ownIdentity)
   24.75 +            let prevViewModel = handshakePartnerTableViewCellViewModel[prevRow]
   24.76              if prevViewModel.showTrustwords {
   24.77                  viewModel.backgroundColorDark = true
   24.78              } else {
   24.79 @@ -181,7 +195,7 @@
   24.80  extension HandshakeViewController {
   24.81  
   24.82      override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
   24.83 -        return handshakeCombinations.count
   24.84 +        return  handshakePartnerTableViewCellViewModel.count
   24.85      }
   24.86  
   24.87      override func tableView(_ tableView: UITableView,
   24.88 @@ -191,9 +205,7 @@
   24.89              for: indexPath) as? HandshakePartnerTableViewCell {
   24.90              cell.delegate = self
   24.91  
   24.92 -            let handshakeCombo = handshakeCombinations[indexPath.row]
   24.93 -            let viewModel = createViewModel(partnerIdentity: handshakeCombo.partnerIdentity,
   24.94 -                                            selfIdentity: handshakeCombo.ownIdentity)
   24.95 +            let viewModel = handshakePartnerTableViewCellViewModel[indexPath.row]
   24.96              adjustBackgroundColor(viewModel: viewModel, indexPath: indexPath)
   24.97              viewModel.trustwordsLanguage = currentLanguageCode
   24.98              cell.viewModel = viewModel
   24.99 @@ -232,14 +244,26 @@
  24.100  
  24.101      ///Returns: A cached view model for the given `partnerIdentity` or a newly created one.
  24.102      private func createViewModel(partnerIdentity: Identity,
  24.103 -                                 selfIdentity: Identity) -> HandshakePartnerTableViewCellViewModel {
  24.104 +                                 selfIdentity: Identity) -> HandshakePartnerTableViewCellViewModel? {
  24.105          if let vm = identityViewModelCache.object(forKey: partnerIdentity) {
  24.106              return vm
  24.107          } else {
  24.108 -            let vm = HandshakePartnerTableViewCellViewModel(ownIdentity: selfIdentity,
  24.109 +            var vm: HandshakePartnerTableViewCellViewModel?
  24.110 +            if let session = session {
  24.111 +                session.performAndWait {
  24.112 +                    vm = HandshakePartnerTableViewCellViewModel(ownIdentity: selfIdentity,
  24.113 +                                                                partner: partnerIdentity)
  24.114 +                }
  24.115 +            } else {
  24.116 +                vm = HandshakePartnerTableViewCellViewModel(ownIdentity: selfIdentity,
  24.117                                                              partner: partnerIdentity)
  24.118 -            identityViewModelCache.setObject(vm, forKey: partnerIdentity)
  24.119 -            return vm
  24.120 +            }
  24.121 +            guard let safeVM = vm else {
  24.122 +                Log.shared.errorAndCrash("Fail to init HandshakePartnerTableViewCellViewModel")
  24.123 +                return nil
  24.124 +            }
  24.125 +            identityViewModelCache.setObject(safeVM, forKey: partnerIdentity)
  24.126 +            return safeVM
  24.127          }
  24.128      }
  24.129  }
  24.130 @@ -263,7 +287,7 @@
  24.131          // reload cells after that one, to ensure the alternating colors are upheld
  24.132          var paths = [IndexPath]()
  24.133          let i1 = indexPath.row + 1
  24.134 -        let i2 = handshakeCombinations.count
  24.135 +        let i2 = handshakePartnerTableViewCellViewModel.count
  24.136          if i1 < i2 {
  24.137              for i in i1..<i2 {
  24.138                  paths.append(IndexPath(row: i, section: indexPath.section))
  24.139 @@ -311,10 +335,13 @@
  24.140  
  24.141  extension HandshakeViewController : RatingReEvaluatorDelegate {
  24.142      func ratingChanged(message: Message) {
  24.143 -        DispatchQueue.main.async {
  24.144 -            self.updateStatusBadge()
  24.145 +        DispatchQueue.main.async { [weak self] in
  24.146 +            guard let me = self else {
  24.147 +                Log.shared.errorAndCrash("Lost myself")
  24.148 +                return
  24.149 +            }
  24.150 +            me.updateStatusBadge()
  24.151          }
  24.152 -
  24.153      }
  24.154  }
  24.155  
    25.1 --- a/pEpForiOS/UI/Handshake/ViewModel/HandshakePartnerTableViewCellViewModel.swift	Tue Jul 30 17:36:20 2019 +0200
    25.2 +++ b/pEpForiOS/UI/Handshake/ViewModel/HandshakePartnerTableViewCellViewModel.swift	Mon Aug 19 09:32:27 2019 +0200
    25.3 @@ -78,10 +78,12 @@
    25.4       */
    25.5      let ownIdentity: Identity
    25.6  
    25.7 +    private let partnerIdentity: Identity
    25.8 +
    25.9      /**
   25.10       Cache the updated own identity.
   25.11       */
   25.12 -    let pEpSelf: PEPIdentity
   25.13 +    var pEpSelf: PEPIdentity
   25.14  
   25.15      /**
   25.16       Cache the updated partner identity.
   25.17 @@ -93,12 +95,12 @@
   25.18      init(ownIdentity: Identity, partner: Identity) {
   25.19          self.expandedState = .notExpanded
   25.20          self.trustwordsLanguage = "en"
   25.21 -        self.partnerRating = PEPUtil.pEpRating(identity: partner)
   25.22 +        self.ownIdentity = ownIdentity
   25.23 +        self.partnerIdentity = partner
   25.24 +        self.partnerRating = PEPUtils.pEpRating(identity: partner)
   25.25 +        self.pEpPartner = partner.updatedIdentity()
   25.26 +        self.pEpSelf = ownIdentity.updatedIdentity()
   25.27          self.partnerColor = partnerRating.pEpColor()
   25.28 -        self.ownIdentity = ownIdentity
   25.29 -
   25.30 -        pEpSelf = ownIdentity.updatedIdentity()
   25.31 -        pEpPartner = partner.updatedIdentity()
   25.32  
   25.33          do {
   25.34              isPartnerpEpUser = try PEPSession().isPEPUser(pEpPartner).boolValue
   25.35 @@ -106,7 +108,10 @@
   25.36              Log.shared.error("%@", "\(err)")
   25.37              isPartnerpEpUser = false
   25.38          }
   25.39 -        setPartnerImage(for: partner)
   25.40 +
   25.41 +        partnerIdentity.session.performAndWait { [weak self] in
   25.42 +            self?.setPartnerImage(for: partner)
   25.43 +        }
   25.44          updateTrustwords()
   25.45      }
   25.46  
   25.47 @@ -170,7 +175,7 @@
   25.48  
   25.49          do {
   25.50              partnerRating = try PEPSession().rating(for: pEpPartner).pEpRating
   25.51 -            partnerColor = PEPUtil.pEpColor(pEpRating: partnerRating)
   25.52 +            partnerColor = PEPUtils.pEpColor(pEpRating: partnerRating)
   25.53          } catch let error as NSError {
   25.54              assertionFailure("\(error)")
   25.55          }
    26.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    26.2 +++ b/pEpForiOS/UI/KeySyncHandshake/KeySyncHandshakeViewController.swift	Mon Aug 19 09:32:27 2019 +0200
    26.3 @@ -0,0 +1,176 @@
    26.4 +//
    26.5 +//  KeySyncHandshakeViewController.swift
    26.6 +//  pEp
    26.7 +//
    26.8 +//  Created by Alejandro Gelos on 05/07/2019.
    26.9 +//  Copyright © 2019 p≡p Security S.A. All rights reserved.
   26.10 +//
   26.11 +
   26.12 +import UIKit
   26.13 +
   26.14 +final class KeySyncHandshakeViewController: UIViewController {
   26.15 +    enum Action {
   26.16 +        case cancel, decline, accept
   26.17 +    }
   26.18 +
   26.19 +    static let storyboardId = "KeySyncHandshakeViewController"
   26.20 +    
   26.21 +    @IBOutlet weak var keySyncWords: UILabel! {
   26.22 +        didSet {
   26.23 +            keySyncWords.backgroundColor = .pEpLightBackground
   26.24 +            keySyncWords.layer.borderColor = UIColor.pEpGreyLines.cgColor
   26.25 +            keySyncWords.layer.cornerRadius = 3
   26.26 +            keySyncWords.layer.borderWidth = 1
   26.27 +        }
   26.28 +    }
   26.29 +    @IBOutlet weak var contentView: KeyInputView! {
   26.30 +        didSet {
   26.31 +            contentView.backgroundColor = .pEpGreyBackground
   26.32 +            let languangePicker = UIPickerView()
   26.33 +            languangePicker.dataSource = self
   26.34 +            languangePicker.delegate = self
   26.35 +            contentView.inputView = languangePicker
   26.36 +        }
   26.37 +    }
   26.38 +    @IBOutlet weak var alertTitle: UILabel! {
   26.39 +        didSet {
   26.40 +            let alertTittle = NSLocalizedString("p≡p Sync", comment: "keySync handshake alert title")
   26.41 +            alertTitle.attributedText = alertTittle.paintPEPToPEPColour()
   26.42 +        }
   26.43 +    }
   26.44 +    @IBOutlet weak var message: UILabel! {
   26.45 +        didSet {
   26.46 +            message.text = NSLocalizedString("A second device is detected. \nPlease confirm the Trustwords on both devices to sync all your privacy. Shall we synchronize?", comment: "keySync handshake alert message")
   26.47 +        }
   26.48 +    }
   26.49 +
   26.50 +    @IBOutlet weak var accept: UIButton! {
   26.51 +        didSet {
   26.52 +            accept.setTitleColor(.pEpGreen, for: .normal)
   26.53 +            accept.setTitle(NSLocalizedString("Sync", comment: "accept hand shake sync button"), for: .normal)
   26.54 +            accept.backgroundColor = .pEpGreyBackground
   26.55 +        }
   26.56 +    }
   26.57 +    @IBOutlet weak var decline: UIButton! {
   26.58 +        didSet {
   26.59 +            decline.setTitleColor(.pEpRed, for: .normal)
   26.60 +            decline.setTitle(NSLocalizedString("Decline", comment: "decline button"), for: .normal)
   26.61 +            decline.backgroundColor = .pEpGreyBackground
   26.62 +        }
   26.63 +    }
   26.64 +    @IBOutlet weak var cancel: UIButton! {
   26.65 +        didSet {
   26.66 +            cancel.setTitleColor(.pEpGreyText, for: .normal)
   26.67 +            cancel.setTitle(NSLocalizedString("Not Now", comment: "not now button"), for: .normal)
   26.68 +            cancel.backgroundColor = .pEpGreyBackground
   26.69 +        }
   26.70 +    }
   26.71 +    @IBOutlet weak var buttonsView: UIView! {
   26.72 +        didSet {
   26.73 +            buttonsView.backgroundColor = .pEpGreyButtonLines
   26.74 +        }
   26.75 +    }
   26.76 +
   26.77 +    private let viewModel = KeySyncHandshakeViewModel()
   26.78 +    private var pickerLanguages = [String]()
   26.79 +    private var meFPR: String?
   26.80 +    private var partnerFPR: String?
   26.81 +
   26.82 +    override func viewDidLoad() {
   26.83 +        super.viewDidLoad()
   26.84 +        viewModel.delegate = self
   26.85 +        viewModel.fingerPrints(meFPR: meFPR, partnerFPR: partnerFPR)
   26.86 +    }
   26.87 +
   26.88 +    func finderPrints(meFPR: String, partnerFPR: String) {
   26.89 +        self.meFPR = meFPR
   26.90 +        self.partnerFPR = partnerFPR
   26.91 +    }
   26.92 +
   26.93 +    @IBAction func didPress(_ sender: UIButton) {
   26.94 +        guard let action = pressedAction(tag: sender.tag) else {
   26.95 +            return
   26.96 +        }
   26.97 +        viewModel.handle(action: action)
   26.98 +    }
   26.99 +
  26.100 +    @IBAction func didLongPressWords(_ sender: UILongPressGestureRecognizer) {
  26.101 +        guard sender.state == .began else {
  26.102 +            return
  26.103 +        }
  26.104 +        viewModel.didLongPressWords()
  26.105 +    }
  26.106 +
  26.107 +    func completionHandler(_ block: @escaping (Action) -> Void) {
  26.108 +        viewModel.completionHandler = block
  26.109 +    }
  26.110 +}
  26.111 +
  26.112 +// MARK: - KeySyncHandshakeViewModelDelegate
  26.113 +
  26.114 +extension KeySyncHandshakeViewController: KeySyncHandshakeViewModelDelegate {
  26.115 +    func dissmissView() {
  26.116 +        DispatchQueue.main.async { [weak self] in
  26.117 +            self?.dismiss(animated: true, completion: nil)
  26.118 +        }
  26.119 +    }
  26.120 +
  26.121 +    func showPicker(withLanguages languages: [String]) {
  26.122 +        pickerLanguages = languages
  26.123 +        DispatchQueue.main.async { [weak self] in
  26.124 +            self?.contentView.becomeFirstResponder()
  26.125 +        }
  26.126 +    }
  26.127 +
  26.128 +    func closePicker() {
  26.129 +        DispatchQueue.main.async { [weak self] in
  26.130 +            self?.contentView.resignFirstResponder()
  26.131 +        }
  26.132 +    }
  26.133 +
  26.134 +    func change(handshakeWordsTo: String) {
  26.135 +        DispatchQueue.main.async { [weak self] in
  26.136 +            self?.keySyncWords.text = handshakeWordsTo
  26.137 +        }
  26.138 +    }
  26.139 +}
  26.140 +
  26.141 +// MARK: - UIPickerViewDelegate
  26.142 +extension KeySyncHandshakeViewController: UIPickerViewDelegate {
  26.143 +    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
  26.144 +        return pickerLanguages[row]
  26.145 +    }
  26.146 +
  26.147 +    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
  26.148 +        viewModel.didSelect(languageRow: row)
  26.149 +    }
  26.150 +}
  26.151 +
  26.152 +// MARK: - UIPickerViewDataSource
  26.153 +extension KeySyncHandshakeViewController: UIPickerViewDataSource {
  26.154 +    func numberOfComponents(in pickerView: UIPickerView) -> Int {
  26.155 +        return 1
  26.156 +    }
  26.157 +
  26.158 +    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
  26.159 +        return pickerLanguages.count
  26.160 +    }
  26.161 +}
  26.162 +
  26.163 +// MARK: - Private
  26.164 +extension KeySyncHandshakeViewController {
  26.165 +    private func pressedAction(tag: Int) -> KeySyncHandshakeViewModel.Action? {
  26.166 +        switch tag {
  26.167 +        case 1:
  26.168 +            return .changeLanguage
  26.169 +        case 2:
  26.170 +            return .cancel
  26.171 +        case 3:
  26.172 +            return .decline
  26.173 +        case 4:
  26.174 +            return .accept
  26.175 +        default:
  26.176 +            return nil
  26.177 +        }
  26.178 +    }
  26.179 +}
    27.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    27.2 +++ b/pEpForiOS/UI/KeySyncHandshake/KeySyncHandshakeViewModel.swift	Mon Aug 19 09:32:27 2019 +0200
    27.3 @@ -0,0 +1,120 @@
    27.4 +//
    27.5 +//  KeySyncHandshakeViewModel.swift
    27.6 +//  pEp
    27.7 +//
    27.8 +//  Created by Alejandro Gelos on 05/07/2019.
    27.9 +//  Copyright © 2019 p≡p Security S.A. All rights reserved.
   27.10 +//
   27.11 +
   27.12 +import Foundation
   27.13 +import PEPObjCAdapterFramework
   27.14 +
   27.15 +protocol KeySyncHandshakeViewModelDelegate: class {
   27.16 +    func dissmissView()
   27.17 +    func showPicker(withLanguages languages: [String])
   27.18 +    func closePicker()
   27.19 +    func change(handshakeWordsTo: String)
   27.20 +}
   27.21 +
   27.22 +final class KeySyncHandshakeViewModel {
   27.23 +    enum Action {
   27.24 +        case cancel, decline, accept, changeLanguage
   27.25 +    }
   27.26 +
   27.27 +    var completionHandler: ((KeySyncHandshakeViewController.Action) -> Void)?
   27.28 +
   27.29 +    weak var delegate: KeySyncHandshakeViewModelDelegate?
   27.30 +    var fullTrustWords = false //Internal since testing
   27.31 +    private var languageCode = Locale.current.languageCode
   27.32 +    private var meFPR: String?
   27.33 +    private var partnerFPR: String?
   27.34 +    private let pEpSession: PEPSessionProtocol
   27.35 +    private var _languages = [PEPLanguage]()
   27.36 +    private var languages: [PEPLanguage] {
   27.37 +        guard _languages.isEmpty else {
   27.38 +            return _languages
   27.39 +        }
   27.40 +        do {
   27.41 +            _languages = try pEpSession.languageList()
   27.42 +            return _languages
   27.43 +        } catch {
   27.44 +            Log.shared.errorAndCrash("%@", error.localizedDescription)
   27.45 +            return []
   27.46 +        }
   27.47 +    }
   27.48 +
   27.49 +    init(pEpSession: PEPSessionProtocol = PEPSession()) {
   27.50 +        self.pEpSession = pEpSession
   27.51 +    }
   27.52 +
   27.53 +    func didSelect(languageRow: Int) {
   27.54 +        languageCode = languages[languageRow].code
   27.55 +        delegate?.closePicker()
   27.56 +        delegate?.change(handshakeWordsTo: trustWorkds())
   27.57 +    }
   27.58 +
   27.59 +    func handle(action: Action) {
   27.60 +        switch action {
   27.61 +        case .accept, .cancel, .decline:
   27.62 +            guard let action = viewControllerAction(viewModelAction: action) else {
   27.63 +                return
   27.64 +            }
   27.65 +            completionHandler?(action)
   27.66 +            delegate?.dissmissView()
   27.67 +        case .changeLanguage:
   27.68 +            handleChangeLanguageButton()
   27.69 +        }
   27.70 +    }
   27.71 +
   27.72 +    func fingerPrints(meFPR: String?, partnerFPR: String?) {
   27.73 +        self.meFPR = meFPR
   27.74 +        self.partnerFPR = partnerFPR
   27.75 +        delegate?.change(handshakeWordsTo: trustWorkds())
   27.76 +    }
   27.77 +
   27.78 +    func didLongPressWords() {
   27.79 +        fullTrustWords = !fullTrustWords
   27.80 +        delegate?.change(handshakeWordsTo: trustWorkds())
   27.81 +    }
   27.82 +}
   27.83 +
   27.84 +// MARK: - Private
   27.85 +
   27.86 +extension KeySyncHandshakeViewModel {
   27.87 +    private func trustWorkds() -> String {
   27.88 +        guard let meFPR = meFPR, let partnerFPR = partnerFPR else {
   27.89 +            Log.shared.errorAndCrash("Nil meFingerPrints or Nil partnerFingerPrints")
   27.90 +            return String()
   27.91 +        }
   27.92 +        do {
   27.93 +            return try pEpSession.getTrustwordsFpr1(meFPR, fpr2: partnerFPR, language: languageCode,
   27.94 +                                                      full: fullTrustWords)
   27.95 +        } catch {
   27.96 +            Log.shared.errorAndCrash("%@", error.localizedDescription)
   27.97 +            return ""
   27.98 +        }
   27.99 +    }
  27.100 +
  27.101 +    private func handleChangeLanguageButton() {
  27.102 +        guard !languages.isEmpty else {
  27.103 +            Log.shared.errorAndCrash("Wont show picker, no languages to show")
  27.104 +            return
  27.105 +        }
  27.106 +        let languagesNames = languages.map { $0.name }
  27.107 +        delegate?.showPicker(withLanguages: languagesNames)
  27.108 +    }
  27.109 +
  27.110 +    private func viewControllerAction(viewModelAction: KeySyncHandshakeViewModel.Action)
  27.111 +        -> KeySyncHandshakeViewController.Action? {
  27.112 +            switch viewModelAction {
  27.113 +            case .accept:
  27.114 +                return .accept
  27.115 +            case .cancel:
  27.116 +                return .cancel
  27.117 +            case .decline:
  27.118 +                return .decline
  27.119 +            case .changeLanguage:
  27.120 +                return nil
  27.121 +            }
  27.122 +    }
  27.123 +}
    28.1 --- a/pEpForiOS/UI/Settings/Setting/AccountSettings/AccountSettingsTableViewController.swift	Tue Jul 30 17:36:20 2019 +0200
    28.2 +++ b/pEpForiOS/UI/Settings/Setting/AccountSettings/AccountSettingsTableViewController.swift	Mon Aug 19 09:32:27 2019 +0200
    28.3 @@ -92,6 +92,7 @@
    28.4          let imap = viewModel?.imapServer
    28.5          self.imapServerTextfield.text = imap?.address
    28.6          self.imapPortTextfield.text = imap?.port
    28.7 +        imapPortTextfield.delegate = self
    28.8          self.imapSecurityTextfield.text = imap?.transport
    28.9          self.imapSecurityTextfield.inputView = securityPicker
   28.10          self.imapSecurityTextfield.delegate = self
   28.11 @@ -100,6 +101,7 @@
   28.12          let smtp = viewModel?.smtpServer
   28.13          self.smtpServerTextfield.text = smtp?.address
   28.14          self.smtpPortTextfield.text = smtp?.port
   28.15 +        smtpPortTextfield.delegate = self
   28.16          self.smtpSecurityTextfield.text = smtp?.transport
   28.17          self.smtpSecurityTextfield.inputView = securityPicker
   28.18          self.smtpSecurityTextfield.delegate = self
   28.19 @@ -292,6 +294,13 @@
   28.20          if textField == passwordTextfield {
   28.21              passWordChanged = true
   28.22          }
   28.23 +        if textField == smtpPortTextfield || textField == imapPortTextfield {
   28.24 +            if string.isBackspace {
   28.25 +                return true
   28.26 +            }
   28.27 +            return string.isDigits
   28.28 +        }
   28.29 +
   28.30          return true
   28.31      }
   28.32  
   28.33 @@ -318,6 +327,7 @@
   28.34                      inComponent component: Int) {
   28.35          if let c = current, let vm = viewModel {
   28.36              c.text = vm.svm[row]
   28.37 +            self.view.endEditing(true)
   28.38          }
   28.39      }
   28.40  }
    29.1 --- a/pEpForiOS/UI/Settings/Setting/OnOffSettings/ViewModel/EnableKeySyncViewModel.swift	Tue Jul 30 17:36:20 2019 +0200
    29.2 +++ b/pEpForiOS/UI/Settings/Setting/OnOffSettings/ViewModel/EnableKeySyncViewModel.swift	Mon Aug 19 09:32:27 2019 +0200
    29.3 @@ -16,7 +16,7 @@
    29.4      var cellIdentifier = "switchOptionCell"
    29.5      private let messageModelService: MessageModelServiceProtocol
    29.6  
    29.7 -    private(set) var title = NSLocalizedString("Key Sync Enable",
    29.8 +    private(set) var title = NSLocalizedString("Enable Key Sync",
    29.9                                                 comment: "enable key sync with other devices in the group")
   29.10  
   29.11      init(_ messageModelService: MessageModelServiceProtocol) {
    30.1 --- a/pEpForiOS/UI/Settings/ViewModel/SettingsCellViewModel.swift	Tue Jul 30 17:36:20 2019 +0200
    30.2 +++ b/pEpForiOS/UI/Settings/ViewModel/SettingsCellViewModel.swift	Mon Aug 19 09:32:27 2019 +0200
    30.3 @@ -89,6 +89,24 @@
    30.4      }
    30.5  
    30.6      func delete() {
    30.7 -        account?.delete()
    30.8 +        guard let acc = account else {
    30.9 +            Log.shared.errorAndCrash(message: "Account lost")
   30.10 +            return
   30.11 +        }
   30.12 +
   30.13 +        let oldAddress = acc.user.address
   30.14 +        acc.delete()
   30.15 +
   30.16 +        if AppSettings.defaultAccount == oldAddress {
   30.17 +            let newDefaultAccount = Account.all().first
   30.18 +            guard let newDefaultAddress = newDefaultAccount?.user.address else {
   30.19 +                return
   30.20 +                //no more accounts, no default account
   30.21 +            }
   30.22 +            AppSettings.defaultAccount = newDefaultAddress
   30.23 +        }
   30.24 +
   30.25      }
   30.26 +
   30.27 +
   30.28  }
    31.1 --- a/pEpForiOS/UI/Settings/ViewModel/SettingsSectionViewModel.swift	Tue Jul 30 17:36:20 2019 +0200
    31.2 +++ b/pEpForiOS/UI/Settings/ViewModel/SettingsSectionViewModel.swift	Mon Aug 19 09:32:27 2019 +0200
    31.3 @@ -59,10 +59,12 @@
    31.4          }
    31.5      }
    31.6  
    31.7 -    private func generateKeySyncCells(_ messageModelService: MessageModelServiceProtocol) {
    31.8 -        cells.append(EnableKeySyncViewModel(messageModelService))
    31.9 -        if isInDeviceGroup() {
   31.10 -            cells.append(SettingsActionCellViewModel(type: .leaveKeySyncGroup))
   31.11 +    func removeLeaveDeviceGroupCell() {
   31.12 +        cells.removeAll { cell in
   31.13 +            guard let actionCell = cell as? SettingsActionCellViewModel else {
   31.14 +                return false
   31.15 +            }
   31.16 +            return actionCell.type == .leaveKeySyncGroup
   31.17          }
   31.18      }
   31.19  
   31.20 @@ -79,8 +81,8 @@
   31.21      }
   31.22  
   31.23      func delete(cell: Int) {
   31.24 -        if let remove = cells[cell] as? SettingsCellViewModel {
   31.25 -            remove.delete()
   31.26 +        if let cellToRemove = cells[cell] as? SettingsCellViewModel {
   31.27 +            cellToRemove.delete()
   31.28              cells.remove(at: cell)
   31.29          }
   31.30      }
   31.31 @@ -113,4 +115,11 @@
   31.32          }
   31.33          return keySyncDeviceGroupService.deviceGroupState == .grouped
   31.34      }
   31.35 +
   31.36 +    private func generateKeySyncCells(_ messageModelService: MessageModelServiceProtocol) {
   31.37 +        cells.append(EnableKeySyncViewModel(messageModelService))
   31.38 +        if isInDeviceGroup() {
   31.39 +            cells.append(SettingsActionCellViewModel(type: .leaveKeySyncGroup))
   31.40 +        }
   31.41 +    }
   31.42  }
    32.1 --- a/pEpForiOS/UI/Settings/ViewModel/SettingsViewModel.swift	Tue Jul 30 17:36:20 2019 +0200
    32.2 +++ b/pEpForiOS/UI/Settings/ViewModel/SettingsViewModel.swift	Mon Aug 19 09:32:27 2019 +0200
    32.3 @@ -45,6 +45,7 @@
    32.4          }
    32.5          do {
    32.6              try keySyncDeviceGroupService.leaveDeviceGroup()
    32.7 +            removeLeaveDeviceGroupCell()
    32.8          } catch {
    32.9              Log.shared.errorAndCrash("%@", error.localizedDescription)
   32.10              return error
   32.11 @@ -80,4 +81,13 @@
   32.12      private func sectionIsValid(section: Int) -> Bool {
   32.13          return section >= 0 && section < sections.count
   32.14      }
   32.15 +
   32.16 +    private func removeLeaveDeviceGroupCell() {
   32.17 +        for section in sections {
   32.18 +            guard section.type == .keySync else {
   32.19 +                continue
   32.20 +            }
   32.21 +            section.removeLeaveDeviceGroupCell()
   32.22 +        }
   32.23 +    }
   32.24  }
    33.1 --- a/pEpForiOS/UI/Util/Extensions/UIViewController+Extension.swift	Tue Jul 30 17:36:20 2019 +0200
    33.2 +++ b/pEpForiOS/UI/Util/Extensions/UIViewController+Extension.swift	Mon Aug 19 09:32:27 2019 +0200
    33.3 @@ -40,4 +40,22 @@
    33.4              return nil
    33.5          }
    33.6      }
    33.7 +
    33.8 +    func presentKeySyncHandShakeAlert(meFPR: String, partnerFPR: String,
    33.9 +                        completion: @escaping (KeySyncHandshakeViewController.Action) -> Void ) {
   33.10 +
   33.11 +        let storyboard = UIStoryboard(name: Constants.suggestionsStoryboard, bundle: .main)
   33.12 +        guard let handShakeViewController = storyboard.instantiateViewController(
   33.13 +            withIdentifier: KeySyncHandshakeViewController.storyboardId) as? KeySyncHandshakeViewController else {
   33.14 +                Log.shared.errorAndCrash("Fail to instantiateViewController KeySyncHandshakeViewController")
   33.15 +                return
   33.16 +        }
   33.17 +        handShakeViewController.completionHandler { action in
   33.18 +            completion(action)
   33.19 +        }
   33.20 +        handShakeViewController.finderPrints(meFPR: meFPR, partnerFPR: partnerFPR)
   33.21 +
   33.22 +        handShakeViewController.modalPresentationStyle = .overFullScreen
   33.23 +        present(handShakeViewController, animated: true, completion: nil)
   33.24 +    }
   33.25  }
    34.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    34.2 +++ b/pEpForiOS/UI/Util/Extensions/nonPasteableUiTextField.swift	Mon Aug 19 09:32:27 2019 +0200
    34.3 @@ -0,0 +1,16 @@
    34.4 +//
    34.5 +//  nonPasteableUiTextField.swift
    34.6 +//  pEp
    34.7 +//
    34.8 +//  Created by Xavier Algarra on 18/07/2019.
    34.9 +//  Copyright © 2019 p≡p Security S.A. All rights reserved.
   34.10 +//
   34.11 +
   34.12 +import Foundation
   34.13 +
   34.14 +class nonEditMenuUiTextField: UITextField {
   34.15 +
   34.16 +    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
   34.17 +        return false
   34.18 +    }
   34.19 +}
    35.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    35.2 +++ b/pEpForiOS/UI/Util/KeyInputView.swift	Mon Aug 19 09:32:27 2019 +0200
    35.3 @@ -0,0 +1,30 @@
    35.4 +//
    35.5 +//  KeyInputView.swift
    35.6 +//  pEp
    35.7 +//
    35.8 +//  Created by Alejandro Gelos on 15/07/2019.
    35.9 +//  Copyright © 2019 p≡p Security S.A. All rights reserved.
   35.10 +//
   35.11 +
   35.12 +import UIKit
   35.13 +
   35.14 +class KeyInputView: UIView {
   35.15 +    var _inputView: UIView?
   35.16 +
   35.17 +    override var canBecomeFirstResponder: Bool { return true }
   35.18 +    override var canResignFirstResponder: Bool { return true }
   35.19 +
   35.20 +    override var inputView: UIView? {
   35.21 +        set { _inputView = newValue }
   35.22 +        get { return _inputView }
   35.23 +    }
   35.24 +}
   35.25 +
   35.26 +// MARK: - UIKeyInput
   35.27 +
   35.28 +//Modify if need more functionality
   35.29 +extension KeyInputView: UIKeyInput {
   35.30 +    var hasText: Bool { return false }
   35.31 +    func insertText(_ text: String) {}
   35.32 +    func deleteBackward() {}
   35.33 +}
    36.1 --- a/pEpForiOS/UI/Util/PEP+UI.swift	Tue Jul 30 17:36:20 2019 +0200
    36.2 +++ b/pEpForiOS/UI/Util/PEP+UI.swift	Mon Aug 19 09:32:27 2019 +0200
    36.3 @@ -76,7 +76,7 @@
    36.4  
    36.5  extension PEPRating {
    36.6      func uiColor() -> UIColor? {
    36.7 -        return PEPUtil.pEpColor(pEpRating: self).uiColor()
    36.8 +        return PEPUtils.pEpColor(pEpRating: self).uiColor()
    36.9      }
   36.10  
   36.11      func statusIcon() -> UIImage? {
    37.1 --- a/pEpForiOS/UI/Util/UnifiedInbox.swift	Tue Jul 30 17:36:20 2019 +0200
    37.2 +++ b/pEpForiOS/UI/Util/UnifiedInbox.swift	Mon Aug 19 09:32:27 2019 +0200
    37.3 @@ -33,7 +33,7 @@
    37.4              predicates.append(CdMessage.PredicateFactory.isInInbox())
    37.5              predicates.append(CdMessage.PredicateFactory.existingMessages())
    37.6              predicates.append(CdMessage.PredicateFactory.processed())
    37.7 -            predicates.append(CdMessage.PredicateFactory.isNotAutoconsumable())
    37.8 +            predicates.append(Message.PredicateFactory.isNotAutoConsumable())
    37.9              return NSCompoundPredicate(andPredicateWithSubpredicates: predicates)
   37.10          }
   37.11      }
    38.1 --- a/pEpForiOS/Util/Identity+pEp.swift	Tue Jul 30 17:36:20 2019 +0200
    38.2 +++ b/pEpForiOS/Util/Identity+pEp.swift	Mon Aug 19 09:32:27 2019 +0200
    38.3 @@ -17,7 +17,7 @@
    38.4  
    38.5      public func decorateButton(button: UIButton) {
    38.6          button.setTitleColor(.black, for: .normal)
    38.7 -        if let color = PEPUtil.pEpColor(identity: self).uiColor() {
    38.8 +        if let color = PEPUtils.pEpColor(identity: self).uiColor() {
    38.9              button.backgroundColor = color
   38.10          } else {
   38.11              let buttonDefault = UIButton()
    39.1 --- a/pEpForiOS/Util/ReplyUtil.swift	Tue Jul 30 17:36:20 2019 +0200
    39.2 +++ b/pEpForiOS/Util/ReplyUtil.swift	Mon Aug 19 09:32:27 2019 +0200
    39.3 @@ -20,7 +20,7 @@
    39.4          guard let quotedText = quotedText(for: message) else {
    39.5              return "\n\n\(footer())"
    39.6          }
    39.7 -        let citation = citationHeaderForMessage(message, replyAll: replyAll)
    39.8 +        let citation = citationHeaderForMessage(message)
    39.9  
   39.10          return "\n\n\(footer())\n\n\(citation)\n\n\(quotedText)"
   39.11      }
   39.12 @@ -31,8 +31,8 @@
   39.13      ///   - textToCite: text to cite
   39.14      ///   - msg: message to take data (sender, date sent ...) from
   39.15      /// - Returns: text with citation header and "send by pEp" footer
   39.16 -     static func citedMessageText(textToCite: String, fromMessage msg: Message) -> String {
   39.17 -        let citation = citationHeaderForMessage(msg, replyAll: false)
   39.18 +    static func citedMessageText(textToCite: String, fromMessage msg: Message) -> String {
   39.19 +        let citation = citationHeaderForMessage(msg)
   39.20          return "\n\n\(footer())\n\n\(citation)\n\n\(textToCite)"
   39.21      }
   39.22  
   39.23 @@ -44,7 +44,7 @@
   39.24      /// - Returns: text with citation header and "send by pEp" footer
   39.25      public static func citedMessageText(textToCite: NSAttributedString,
   39.26                                          fromMessage msg: Message) -> NSAttributedString {
   39.27 -        let citation = citationHeaderForMessage(msg, replyAll: false)
   39.28 +        let citation = citationHeaderForMessage(msg)
   39.29  
   39.30          let defaultFont = UIFont.preferredFont(forTextStyle: .body)
   39.31          var result = NSAttributedString(string: "\n\n\(footer())\n\n\(citation)\n\n",
   39.32 @@ -132,7 +132,7 @@
   39.33          return quotedText
   39.34      }
   39.35  
   39.36 -    static private func citationHeaderForMessage(_ message: Message, replyAll: Bool) -> String {
   39.37 +    static private func citationHeaderForMessage(_ message: Message) -> String {
   39.38          let dateFormatter = DateFormatter.init()
   39.39          dateFormatter.dateStyle = DateFormatter.Style.long
   39.40          dateFormatter.timeStyle = DateFormatter.Style.long
   39.41 @@ -140,17 +140,11 @@
   39.42          let theDate = message.sent
   39.43  
   39.44          var theNames = [String]()
   39.45 -        if replyAll {
   39.46 -            let contacts = message.allRecipients
   39.47 -            theNames.append(
   39.48 -                contentsOf: contacts.map() { return replyNameFromIdentity($0) })
   39.49 -        } else {
   39.50 -            if let from = message.from {
   39.51 -                theNames.append(replyNameFromIdentity(from))
   39.52 -            }
   39.53 +        if let from = message.from {
   39.54 +            theNames.append(replyNameFromIdentity(from))
   39.55          }
   39.56  
   39.57 -        if theNames.count == 0 {
   39.58 +        if theNames.isEmpty {
   39.59              if let rd = theDate {
   39.60                  return String.localizedStringWithFormat(
   39.61                      NSLocalizedString("Someone wrote on %1$@:",
   39.62 @@ -160,7 +154,7 @@
   39.63                  return NSLocalizedString("Someone wrote:",
   39.64                                           comment: "Reply to unknown sender without date")
   39.65              }
   39.66 -        } else if theNames.count == 1 {
   39.67 +        } else {
   39.68              if let rd = theDate {
   39.69                  return String.localizedStringWithFormat(
   39.70                      NSLocalizedString(
   39.71 @@ -174,21 +168,6 @@
   39.72                          comment: "Reply to single contact, without date. Placeholder: Name."),
   39.73                      theNames[0])
   39.74              }
   39.75 -        } else {
   39.76 -            let allNames = theNames.joined(separator: nameSeparator)
   39.77 -            if let rd = theDate {
   39.78 -                return String.localizedStringWithFormat(
   39.79 -                    NSLocalizedString(
   39.80 -                        "%1$@ wrote on %@:",
   39.81 -                        comment: "Reply to multiple contacts, with date. Placeholder: Name."),
   39.82 -                    allNames, dateFormatter.string(from: rd as Date))
   39.83 -            } else {
   39.84 -                return String.localizedStringWithFormat(
   39.85 -                    NSLocalizedString(
   39.86 -                        "%1$@ wrote:",
   39.87 -                        comment: "Reply to multiple contacts, without date. Placeholder: Names"),
   39.88 -                    allNames)
   39.89 -            }
   39.90          }
   39.91      }
   39.92  
    40.1 --- a/pEpForiOSTests/Background/FetchNumberOfNewMailsServiceTest.swift	Tue Jul 30 17:36:20 2019 +0200
    40.2 +++ b/pEpForiOSTests/Background/FetchNumberOfNewMailsServiceTest.swift	Mon Aug 19 09:32:27 2019 +0200
    40.3 @@ -13,6 +13,7 @@
    40.4  import XCTest
    40.5  
    40.6  import CoreData
    40.7 +import PEPObjCAdapterFramework
    40.8  
    40.9  @testable import pEpForiOS
   40.10  @testable import MessageModel
   40.11 @@ -28,7 +29,64 @@
   40.12          queue = OperationQueue()
   40.13      }
   40.14  
   40.15 -    func testUnreadMail() {
   40.16 +    // MARK: - No Mails
   40.17 +
   40.18 +    func testUnreadMail_normalOnly_none() {
   40.19 +        assertNewMailsService(numNewNormalMessages: 0, numNewAutoconsumableMessages: 0)
   40.20 +    }
   40.21 +
   40.22 +    // MARK: - Normal Mails Only
   40.23 +
   40.24 +    func testUnreadMail_normalOnly_one() {
   40.25 +        assertNewMailsService(numNewNormalMessages: 1, numNewAutoconsumableMessages: 0)
   40.26 +    }
   40.27 +
   40.28 +    func testUnreadMail_normalOnly_two() {
   40.29 +        assertNewMailsService(numNewNormalMessages: 2, numNewAutoconsumableMessages: 0)
   40.30 +    }
   40.31 +
   40.32 +    func testUnreadMail_normalOnly_many() {
   40.33 +        assertNewMailsService(numNewNormalMessages: 5, numNewAutoconsumableMessages: 0)
   40.34 +    }
   40.35 +
   40.36 +    // MARK: - Autocomsumable Mails Only
   40.37 +
   40.38 +    func testUnreadMail_autoconsumableOnly_one() {
   40.39 +        assertNewMailsService(numNewNormalMessages: 0, numNewAutoconsumableMessages: 1)
   40.40 +    }
   40.41 +
   40.42 +    func testUnreadMail_autoconsumableOnly_two() {
   40.43 +        assertNewMailsService(numNewNormalMessages: 0, numNewAutoconsumableMessages: 2)
   40.44 +    }
   40.45 +
   40.46 +    func testUnreadMail_autoconsumableOnly_many() {
   40.47 +        assertNewMailsService(numNewNormalMessages: 0, numNewAutoconsumableMessages: 5)
   40.48 +    }
   40.49 +
   40.50 +    // MARK: - Normal & Auto-Consumable Mails Exist
   40.51 +
   40.52 +    func testUnreadMail_normalAndAutoconsumable_one() {
   40.53 +        assertNewMailsService(numNewNormalMessages: 1, numNewAutoconsumableMessages: 1)
   40.54 +    }
   40.55 +
   40.56 +    func testUnreadMail_normalAndAutoconsumable_multi_sameCount() {
   40.57 +        assertNewMailsService(numNewNormalMessages: 2, numNewAutoconsumableMessages: 2)
   40.58 +    }
   40.59 +
   40.60 +    func testUnreadMail_normalAndAutoconsumable_multi_lessNormal() {
   40.61 +        assertNewMailsService(numNewNormalMessages: 1, numNewAutoconsumableMessages: 2)
   40.62 +    }
   40.63 +
   40.64 +    func testUnreadMail_normalAndAutoconsumable_multi_lessAutoconsumable() {
   40.65 +        assertNewMailsService(numNewNormalMessages: 2, numNewAutoconsumableMessages: 1)
   40.66 +    }
   40.67 +}
   40.68 +
   40.69 +// MARK: - HELPERS
   40.70 +
   40.71 +extension FetchNumberOfNewMailsServiceTest {
   40.72 +
   40.73 +    func assertNewMailsService(numNewNormalMessages: Int, numNewAutoconsumableMessages: Int) {
   40.74          loginIMAP(imapSyncData: imapSyncData, errorContainer: errorContainer, queue: queue)
   40.75          fetchFoldersIMAP(imapSyncData: imapSyncData, queue: queue)
   40.76  
   40.77 @@ -50,25 +108,51 @@
   40.78          partnerId.addressBookID = nil
   40.79          partnerId.userName = "USER_somepartner@example.com"
   40.80  
   40.81 -        let mail1 = CdMessage(context: moc)
   40.82 -        mail1.uuid = MessageID.generateUUID(localPart: "testUnreadMail")
   40.83 -        mail1.uid = 0
   40.84 -        mail1.parent = cdInbox
   40.85 -        mail1.addToTo(partnerId)
   40.86 -        mail1.shortMessage = "Are you ok?"
   40.87 -        mail1.longMessage = "Hi there!"
   40.88 +        var newMailsExpectedToBeCounted = [CdMessage]()
   40.89 +        var newMailsNotExpectedToBeCounted = [CdMessage]()
   40.90 +
   40.91 +        // Create new normal mails
   40.92 +        for _ in 0..<numNewNormalMessages {
   40.93 +            let newNormalMail = CdMessage(context: moc) //!!!: replace with TestuTil.createCdMessage after this file has been moved to MM
   40.94 +            newNormalMail.uuid = MessageID.generateUUID(localPart: "testUnreadMail")
   40.95 +            newNormalMail.uid = 0
   40.96 +            newNormalMail.parent = cdInbox
   40.97 +            newNormalMail.addToTo(partnerId)
   40.98 +            newNormalMail.shortMessage = "Are you ok?"
   40.99 +            newNormalMail.longMessage = "Hi there!"
  40.100 +            newMailsExpectedToBeCounted.append(newNormalMail)
  40.101 +        }
  40.102 +
  40.103 +        // Create new autoconsumable mails
  40.104 +        for _ in 0..<numNewAutoconsumableMessages {
  40.105 +            let newAutoConsumableMail = CdMessage(context: moc)//!!!: replace with TestuTil.createCdMessage after this file has been moved to MM
  40.106 +            newAutoConsumableMail.uuid = MessageID.generateUUID(localPart: "testUnreadMail")
  40.107 +            newAutoConsumableMail.uid = 0
  40.108 +            newAutoConsumableMail.parent = cdInbox
  40.109 +            newAutoConsumableMail.addToTo(partnerId)
  40.110 +            //
  40.111 +            newAutoConsumableMail.shortMessage = "auto-consumable"
  40.112 +            newAutoConsumableMail.longMessage = "auto-consumable"
  40.113 +            let header = CdHeaderField(context: moc)
  40.114 +            header.name = kPepHeaderAutoConsume
  40.115 +            header.value = kPepValueAutoConsumeYes
  40.116 +            newAutoConsumableMail.optionalFields = NSOrderedSet(array: [header])
  40.117 +            newMailsNotExpectedToBeCounted.append(newAutoConsumableMail)
  40.118 +        }
  40.119 +        // save
  40.120          moc.saveAndLogErrors()
  40.121  
  40.122 +        // upload to server ...
  40.123          appendMailsIMAP(folder: cdInbox,
  40.124                          imapSyncData: imapSyncData,
  40.125                          errorContainer: errorContainer,
  40.126                          queue: queue)
  40.127 -
  40.128 +        // ... and fetch again
  40.129          guard let numNewMails = fetchNumberOfNewMails(errorContainer: errorContainer, context: moc)
  40.130              else {
  40.131                  XCTFail()
  40.132                  return
  40.133          }
  40.134 -        XCTAssertEqual(numNewMails, numNewMailsOrig + 1)
  40.135 +        XCTAssertEqual(numNewMails, numNewMailsOrig + newMailsExpectedToBeCounted.count)
  40.136      }
  40.137  }
    41.1 --- a/pEpForiOSTests/Background/LoginImapOperationTest.swift	Tue Jul 30 17:36:20 2019 +0200
    41.2 +++ b/pEpForiOSTests/Background/LoginImapOperationTest.swift	Mon Aug 19 09:32:27 2019 +0200
    41.3 @@ -26,18 +26,18 @@
    41.4  
    41.5          let imapLogin = LoginImapOperation(
    41.6              parentName: #function, errorContainer: errorContainer, imapSyncData: imapSyncData)
    41.7 -        imapLogin.completionBlock = {
    41.8 -            imapLogin.completionBlock = nil
    41.9 -            XCTAssertNotNil(self.imapSyncData.sync)
   41.10 +        imapLogin.completionBlock = { [weak self] in
   41.11 +            XCTAssertNotNil(self?.imapSyncData?.sync)
   41.12              expLoginSucceeds.fulfill()
   41.13          }
   41.14  
   41.15          let queue = OperationQueue()
   41.16          queue.addOperation(imapLogin)
   41.17  
   41.18 -        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   41.19 +        waitForExpectations(timeout: TestUtil.waitTime) { error in
   41.20              XCTAssertNil(error)
   41.21 -            XCTAssertFalse(imapLogin.hasErrors())
   41.22 -        })
   41.23 +        }
   41.24 +
   41.25 +        XCTAssertFalse(imapLogin.hasErrors())
   41.26      }    
   41.27  }
    42.1 --- a/pEpForiOSTests/CdMessagePEPMessageTests.swift	Tue Jul 30 17:36:20 2019 +0200
    42.2 +++ b/pEpForiOSTests/CdMessagePEPMessageTests.swift	Mon Aug 19 09:32:27 2019 +0200
    42.3 @@ -23,7 +23,7 @@
    42.4  
    42.5          let pEpReceiver = cdReceiver.pEpIdentity()
    42.6  
    42.7 -        let pEpMsg = PEPUtil.pEp(cdMessage: cdMsg, outgoing: true)
    42.8 +        let pEpMsg = PEPUtils.pEp(cdMessage: cdMsg, outgoing: true)
    42.9  
   42.10          XCTAssertEqual(pEpMsg.to?[0], pEpReceiver)
   42.11      }
    43.1 --- a/pEpForiOSTests/DecryptImportedMessagesTests.swift	Tue Jul 30 17:36:20 2019 +0200
    43.2 +++ b/pEpForiOSTests/DecryptImportedMessagesTests.swift	Mon Aug 19 09:32:27 2019 +0200
    43.3 @@ -9,7 +9,8 @@
    43.4  import XCTest
    43.5  import CoreData
    43.6  
    43.7 -@testable import pEpForiOS
    43.8 +//!!!: Must be moved to MM.
    43.9 +
   43.10  @testable import MessageModel
   43.11  import PEPObjCAdapterFramework
   43.12  
   43.13 @@ -23,18 +24,17 @@
   43.14       */
   43.15      func testDecrypt002() {
   43.16          let cdOwnAccount = DecryptionUtil.createLocalAccount(ownUserName: "Someonei",
   43.17 -                                                             ownUserID: "User_Someonei",
   43.18 +                                                             ownUserID: CdIdentity.pEpOwnUserID,
   43.19                                                               ownEmailAddress: "someone@gmx.de",
   43.20                                                               context: moc)
   43.21          self.backgroundQueue = OperationQueue()
   43.22 -        let cdMessage = DecryptionUtil.decryptTheMessage(
   43.23 -            testCase: self,
   43.24 -            backgroundQueue: backgroundQueue,
   43.25 -            cdOwnAccount: cdOwnAccount,
   43.26 -            fileName: "IOS-1300_odt_attachment.txt")
   43.27 +        let cdMessage = DecryptionUtil.decryptTheMessage(testCase: self,
   43.28 +                                                         backgroundQueue: backgroundQueue,
   43.29 +                                                         cdOwnAccount: cdOwnAccount,
   43.30 +                                                         fileName: "IOS-1300_odt_attachment.txt")
   43.31  
   43.32          guard let theCdMessage = cdMessage else {
   43.33 -            XCTFail()
   43.34 +            XCTFail("no msg")
   43.35              return
   43.36          }
   43.37  
   43.38 @@ -54,12 +54,12 @@
   43.39       */
   43.40      func testDecryptUndisplayedAttachedJpegMessage() {
   43.41          let cdOwnAccount = DecryptionUtil.createLocalAccount(
   43.42 -            ownUserName: "ThisIsMe",
   43.43 -            ownUserID: "User_Me",
   43.44 +            ownUserName: "User_Me",
   43.45 +            ownUserID: CdIdentity.pEpOwnUserID,
   43.46              ownEmailAddress: "iostest001@peptest.ch",
   43.47              context: moc)
   43.48  
   43.49 -        self.backgroundQueue = OperationQueue()
   43.50 +        backgroundQueue = OperationQueue()
   43.51          let cdMessage = DecryptionUtil.decryptTheMessage(
   43.52              testCase: self,
   43.53              backgroundQueue: backgroundQueue,
   43.54 @@ -83,15 +83,15 @@
   43.55      /**
   43.56       IOS-1378
   43.57       - Note: If you need to manually verify something:
   43.58 -       * The public/secret key pair of Leon Kowalski (subject)
   43.59 -         is in `Leon Kowalski (19B9EE3B) – Private.asc`.
   43.60 -       * The public/secret key pair of Harry Bryant (sender) is in
   43.61 -         `Harry Bryant iostest002@peptest.ch (0x5716EA2D9AE32468) pub-sec.asc`.
   43.62 +     * The public/secret key pair of Leon Kowalski (subject)
   43.63 +     is in `Leon Kowalski (19B9EE3B) – Private.asc`.
   43.64 +     * The public/secret key pair of Harry Bryant (sender) is in
   43.65 +     `Harry Bryant iostest002@peptest.ch (0x5716EA2D9AE32468) pub-sec.asc`.
   43.66       */
   43.67      func testSetOwnKey() {
   43.68          let cdOwnAccount = DecryptionUtil.createLocalAccount(
   43.69              ownUserName: "Rick Deckard",
   43.70 -            ownUserID: "rick_deckard_uid",
   43.71 +            ownUserID: CdIdentity.pEpOwnUserID,
   43.72              ownEmailAddress: "iostest001@peptest.ch",
   43.73              context: moc)
   43.74  
   43.75 @@ -134,76 +134,76 @@
   43.76  
   43.77      // ENGINE-505
   43.78      /*
   43.79 -    func testNullInnerMimeType() {
   43.80 -        let cdOwnAccount = DecryptionUtil.createLocalAccount(
   43.81 -            ownUserName: "ThisIsMe",
   43.82 -            ownUserID: "User_Me",
   43.83 -            ownEmailAddress: "guile-user@gnu.org")
   43.84 +     func testNullInnerMimeType() {
   43.85 +     let cdOwnAccount = DecryptionUtil.createLocalAccount(
   43.86 +     ownUserName: "ThisIsMe",
   43.87 +     ownUserID: "User_Me",
   43.88 +     ownEmailAddress: "guile-user@gnu.org")
   43.89  
   43.90 -        self.backgroundQueue = OperationQueue()
   43.91 -        let cdMessage = DecryptionUtil.decryptTheMessage(
   43.92 -            testCase: self,
   43.93 -            backgroundQueue: backgroundQueue,
   43.94 -            cdOwnAccount: cdOwnAccount,
   43.95 -            fileName: "ENGINE-505_Mail_NullInnerMimeType.txt")
   43.96 +     self.backgroundQueue = OperationQueue()
   43.97 +     let cdMessage = DecryptionUtil.decryptTheMessage(
   43.98 +     testCase: self,
   43.99 +     backgroundQueue: backgroundQueue,
  43.100 +     cdOwnAccount: cdOwnAccount,
  43.101 +     fileName: "ENGINE-505_Mail_NullInnerMimeType.txt")
  43.102  
  43.103 -        guard let theCdMessage = cdMessage else {
  43.104 -            XCTFail()
  43.105 -            return
  43.106 -        }
  43.107 +     guard let theCdMessage = cdMessage else {
  43.108 +     XCTFail()
  43.109 +     return
  43.110 +     }
  43.111  
  43.112 -        XCTAssertEqual(theCdMessage.pEpRating, Int16(.unencrypted.rawValue))
  43.113 -        XCTAssertEqual(theCdMessage.shortMessage,
  43.114 -                       "Re: Help needed debugging segfault with Guile 1.8.7")
  43.115 -        XCTAssertNil(theCdMessage.longMessage)
  43.116 +     XCTAssertEqual(theCdMessage.pEpRating, Int16(.unencrypted.rawValue))
  43.117 +     XCTAssertEqual(theCdMessage.shortMessage,
  43.118 +     "Re: Help needed debugging segfault with Guile 1.8.7")
  43.119 +     XCTAssertNil(theCdMessage.longMessage)
  43.120  
  43.121 -        let attachments = theCdMessage.attachments?.array as? [CdAttachment] ?? []
  43.122 -        XCTAssertEqual(attachments.count, 2)
  43.123 +     let attachments = theCdMessage.attachments?.array as? [CdAttachment] ?? []
  43.124 +     XCTAssertEqual(attachments.count, 2)
  43.125  
  43.126 -        guard let msg = theCdMessage.message() else {
  43.127 -            XCTFail()
  43.128 -            return
  43.129 -        }
  43.130 +     guard let msg = theCdMessage.message() else {
  43.131 +     XCTFail()
  43.132 +     return
  43.133 +     }
  43.134  
  43.135 -        XCTAssertEqual(msg.attachments.count, 1)
  43.136 -    }
  43.137 +     XCTAssertEqual(msg.attachments.count, 1)
  43.138 +     }
  43.139       */
  43.140  
  43.141      // ENGINE-456 / IOS-1258
  43.142      /*
  43.143 -    func test_ENGINE_459() {
  43.144 -        let cdOwnAccount = DecryptionUtil.createLocalAccount(
  43.145 -            ownUserName: "ThisIsMe",
  43.146 -            ownUserID: "User_Me",
  43.147 -            ownEmailAddress: "iostest010@peptest.ch")
  43.148 +     func test_ENGINE_459() {
  43.149 +     let cdOwnAccount = DecryptionUtil.createLocalAccount(
  43.150 +     ownUserName: "ThisIsMe",
  43.151 +     ownUserID: "User_Me",
  43.152 +     ownEmailAddress: "iostest010@peptest.ch")
  43.153  
  43.154 -        self.backgroundQueue = OperationQueue()
  43.155 -        let cdMessage = DecryptionUtil.decryptTheMessage(
  43.156 -            testCase: self,
  43.157 -            backgroundQueue: backgroundQueue,
  43.158 -            cdOwnAccount: cdOwnAccount,
  43.159 -            fileName: "ENGINE-456_Mail_PEP_OUT_OF_MEMORY.txt")
  43.160 +     self.backgroundQueue = OperationQueue()
  43.161 +     let cdMessage = DecryptionUtil.decryptTheMessage(
  43.162 +     testCase: self,
  43.163 +     backgroundQueue: backgroundQueue,
  43.164 +     cdOwnAccount: cdOwnAccount,
  43.165 +     fileName: "ENGINE-456_Mail_PEP_OUT_OF_MEMORY.txt")
  43.166  
  43.167 -        guard let theCdMessage = cdMessage else {
  43.168 -            XCTFail()
  43.169 -            return
  43.170 -        }
  43.171 +     guard let theCdMessage = cdMessage else {
  43.172 +     XCTFail()
  43.173 +     return
  43.174 +     }
  43.175  
  43.176 -        XCTAssertEqual(theCdMessage.pEpRating, Int16(.unencrypted.rawValue))
  43.177 -        XCTAssertEqual(theCdMessage.shortMessage,
  43.178 -                       "Re: Help needed debugging segfault with Guile 1.8.7")
  43.179 -        XCTAssertNil(theCdMessage.longMessage)
  43.180 +     XCTAssertEqual(theCdMessage.pEpRating, Int16(.unencrypted.rawValue))
  43.181 +     XCTAssertEqual(theCdMessage.shortMessage,
  43.182 +     "Re: Help needed debugging segfault with Guile 1.8.7")
  43.183 +     XCTAssertNil(theCdMessage.longMessage)
  43.184  
  43.185 -        let attachments = theCdMessage.attachments?.array as? [CdAttachment] ?? []
  43.186 -        XCTAssertEqual(attachments.count, 2)
  43.187 +     let attachments = theCdMessage.attachments?.array as? [CdAttachment] ?? []
  43.188 +     XCTAssertEqual(attachments.count, 2)
  43.189  
  43.190 -        guard let msg = theCdMessage.message() else {
  43.191 -            XCTFail()
  43.192 -            return
  43.193 -        }
  43.194 +     guard let msg = theCdMessage.message() else {
  43.195 +     XCTFail()
  43.196 +     return
  43.197 +     }
  43.198  
  43.199 -        XCTAssertEqual(msg.attachments.count, 1)
  43.200 -    }
  43.201 +     XCTAssertEqual(msg.attachments.count, 1)
  43.202 +     }
  43.203       */
  43.204  
  43.205      // MARK: - Helpers
    44.1 --- a/pEpForiOSTests/DecryptionTestsInternal.swift	Tue Jul 30 17:36:20 2019 +0200
    44.2 +++ b/pEpForiOSTests/DecryptionTestsInternal.swift	Mon Aug 19 09:32:27 2019 +0200
    44.3 @@ -170,14 +170,14 @@
    44.4          cdMsg.parent = cdInbox
    44.5  
    44.6          XCTAssertFalse(cdMsg.imap?.localFlags?.flagDeleted ?? true)
    44.7 -        XCTAssertEqual(cdMsg.pEpRating, PEPUtil.pEpRatingNone)
    44.8 +        XCTAssertEqual(cdMsg.pEpRating, PEPUtils.pEpRatingNone)
    44.9          if shouldEncrypt {
   44.10              XCTAssertTrue(cdMsg.isProbablyPGPMime())
   44.11          }
   44.12  
   44.13          moc.saveAndLogErrors()
   44.14  
   44.15 -        XCTAssertEqual(Int32(cdMsg.pEpRating), Int32(PEPUtil.pEpRatingNone))
   44.16 +        XCTAssertEqual(Int32(cdMsg.pEpRating), Int32(PEPUtils.pEpRatingNone))
   44.17  
   44.18          let expectationDecryptHasRun = expectation(description: "expectationDecryptHasRun")
   44.19          let errorContainer = ErrorContainer()
    45.1 --- a/pEpForiOSTests/Features/ReUploadTest.swift	Tue Jul 30 17:36:20 2019 +0200
    45.2 +++ b/pEpForiOSTests/Features/ReUploadTest.swift	Mon Aug 19 09:32:27 2019 +0200
    45.3 @@ -168,7 +168,7 @@
    45.4  //
    45.5  //    // Similar to super.setup() but without bothering Xcode Test
    45.6  //    private func setupWithoutBotheringXCT() {
    45.7 -//        XCTAssertTrue(PEPUtil.pEpClean())
    45.8 +//        XCTAssertTrue(PEPUtils.pEpClean())
    45.9  //        persistentSetup = PersistentSetup()
   45.10  //        let cdAccount = SecretTestData().createWorkingCdAccount()
   45.11  //        moc.saveAndLogErrors()
   45.12 @@ -181,7 +181,7 @@
   45.13  //        persistentSetup.tearDownCoreDataStack()
   45.14  //        persistentSetup = nil
   45.15  //        PEPSession.cleanup()
   45.16 -//        XCTAssertTrue(PEPUtil.pEpClean())
   45.17 +//        XCTAssertTrue(PEPUtils.pEpClean())
   45.18  //    }
   45.19  //
   45.20  //    // MARK: The actual test
   45.21 @@ -253,7 +253,7 @@
   45.22  //        }
   45.23  //        XCTAssertTrue(msg.uid > 0, "We fetched the message from server")
   45.24  //
   45.25 -//        let senderRatingOnServer = PEPUtil.pEpRatingFromInt(msg.pEpRatingInt)
   45.26 +//        let senderRatingOnServer = PEPUtils.pEpRatingFromInt(msg.pEpRatingInt)
   45.27  //        if expectedSenderRatingOnServerEncrypted {
   45.28  //            XCTAssertFalse(senderRatingOnServer == .unencrypted,
   45.29  //                           "assumed stored rating on sever")
   45.30 @@ -305,7 +305,7 @@
   45.31  //
   45.32  //        XCTAssertTrue(receivedMsg.uid > 0, "We fetched the message from server")
   45.33  //
   45.34 -//        guard let receiverRatingOnServer = PEPUtil.pEpRatingFromInt(receivedMsg.pEpRatingInt) else {
   45.35 +//        guard let receiverRatingOnServer = PEPUtils.pEpRatingFromInt(receivedMsg.pEpRatingInt) else {
   45.36  //            XCTFail("No rating.")
   45.37  //            return
   45.38  //        }
    46.1 --- a/pEpForiOSTests/HandshakeTests.swift	Tue Jul 30 17:36:20 2019 +0200
    46.2 +++ b/pEpForiOSTests/HandshakeTests.swift	Mon Aug 19 09:32:27 2019 +0200
    46.3 @@ -43,7 +43,7 @@
    46.4                  return
    46.5          }
    46.6  
    46.7 -        let pEpMessage = PEPUtil.pEp(cdMessage: cdMessage, outgoing: true)
    46.8 +        let pEpMessage = PEPUtils.pEp(cdMessage: cdMessage, outgoing: true)
    46.9  
   46.10          let theAttachments = pEpMessage.attachments ?? []
   46.11          XCTAssertEqual(theAttachments.count, 1)
    47.1 --- a/pEpForiOSTests/MailParsingTests.swift	Tue Jul 30 17:36:20 2019 +0200
    47.2 +++ b/pEpForiOSTests/MailParsingTests.swift	Mon Aug 19 09:32:27 2019 +0200
    47.3 @@ -46,7 +46,7 @@
    47.4                  return
    47.5          }
    47.6  
    47.7 -        let pEpMessage = PEPUtil.pEp(cdMessage: cdMessage, outgoing: true)
    47.8 +        let pEpMessage = PEPUtils.pEp(cdMessage: cdMessage, outgoing: true)
    47.9  
   47.10          let theAttachments = pEpMessage.attachments ?? []
   47.11          XCTAssertEqual(theAttachments.count, 1)
   47.12 @@ -83,7 +83,7 @@
   47.13                  return
   47.14          }
   47.15  
   47.16 -        let pEpMessage = PEPUtil.pEp(cdMessage: cdMessage, outgoing: true)
   47.17 +        let pEpMessage = PEPUtils.pEp(cdMessage: cdMessage, outgoing: true)
   47.18  
   47.19          let theAttachments = pEpMessage.attachments ?? []
   47.20          XCTAssertEqual(theAttachments.count, 2)
   47.21 @@ -120,7 +120,7 @@
   47.22                  return
   47.23          }
   47.24  
   47.25 -        let pEpMessage = PEPUtil.pEp(cdMessage: cdMessage, outgoing: true)
   47.26 +        let pEpMessage = PEPUtils.pEp(cdMessage: cdMessage, outgoing: true)
   47.27  
   47.28          XCTAssertEqual(pEpMessage.shortMessage, "blah")
   47.29          XCTAssertNotNil(pEpMessage.longMessage)
    48.1 --- a/pEpForiOSTests/MessagePantomimeTests.swift	Tue Jul 30 17:36:20 2019 +0200
    48.2 +++ b/pEpForiOSTests/MessagePantomimeTests.swift	Mon Aug 19 09:32:27 2019 +0200
    48.3 @@ -97,7 +97,7 @@
    48.4          let pEpMsgDict = cdMsg.pEpMessageDict()
    48.5          XCTAssertEqual(pEpMsgDict[kPepReferences] as? [String] ?? [], allRefs)
    48.6  
    48.7 -        let cwMsg2 = PEPUtil.pantomime(pEpMessageDict: pEpMsgDict)
    48.8 +        let cwMsg2 = PEPUtils.pantomime(pEpMessageDict: pEpMsgDict)
    48.9          XCTAssertEqual(cwMsg2.allReferences() as? [String] ?? [], allRefs)
   48.10      }
   48.11  }
    49.1 --- a/pEpForiOSTests/MessageReevalutionTests.swift	Tue Jul 30 17:36:20 2019 +0200
    49.2 +++ b/pEpForiOSTests/MessageReevalutionTests.swift	Mon Aug 19 09:32:27 2019 +0200
    49.3 @@ -28,7 +28,7 @@
    49.4          super.setUp()
    49.5  
    49.6          let ownIdentity = PEPIdentity(address: "iostest002@peptest.ch",
    49.7 -                                      userID: "iostest002@peptest.ch_ID",
    49.8 +                                      userID: CdIdentity.pEpOwnUserID,
    49.9                                        userName: "iOS Test 002",
   49.10                                        isOwn: true)
   49.11  
   49.12 @@ -134,9 +134,9 @@
   49.13          senderIdentity = theSenderIdentity
   49.14      }
   49.15      func testCommunicationTypes() {
   49.16 -        let senderIdent = senderIdentity.updatedIdentity(session: session)
   49.17 +        let senderIdent = senderIdentity.updatedIdentity()
   49.18          XCTAssertFalse(try! senderIdent.isPEPUser(session).boolValue)
   49.19 -        XCTAssertEqual(senderIdentity.pEpRating(session: session), .reliable)
   49.20 +        XCTAssertEqual(senderIdentity.pEpRating(), .reliable)
   49.21  
   49.22          try! session.keyMistrusted(senderIdent)
   49.23  
   49.24 @@ -146,8 +146,44 @@
   49.25          XCTAssertEqual(senderIdentity.pEpRating(), .haveNoKey)
   49.26      }
   49.27  
   49.28 -    func reevaluateMessage(expectedRating: PEPRating, inBackground: Bool = true,
   49.29 -                           infoMessage: String) {
   49.30 +    func testTrustMistrust() {
   49.31 +        let runReevaluationInBackground = false
   49.32 +        let senderIdent = senderIdentity.updatedIdentity(session: session)
   49.33 +
   49.34 +        try! session.keyResetTrust(senderIdent)
   49.35 +        XCTAssertFalse(senderIdent.isConfirmed)
   49.36 +        reevaluateMessage(
   49.37 +            expectedRating: .reliable,
   49.38 +            inBackground: runReevaluationInBackground,
   49.39 +            infoMessage: "in the beginning")
   49.40 +
   49.41 +        for _ in 0..<1 {
   49.42 +            try! session.trustPersonalKey(senderIdent)
   49.43 +            XCTAssertTrue(senderIdent.isConfirmed)
   49.44 +            XCTAssertEqual(senderIdentity.pEpRating(), .trusted)
   49.45 +            reevaluateMessage(
   49.46 +                expectedRating: .trusted,
   49.47 +                inBackground: runReevaluationInBackground,
   49.48 +                infoMessage: "after trust")
   49.49 +
   49.50 +            try! session.keyMistrusted(senderIdent)
   49.51 +            XCTAssertEqual(senderIdentity.pEpRating(), .haveNoKey)
   49.52 +            reevaluateMessage(
   49.53 +                expectedRating: .mistrust,
   49.54 +                inBackground: runReevaluationInBackground,
   49.55 +                infoMessage: "after mistrust")
   49.56 +            try! session.update(senderIdent)
   49.57 +            XCTAssertFalse(senderIdent.isConfirmed)
   49.58 +        }
   49.59 +    }
   49.60 +}
   49.61 +
   49.62 +// MARK: - HELPER
   49.63 +
   49.64 +extension MessageReevalutionTests {
   49.65 +
   49.66 +    private func reevaluateMessage(expectedRating: PEPRating, inBackground: Bool = true,
   49.67 +                                   infoMessage: String) {
   49.68          let message = MessageModelObjectUtils.getMessage(fromCdMessage: cdDecryptedMessage)
   49.69  
   49.70          if inBackground {
   49.71 @@ -171,35 +207,4 @@
   49.72              reevalOp.reEvaluate()
   49.73          }
   49.74      }
   49.75 -
   49.76 -    func testTrustMistrust() {
   49.77 -        let runReevaluationInBackground = false
   49.78 -        let senderIdent = senderIdentity.updatedIdentity(session: session)
   49.79 -
   49.80 -        try! session.keyResetTrust(senderIdent)
   49.81 -        XCTAssertFalse(senderIdent.isConfirmed)
   49.82 -        reevaluateMessage(
   49.83 -            expectedRating: .reliable,
   49.84 -            inBackground: runReevaluationInBackground,
   49.85 -            infoMessage: "in the beginning")
   49.86 -
   49.87 -        for _ in 0..<1 {
   49.88 -            try! session.trustPersonalKey(senderIdent)
   49.89 -            XCTAssertTrue(senderIdent.isConfirmed)
   49.90 -            XCTAssertEqual(senderIdentity.pEpRating(session: session), .trusted)
   49.91 -            reevaluateMessage(
   49.92 -                expectedRating: .trusted,
   49.93 -                inBackground: runReevaluationInBackground,
   49.94 -                infoMessage: "after trust")
   49.95 -
   49.96 -            try! session.keyMistrusted(senderIdent)
   49.97 -            XCTAssertEqual(senderIdentity.pEpRating(session: session), .haveNoKey)
   49.98 -            reevaluateMessage(
   49.99 -                expectedRating: .mistrust,
  49.100 -                inBackground: runReevaluationInBackground,
  49.101 -                infoMessage: "after mistrust")
  49.102 -            try! session.update(senderIdent)
  49.103 -            XCTAssertFalse(senderIdent.isConfirmed)
  49.104 -        }
  49.105 -    }
  49.106  }
    50.1 --- a/pEpForiOSTests/Models/CdMessage+PantomimeTest.swift	Tue Jul 30 17:36:20 2019 +0200
    50.2 +++ b/pEpForiOSTests/Models/CdMessage+PantomimeTest.swift	Mon Aug 19 09:32:27 2019 +0200
    50.3 @@ -232,51 +232,52 @@
    50.4      }
    50.5  
    50.6      func testUpdateFromServer() {
    50.7 -        let (m, _, localFlags, serverFlags) = createCdMessageForFlags()
    50.8 +        let m = createCdMessageForFlags()
    50.9          let cwFlags = CWFlags()
   50.10  
   50.11          // Server adds .seen, user just flagged -> both
   50.12 -        localFlags.flagFlagged = true
   50.13 +        m.imapFields().localFlags?.flagFlagged = true
   50.14          cwFlags.add(.seen)
   50.15          XCTAssertTrue(cwFlags.contain(.seen))
   50.16          m.updateFromServer(cwFlags: cwFlags, context: moc)
   50.17 -        XCTAssertTrue(localFlags.flagFlagged)
   50.18 -        XCTAssertTrue(localFlags.flagSeen)
   50.19 -        XCTAssertEqual(serverFlags.rawFlagsAsShort(), cwFlags.rawFlagsAsShort())
   50.20 +        XCTAssertTrue(m.imapFields().localFlags?.flagFlagged ?? false)
   50.21 +        XCTAssertTrue(m.imapFields().localFlags?.flagSeen ?? false)
   50.22 +        XCTAssertEqual(m.imapFields().serverFlags?.rawFlagsAsShort(), cwFlags.rawFlagsAsShort())
   50.23  
   50.24 -        // No user action, server adds .seen -> .seen locally
   50.25 -        localFlags.reset()
   50.26 -        serverFlags.reset()
   50.27 +        // No user action, server adds .seen -> .seen locally (ServerFags)
   50.28 +        m.imapFields().localFlags?.reset()
   50.29 +        m.imapFields().serverFlags?.reset()
   50.30          m.updateFromServer(cwFlags: cwFlags, context: moc)
   50.31 -        XCTAssertTrue(localFlags.flagSeen)
   50.32 -        XCTAssertEqual(serverFlags.rawFlagsAsShort(), cwFlags.rawFlagsAsShort())
   50.33 +        XCTAssertTrue(m.imapFields().localFlags?.flagSeen ?? false)
   50.34 +        let invalid = Int16(-1)
   50.35 +        XCTAssertEqual(m.imapFields().serverFlags?.rawFlagsAsShort() ?? invalid, cwFlags.rawFlagsAsShort())
   50.36  
   50.37          // Conflict: User just unflagged, that should win over the data from the server
   50.38 -        localFlags.reset()
   50.39 -        serverFlags.reset()
   50.40 +        m.imapFields().localFlags?.reset()
   50.41 +        m.imapFields().serverFlags?.reset()
   50.42          cwFlags.removeAll()
   50.43          cwFlags.add(.flagged)
   50.44 -        serverFlags.flagFlagged = true
   50.45 +        m.imapFields().serverFlags?.flagFlagged = true
   50.46          m.updateFromServer(cwFlags: cwFlags, context: moc)
   50.47 -        XCTAssertFalse(localFlags.flagFlagged)
   50.48 -        XCTAssertEqual(serverFlags.rawFlagsAsShort(), cwFlags.rawFlagsAsShort())
   50.49 +        XCTAssertFalse(m.imapFields().localFlags?.flagFlagged ?? true)
   50.50 +        XCTAssertEqual(m.imapFields().serverFlags?.rawFlagsAsShort(), cwFlags.rawFlagsAsShort())
   50.51  
   50.52          // Conflict: User has unset .recent, but that comes as set from the server.
   50.53 -        localFlags.reset()
   50.54 -        serverFlags.reset()
   50.55 +        m.imapFields().localFlags?.reset()
   50.56 +        m.imapFields().serverFlags?.reset()
   50.57          cwFlags.removeAll()
   50.58          cwFlags.add(.recent)
   50.59          cwFlags.add(.flagged)
   50.60 -        serverFlags.flagRecent = true
   50.61 +        m.imapFields().serverFlags?.flagRecent = true
   50.62          m.updateFromServer(cwFlags: cwFlags, context: moc)
   50.63 -        XCTAssertTrue(localFlags.flagRecent)
   50.64 -        XCTAssertTrue(localFlags.flagFlagged)
   50.65 -        XCTAssertEqual(serverFlags.rawFlagsAsShort(), cwFlags.rawFlagsAsShort())
   50.66 +        XCTAssertTrue(m.imapFields().localFlags?.flagRecent ?? false)
   50.67 +        XCTAssertTrue(m.imapFields().localFlags?.flagFlagged ?? false)
   50.68 +        XCTAssertEqual(m.imapFields().serverFlags?.rawFlagsAsShort(), cwFlags.rawFlagsAsShort())
   50.69      }
   50.70  
   50.71      // MARK: - HELPER
   50.72  
   50.73 -    func createCdMessageForFlags() -> (CdMessage, CdImapFields, CdImapFlags, CdImapFlags) {
   50.74 +    func createCdMessageForFlags() -> CdMessage {
   50.75          let m = CdMessage(context: moc)
   50.76          let imap = CdImapFields(context: moc)
   50.77          let serverFlags = CdImapFlags(context: moc)
   50.78 @@ -293,7 +294,7 @@
   50.79          XCTAssertFalse(localFlags.flagDeleted)
   50.80          XCTAssertFalse(localFlags.flagFlagged)
   50.81  
   50.82 -        return (m, imap, localFlags, serverFlags)
   50.83 +        return m
   50.84      }
   50.85  
   50.86      func setAllCurrentImapFlags(of message: CdMessage, to isEnabled: Bool) {
    51.1 --- a/pEpForiOSTests/Models/EmailListViewModelTest.swift	Tue Jul 30 17:36:20 2019 +0200
    51.2 +++ b/pEpForiOSTests/Models/EmailListViewModelTest.swift	Mon Aug 19 09:32:27 2019 +0200
    51.3 @@ -12,42 +12,38 @@
    51.4  @testable import MessageModel
    51.5  
    51.6  class EmailListViewModelTest: CoreDataDrivenTestBase {
    51.7 -    var folder: Folder!
    51.8 +    var inbox: Folder!
    51.9      var trashFolder: Folder!
   51.10      var outboxFolder: Folder!
   51.11      var draftsFolder: Folder!
   51.12      var emailListVM : EmailListViewModel!
   51.13 -    var masterViewController: TestMasterViewController!
   51.14 +    fileprivate var masterViewController: TestMasterViewController!
   51.15      var acc : Account!
   51.16  
   51.17 -    /** this set up a view model with one account and one folder saved **/
   51.18      override func setUp() {
   51.19          super.setUp()
   51.20  
   51.21          acc = cdAccount.account()
   51.22  
   51.23 -
   51.24 -        folder = Folder(name: "inbox", parent: nil, account: acc, folderType: .inbox)
   51.25 -        trashFolder = Folder(name: "trash",
   51.26 -                             parent: nil,
   51.27 -                             account: acc,
   51.28 -                             folderType: .trash)
   51.29 +        inbox = Folder(name: "inbox", parent: nil, account: acc, folderType: .inbox)
   51.30 +        trashFolder = Folder(name: "trash", parent: nil, account: acc, folderType: .trash)
   51.31          outboxFolder = Folder(name: "outbox", parent: nil, account: acc, folderType: .outbox)
   51.32          draftsFolder = Folder(name: "drafts", parent: nil, account: acc, folderType: .drafts)
   51.33 -        moc.saveAndLogErrors()
   51.34 +        Session.main.commit()
   51.35 +    }
   51.36 +
   51.37 +    override func tearDown() {
   51.38 +        masterViewController = nil
   51.39 +        super.tearDown()
   51.40      }
   51.41  
   51.42      func secondAccountSetUp() {
   51.43          let acc2 = SecretTestData().createWorkingAccount(number: 1)
   51.44          _ = Folder(name: "inbox", parent: nil, account: acc2, folderType: .inbox)
   51.45 -        _ = Folder(name: "trash",
   51.46 -                             parent: nil,
   51.47 -                             account: acc2,
   51.48 -                             folderType: .trash)
   51.49 +        _ = Folder(name: "trash", parent: nil, account: acc2, folderType: .trash)
   51.50          _ = Folder(name: "outbox", parent: nil, account: acc2, folderType: .outbox)
   51.51          _ = Folder(name: "drafts", parent: nil, account: acc2, folderType: .drafts)
   51.52 -        moc.saveAndLogErrors()
   51.53 -
   51.54 +        Session.main.commit()
   51.55      }
   51.56  
   51.57      // MARK: - Test section
   51.58 @@ -64,7 +60,7 @@
   51.59      }
   51.60  
   51.61      func test10MessagesInInitialSetup() {
   51.62 -         TestUtil.createMessages(number: 10, engineProccesed: true, inFolder: folder, setUids: true)
   51.63 +        TestUtil.createMessages(number: 10, engineProccesed: true, inFolder: inbox, setUids: true)
   51.64          setupViewModel()
   51.65          emailListVM.startMonitoring()
   51.66          XCTAssertEqual(emailListVM.rowCount, 10)
   51.67 @@ -72,11 +68,11 @@
   51.68  
   51.69      func testGetFolderName() {
   51.70          setupViewModel()
   51.71 -        XCTAssertEqual(Folder.localizedName(realName: self.folder.realName), emailListVM.folderName)
   51.72 +        XCTAssertEqual(Folder.localizedName(realName: self.inbox.realName), emailListVM.folderName)
   51.73      }
   51.74  
   51.75      func testGetDestructiveAction() {
   51.76 -        TestUtil.createMessages(number: 1, engineProccesed: true, inFolder: folder)
   51.77 +        TestUtil.createMessages(number: 1, engineProccesed: true, inFolder: inbox)
   51.78          setupViewModel()
   51.79          emailListVM.startMonitoring()
   51.80          let destructiveAction = emailListVM.getDestructiveActtion(forMessageAt: 0)
   51.81 @@ -107,7 +103,7 @@
   51.82      }
   51.83  
   51.84      func testDefaultFilterActiveIsUnread() {
   51.85 -        let messages = TestUtil.createMessages(number: 20, engineProccesed: true, inFolder: folder)
   51.86 +        let messages = TestUtil.createMessages(number: 20, engineProccesed: true, inFolder: inbox)
   51.87          messages.forEach { (msg) in
   51.88              msg.imapFlags.seen = true
   51.89          }
   51.90 @@ -130,7 +126,9 @@
   51.91          let imap = ImapFlags()
   51.92          imap.seen = true
   51.93          messages[0].imapFlags = imap
   51.94 +
   51.95          waitForExpectations(timeout: TestUtil.waitTime)
   51.96 +
   51.97          XCTAssertEqual(4, emailListVM.rowCount)
   51.98          unreadActive = emailListVM.unreadFilterEnabled()
   51.99          XCTAssertTrue(unreadActive)
  51.100 @@ -139,7 +137,7 @@
  51.101      }
  51.102  
  51.103      func testGetFlagAndMoreAction() {
  51.104 -        let messages = TestUtil.createMessages(number: 1, engineProccesed: true, inFolder: folder)
  51.105 +        let messages = TestUtil.createMessages(number: 1, engineProccesed: true, inFolder: inbox)
  51.106          setupViewModel()
  51.107          emailListVM.startMonitoring()
  51.108          var flagAction = emailListVM.getFlagAction(forMessageAt: 0)
  51.109 @@ -186,7 +184,7 @@
  51.110  
  51.111          XCTAssertFalse(noAccounts)
  51.112  
  51.113 -        cdAccount.delete()
  51.114 +        moc.delete(cdAccount)
  51.115          setupViewModel()
  51.116          noAccounts = emailListVM.showLoginView
  51.117  
  51.118 @@ -196,7 +194,7 @@
  51.119      // MARK: - Search section
  51.120  
  51.121      func testSetSearchFilterWith0results() {
  51.122 -        TestUtil.createMessages(number: 10, engineProccesed: true, inFolder: folder)
  51.123 +        TestUtil.createMessages(number: 10, engineProccesed: true, inFolder: inbox)
  51.124          setupViewModel()
  51.125          emailListVM.startMonitoring()
  51.126          emailListVM.setSearch(forSearchText: "blabla@blabla.com")
  51.127 @@ -204,7 +202,7 @@
  51.128      }
  51.129  
  51.130      func testRemoveSearchFilterAfter0Results() {
  51.131 -        TestUtil.createMessages(number: 10, engineProccesed: true, inFolder: folder)
  51.132 +        TestUtil.createMessages(number: 10, engineProccesed: true, inFolder: inbox)
  51.133          setupViewModel()
  51.134          emailListVM.startMonitoring()
  51.135          XCTAssertEqual(emailListVM.rowCount, 10)
  51.136 @@ -216,19 +214,19 @@
  51.137  
  51.138      func testSetSearchFilterAddressWith3results() {
  51.139          let textToSearch = "searchTest@mail.com"
  51.140 -        TestUtil.createMessages(number: 10, engineProccesed: true, inFolder: folder)
  51.141 -        TestUtil.createMessage(inFolder: folder,
  51.142 -                      from: Identity(address: textToSearch),
  51.143 -                      tos: [folder.account.user],
  51.144 -                      uid: 666).save()
  51.145 -        TestUtil.createMessage(inFolder: folder,
  51.146 -                      from: Identity(address: textToSearch),
  51.147 -                      tos: [folder.account.user],
  51.148 -                      uid: 667).save()
  51.149 -        TestUtil.createMessage(inFolder: folder,
  51.150 -                      from: Identity(address: textToSearch),
  51.151 -                      tos: [folder.account.user],
  51.152 -                      uid: 668).save()
  51.153 +        TestUtil.createMessages(number: 10, engineProccesed: true, inFolder: inbox)
  51.154 +        TestUtil.createMessage(inFolder: inbox,
  51.155 +                               from: Identity(address: textToSearch),
  51.156 +                               tos: [inbox.account.user],
  51.157 +                               uid: 666).save()
  51.158 +        TestUtil.createMessage(inFolder: inbox,
  51.159 +                               from: Identity(address: textToSearch),
  51.160 +                               tos: [inbox.account.user],
  51.161 +                               uid: 667).save()
  51.162 +        TestUtil.createMessage(inFolder: inbox,
  51.163 +                               from: Identity(address: textToSearch),
  51.164 +                               tos: [inbox.account.user],
  51.165 +                               uid: 668).save()
  51.166          setupViewModel()
  51.167          emailListVM.startMonitoring()
  51.168          XCTAssertEqual(emailListVM.rowCount, 13)
  51.169 @@ -238,12 +236,12 @@
  51.170  
  51.171      func testSetSearchFilterShortMessageWith1results() {
  51.172          let textToSearch = "searchTest"
  51.173 -        TestUtil.createMessages(number: 10, engineProccesed: true, inFolder: folder)
  51.174 -        TestUtil.createMessage(inFolder: folder,
  51.175 -                      from: Identity(address: "mail@mail.com"),
  51.176 -                      tos: [folder.account.user],
  51.177 -                      shortMessage: textToSearch,
  51.178 -                      uid: 666).save()
  51.179 +        TestUtil.createMessages(number: 10, engineProccesed: true, inFolder: inbox)
  51.180 +        TestUtil.createMessage(inFolder: inbox,
  51.181 +                               from: Identity(address: "mail@mail.com"),
  51.182 +                               tos: [inbox.account.user],
  51.183 +                               shortMessage: textToSearch,
  51.184 +                               uid: 666).save()
  51.185          setupViewModel()
  51.186          emailListVM.startMonitoring()
  51.187          XCTAssertEqual(emailListVM.rowCount, 11)
  51.188 @@ -254,16 +252,16 @@
  51.189      func testSetSearchMultipleSitesMatchInMessagesWith2results() {
  51.190          let textToSearch = "searchTest"
  51.191          let longText = "bla " + textToSearch + " bla"
  51.192 -        TestUtil.createMessages(number: 10, engineProccesed: true, inFolder: folder)
  51.193 -        TestUtil.createMessage(inFolder: folder,
  51.194 -                      from: Identity(address: "mail@mail.com"),
  51.195 -                      shortMessage: textToSearch,
  51.196 -                      uid: 666).save()
  51.197 -        TestUtil.createMessage(inFolder: folder,
  51.198 -                      from: Identity(address: "mail@mail.com"),
  51.199 -                      tos: [folder.account.user],
  51.200 -                      longMessage: longText,
  51.201 -                      uid: 667).save()
  51.202 +        TestUtil.createMessages(number: 10, engineProccesed: true, inFolder: inbox)
  51.203 +        TestUtil.createMessage(inFolder: inbox,
  51.204 +                               from: Identity(address: "mail@mail.com"),
  51.205 +                               shortMessage: textToSearch,
  51.206 +                               uid: 666).save()
  51.207 +        TestUtil.createMessage(inFolder: inbox,
  51.208 +                               from: Identity(address: "mail@mail.com"),
  51.209 +                               tos: [inbox.account.user],
  51.210 +                               longMessage: longText,
  51.211 +                               uid: 667).save()
  51.212          setupViewModel()
  51.213          emailListVM.startMonitoring()
  51.214          XCTAssertEqual(emailListVM.rowCount, 12)
  51.215 @@ -272,131 +270,112 @@
  51.216      }
  51.217  
  51.218      // Threading feature is currently non-existing. Keep this code, might help later.
  51.219 -//    //thread view nos is totaly disabled that means always false
  51.220 -//    func testCheckIfSettingsChanged() {
  51.221 -//        setupViewModel()
  51.222 -//        emailListVM.startMonitoring()
  51.223 -//        XCTAssertFalse(AppSettings.threadedViewEnabled)
  51.224 -//        AppSettings.threadedViewEnabled = true
  51.225 -//        XCTAssertFalse(emailListVM.checkIfSettingsChanged())
  51.226 -//    }
  51.227 +    //    //thread view nos is totaly disabled that means always false
  51.228 +    //    func testCheckIfSettingsChanged() {
  51.229 +    //        setupViewModel()
  51.230 +    //        emailListVM.startMonitoring()
  51.231 +    //        XCTAssertFalse(AppSettings.threadedViewEnabled)
  51.232 +    //        AppSettings.threadedViewEnabled = true
  51.233 +    //        XCTAssertFalse(emailListVM.checkIfSettingsChanged())
  51.234 +    //    }
  51.235  
  51.236      // MARK: - cell for row
  51.237 -/*
  51.238 -    func testIndexFromMessage() {
  51.239 -        let msgs = TestUtil.createMessages(number: 10, inFolder: folder)
  51.240 -        setupViewModel()
  51.241 -        emailListVM.startMonitoring()
  51.242 -        var index = emailListVM.index(of: msgs[0])
  51.243 -        XCTAssertEqual(index, 9)
  51.244 -        index = emailListVM.index(of: msgs[9])
  51.245 -        XCTAssertEqual(index, 0)
  51.246 -    }*/
  51.247 +    /*
  51.248 +     func testIndexFromMessage() {
  51.249 +     let msgs = TestUtil.createMessages(number: 10, inFolder: folder)
  51.250 +     setupViewModel()
  51.251 +     emailListVM.startMonitoring()
  51.252 +     var index = emailListVM.index(of: msgs[0])
  51.253 +     XCTAssertEqual(index, 9)
  51.254 +     index = emailListVM.index(of: msgs[9])
  51.255 +     XCTAssertEqual(index, 0)
  51.256 +     }*/
  51.257  
  51.258      func testViewModel() {
  51.259 -        let msg = TestUtil.createMessage(inFolder: folder, from: folder.account.user, uid: 1)
  51.260 +        let msg = TestUtil.createMessage(inFolder: inbox, from: inbox.account.user, uid: 1)
  51.261          msg.save()
  51.262          setupViewModel()
  51.263          emailListVM.startMonitoring()
  51.264 -        let index = emailListVM.index(of: msg)
  51.265 -        guard let ind = index else {
  51.266 -            XCTFail()
  51.267 -            return
  51.268 -        }
  51.269 -        let vm = emailListVM.viewModel(for: ind)
  51.270 +        let indexOfTheOneAndOnlyMsg = 0
  51.271 +        let vm = emailListVM.viewModel(for: indexOfTheOneAndOnlyMsg)
  51.272          XCTAssertEqual(vm?.message(), msg)
  51.273          XCTAssertEqual(vm?.subject, msg.shortMessage)
  51.274      }
  51.275  
  51.276 -//    func testSetUpFilterViewModel() {
  51.277 -//        var filterEnabled = false
  51.278 -//        setupViewModel()
  51.279 -//        XCTAssertEqual(filterEnabled, emailListVM.isFilterEnabled)
  51.280 -//        filterEnabled = true
  51.281 -//        setUpViewModelExpectations(expectedUpdateView: true)
  51.282 -//        emailListVM.isFilterEnabled = filterEnabled
  51.283 -//        waitForExpectations(timeout: TestUtil.waitTime)
  51.284 -//        XCTAssertEqual(filterEnabled, emailListVM.isFilterEnabled)
  51.285 -//    }
  51.286 +    //    func testSetUpFilterViewModel() {
  51.287 +    //        var filterEnabled = false
  51.288 +    //        setupViewModel()
  51.289 +    //        XCTAssertEqual(filterEnabled, emailListVM.isFilterEnabled)
  51.290 +    //        filterEnabled = true
  51.291 +    //        setUpViewModelExpectations(expectedUpdateView: true)
  51.292 +    //        emailListVM.isFilterEnabled = filterEnabled
  51.293 +    //        waitForExpectations(timeout: TestUtil.waitTime)
  51.294 +    //        XCTAssertEqual(filterEnabled, emailListVM.isFilterEnabled)
  51.295 +    //    }
  51.296  
  51.297      func testNewMessageReceivedAndDisplayedInTheCorrectPosition() {
  51.298 -        TestUtil.createMessages(number: 10, engineProccesed: true, inFolder: folder)
  51.299 +        var messages = TestUtil.createMessages(number: 10, engineProccesed: true, inFolder: inbox)
  51.300          setupViewModel()
  51.301          emailListVM.startMonitoring()
  51.302 -        XCTAssertEqual(emailListVM.rowCount, 10)
  51.303 +        XCTAssertEqual(emailListVM.rowCount, messages.count)
  51.304          setUpViewModelExpectations(expectationDidInsertDataAt: true)
  51.305 -        let msg = TestUtil.createMessage(inFolder: folder, from: folder.account.user)
  51.306 -        msg.save()
  51.307 +        let msg = TestUtil.createMessage(inFolder: inbox, from: inbox.account.user)
  51.308 +        messages.append(msg)
  51.309 +        Session.main.commit()
  51.310          waitForExpectations(timeout: TestUtil.waitTime)
  51.311 -        XCTAssertEqual(emailListVM.rowCount, 11)
  51.312 -        var index = emailListVM.index(of: msg)
  51.313 -        XCTAssertEqual(index, 0)
  51.314 -        let nonShownMsg = TestUtil.createMessage(inFolder: trashFolder, from: folder.account.user)
  51.315 -        nonShownMsg.save()
  51.316 -        XCTAssertEqual(emailListVM.rowCount, 11)
  51.317 -        index = emailListVM.index(of: msg)
  51.318 -        XCTAssertEqual(index, 0)
  51.319 +        XCTAssertEqual(emailListVM.rowCount, messages.count)
  51.320 +
  51.321 +        guard let firstMsgVM = emailListVM.viewModel(for: 0) else {
  51.322 +            XCTFail()
  51.323 +            return
  51.324 +        }
  51.325 +        XCTAssertEqual(firstMsgVM.message(), msg)
  51.326 +
  51.327 +        // Create a message that must not be shown
  51.328 +        TestUtil.createMessage(inFolder: trashFolder, from: inbox.account.user)
  51.329 +        Session.main.commit()
  51.330 +        XCTAssertEqual(emailListVM.rowCount, messages.count)
  51.331      }
  51.332  
  51.333      func testNewMessageUpdateReceivedAndDisplayed() {
  51.334 -        let numMails = 10
  51.335 -        TestUtil.createMessages(number: numMails, engineProccesed: true, inFolder: folder)
  51.336 -        let msg = TestUtil.createMessage(inFolder: folder,
  51.337 -                                         from: folder.account.user,
  51.338 -                                         uid: numMails + 1)
  51.339 -        msg.imapFlags.flagged = false
  51.340 -        msg.save()
  51.341 -        XCTAssertFalse((msg.imapFlags.flagged))
  51.342 +        var messages = TestUtil.createMessages(number: 10, engineProccesed: true, inFolder: inbox)
  51.343 +        Session.main.commit()
  51.344          setupViewModel()
  51.345          emailListVM.startMonitoring()
  51.346 -        XCTAssertEqual(emailListVM.rowCount, 11)
  51.347 -        msg.imapFlags.flagged = true
  51.348 -        msg.save()
  51.349 -        waitForExpectations(timeout: TestUtil.waitTime)
  51.350 -        var index = emailListVM.index(of: msg)
  51.351 -        if let ind = index {
  51.352 -            let newMsg = emailListVM.message(representedByRowAt: IndexPath(row: ind, section: 0))
  51.353 -            XCTAssertTrue((newMsg?.imapFlags.flagged)!)
  51.354 -        } else {
  51.355 -            XCTFail()
  51.356 +        XCTAssertEqual(emailListVM.rowCount, messages.count)
  51.357 +        waitForExpectations(timeout: TestUtil.waitTime) //BUFF: rm?
  51.358 +
  51.359 +        let numFlagged = 2
  51.360 +        for i in 0..<numFlagged {
  51.361 +            messages[i].imapFlags.flagged = true
  51.362          }
  51.363 +        Session.main.commit()
  51.364  
  51.365 -        let nonShownMsg = TestUtil.createMessage(inFolder: trashFolder, from: folder.account.user)
  51.366 -        nonShownMsg.save()
  51.367 -        XCTAssertEqual(emailListVM.rowCount, 11)
  51.368 -        index = emailListVM.index(of: nonShownMsg)
  51.369 -        XCTAssertNil(index)
  51.370 +        XCTAssertEqual(emailListVM.rowCount, messages.count - numFlagged)
  51.371      }
  51.372  
  51.373      func testNewMessageDeleteReceivedAndDisplayed() {
  51.374 -        let numMails = 10
  51.375 -        TestUtil.createMessages(number: 10, engineProccesed: true, inFolder: folder)
  51.376 -        let msg = TestUtil.createMessage(inFolder: folder,
  51.377 -                                         from: folder.account.user,
  51.378 -                                         uid: numMails + 1)
  51.379 -        msg.imapFlags.flagged = false
  51.380 -        msg.save()
  51.381 -        XCTAssertFalse((msg.imapFlags.flagged))
  51.382 +        var messages = TestUtil.createMessages(number: 10, engineProccesed: true, inFolder: inbox)
  51.383 +        Session.main.commit()
  51.384          setupViewModel()
  51.385          emailListVM.startMonitoring()
  51.386 -        XCTAssertEqual(emailListVM.rowCount, 11)
  51.387 +        XCTAssertEqual(emailListVM.rowCount, messages.count)
  51.388 +
  51.389 +
  51.390          setUpViewModelExpectations(expectationDidDeleteDataAt: true)
  51.391 -        msg.delete()
  51.392 +
  51.393 +        let numDelete = 1
  51.394 +        for i in 0..<numDelete {
  51.395 +            messages[i].cdMessage()?.imapFields().localFlags?.flagDeleted = true
  51.396 +        }
  51.397 +        Session.main.commit()
  51.398          waitForExpectations(timeout: TestUtil.waitTime)
  51.399 -        var index = emailListVM.index(of: msg)
  51.400 -        XCTAssertNil(index)
  51.401 -        XCTAssertEqual(emailListVM.rowCount, 10)
  51.402  
  51.403 -        let nonShownMsg = TestUtil.createMessage(inFolder: trashFolder, from: folder.account.user)
  51.404 -        nonShownMsg.save()
  51.405 -        nonShownMsg.delete()
  51.406 -        XCTAssertEqual(emailListVM.rowCount, 10)
  51.407 -        index = emailListVM.index(of: nonShownMsg)
  51.408 -        XCTAssertNil(index)
  51.409 +        XCTAssertEqual(emailListVM.rowCount, messages.count - numDelete)
  51.410      }
  51.411  
  51.412      func testgetMoveToFolderViewModel() {
  51.413 -        TestUtil.createMessages(number: 4, inFolder: folder)
  51.414 +        TestUtil.createMessages(number: 4, inFolder: inbox)
  51.415          let index: [IndexPath] = [IndexPath(row: 0, section: 1),
  51.416                                    IndexPath(row: 0, section: 2)]
  51.417          setupViewModel()
  51.418 @@ -408,41 +387,6 @@
  51.419          XCTAssertEqual(index.count, postMessages.count)
  51.420      }
  51.421  
  51.422 -    func testFlagUnflagMessageIsImmediate() {
  51.423 -        let message = givenThereIsAMessageIn(folderType: .inbox)
  51.424 -        let messageMoc = message?.cdMessage()?.managedObjectContext
  51.425 -        setupViewModel()
  51.426 -        emailListVM.startMonitoring()
  51.427 -
  51.428 -        let indexPath = IndexPath(row: 0, section: 0)
  51.429 -
  51.430 -        emailListVM.setFlagged(forIndexPath: [indexPath])
  51.431 -        guard let isFlagged = emailListVM.viewModel(for: indexPath.row)?.isFlagged else {
  51.432 -            XCTFail()
  51.433 -            return
  51.434 -        }
  51.435 -
  51.436 -        emailListVM.unsetFlagged(forIndexPath: [indexPath])
  51.437 -        guard let isNotFlagged = emailListVM.viewModel(for: indexPath.row)?.isFlagged else {
  51.438 -            XCTFail()
  51.439 -            return
  51.440 -        }
  51.441 -        let messageDidSaveExpectation = expectation(description: "message is saved")
  51.442 -        messageDidSaveExpectation.expectedFulfillmentCount = 8
  51.443 -
  51.444 -        NotificationCenter.default
  51.445 -            .addObserver(forName: Notification.Name.NSManagedObjectContextDidSave,
  51.446 -                         object: messageMoc,
  51.447 -                         queue: nil) { (notification) in
  51.448 -                            print("fulfill")
  51.449 -                            messageDidSaveExpectation.fulfill()
  51.450 -        }
  51.451 -
  51.452 -        let isImmediate = isFlagged != isNotFlagged
  51.453 -        XCTAssertTrue(isImmediate)
  51.454 -        wait(for: [messageDidSaveExpectation], timeout: UnitTestUtils.waitTime)
  51.455 -    }
  51.456 -
  51.457      func testMessageInOutboxAreNonEditableAndNonSelectable() {
  51.458          TestUtil.createMessage(uid: 1, inFolder: outboxFolder)
  51.459          moc.saveAndLogErrors()
  51.460 @@ -456,7 +400,7 @@
  51.461      }
  51.462  
  51.463      func testMessageInInboxAreOnlySelectable() {
  51.464 -        TestUtil.createMessage(uid: 1, inFolder: folder)
  51.465 +        TestUtil.createMessage(uid: 1, inFolder: inbox)
  51.466          moc.saveAndLogErrors()
  51.467          setupViewModel()
  51.468          emailListVM.startMonitoring()
  51.469 @@ -480,14 +424,14 @@
  51.470      }
  51.471  
  51.472      func testComposePrefilledFromAccountIsCorrectlySettedWithOnlyOneAccount() {
  51.473 -        let expectedFrom = folder.account.user
  51.474 +        let expectedFrom = inbox.account.user
  51.475          setupViewModel()
  51.476          let composeVM = emailListVM.composeViewModelForNewMessage()
  51.477          XCTAssertEqual(composeVM.state.from, expectedFrom)
  51.478      }
  51.479  
  51.480      func testComposePrefilledFromAccountIsDefaultAccountFromUnifiedIboxWithMultipleAccounts() {
  51.481 -        let expectedFrom = folder.account.user
  51.482 +        let expectedFrom = inbox.account.user
  51.483          secondAccountSetUp()
  51.484          AppSettings.defaultAccount = acc.user.address
  51.485          setupViewModel(forfolder: UnifiedInbox())
  51.486 @@ -496,25 +440,28 @@
  51.487      }
  51.488  
  51.489      func testComposePrefilledFromAccountIsFolderAccountFromSpecificFolderWithMultipleAccounts() {
  51.490 -        let expectedFrom = folder.account.user
  51.491 +        let expectedFrom = inbox.account.user
  51.492          secondAccountSetUp()
  51.493 -        setupViewModel(forfolder: folder)
  51.494 +        setupViewModel(forfolder: inbox)
  51.495          let composeVM = emailListVM.composeViewModelForNewMessage()
  51.496          XCTAssertEqual(composeVM.state.from, expectedFrom)
  51.497      }
  51.498 +}
  51.499  
  51.500 +// MARK: - HELPER
  51.501  
  51.502 +extension EmailListViewModelTest {
  51.503  
  51.504 -    // Mark: - setting up
  51.505 -
  51.506 -    private func setUpViewModel(forFolder folder: DisplayableFolderProtocol, masterViewController: TestMasterViewController) {
  51.507 -        self.emailListVM = EmailListViewModel(emailListViewModelDelegate: masterViewController, folderToShow: folder)
  51.508 +    private func setUpViewModel(forFolder folder: DisplayableFolderProtocol,
  51.509 +                                masterViewController: TestMasterViewController) {
  51.510 +        self.emailListVM = EmailListViewModel(emailListViewModelDelegate: masterViewController,
  51.511 +                                              folderToShow: folder)
  51.512      }
  51.513  
  51.514      private func setupViewModel(forfolder internalFolder: DisplayableFolderProtocol? = nil) {
  51.515          let folderToUse: DisplayableFolderProtocol
  51.516          if internalFolder == nil {
  51.517 -            folderToUse = folder
  51.518 +            folderToUse = inbox
  51.519          } else {
  51.520              folderToUse = internalFolder!
  51.521          }
  51.522 @@ -522,16 +469,16 @@
  51.523      }
  51.524  
  51.525      /*private func setSearchFilter(text: String) {
  51.526 -        setNewUpdateViewExpectation()
  51.527 -        emailListVM.setSearchFilter(forSearchText: text)
  51.528 -        waitForExpectations(timeout: TestUtil.waitTime)
  51.529 -    }
  51.530 +     setNewUpdateViewExpectation()
  51.531 +     emailListVM.setSearchFilter(forSearchText: text)
  51.532 +     waitForExpectations(timeout: TestUtil.waitTime)
  51.533 +     }
  51.534  
  51.535 -    private func removeSearchFilter() {
  51.536 -        setNewUpdateViewExpectation()
  51.537 -        emailListVM.removeSearchFilter()
  51.538 -        waitForExpectations(timeout: TestUtil.waitTime)
  51.539 -    }*/
  51.540 +     private func removeSearchFilter() {
  51.541 +     setNewUpdateViewExpectation()
  51.542 +     emailListVM.removeSearchFilter()
  51.543 +     waitForExpectations(timeout: TestUtil.waitTime)
  51.544 +     }*/
  51.545  
  51.546      private func setNewUpdateViewExpectation() {
  51.547          let updateViewExpectation = expectation(description: "UpdateViewCalled")
  51.548 @@ -540,13 +487,14 @@
  51.549  
  51.550      private func createViewModelWithExpectations(forFolder folder: DisplayableFolderProtocol, expectedUpdateView: Bool) {
  51.551          let viewModelTestDelegate = TestMasterViewController()
  51.552 +        masterViewController = viewModelTestDelegate
  51.553          setUpViewModel(forFolder: folder, masterViewController: viewModelTestDelegate)
  51.554      }
  51.555  
  51.556      private func setUpViewModelExpectations(expectedUpdateView: Bool = false,
  51.557 -                                                expectationDidInsertDataAt: Bool = false,
  51.558 -                                                expectationDidUpdateDataAt: Bool = false,
  51.559 -                                                expectationDidDeleteDataAt: Bool = false ) {
  51.560 +                                            expectationDidInsertDataAt: Bool = false,
  51.561 +                                            expectationDidUpdateDataAt: Bool = false,
  51.562 +                                            expectationDidDeleteDataAt: Bool = false ) {
  51.563          var expectationUpdateViewCalled: XCTestExpectation?
  51.564          if expectedUpdateView {
  51.565              expectationUpdateViewCalled = expectation(description: "UpdateViewCalled")
  51.566 @@ -570,15 +518,16 @@
  51.567                  expectation(description: "excpectationDidInsertDataAtCalled")
  51.568          }
  51.569  
  51.570 -        emailListVM.emailListViewModelDelegate = TestMasterViewController(
  51.571 -            expectationUpdateView: expectationUpdateViewCalled,
  51.572 -            expectationDidInsertDataAt: excpectationDidInsertDataAtCalled,
  51.573 -            expectationDidUpdateDataAt: excpectationDidUpdateDataAtCalled,
  51.574 -            expectationDidRemoveDataAt: excpectationDidDeleteDataAtCalled)
  51.575 +        masterViewController =
  51.576 +            TestMasterViewController(expectationUpdateView: expectationUpdateViewCalled,
  51.577 +                                     expectationDidInsertDataAt: excpectationDidInsertDataAtCalled,
  51.578 +                                     expectationDidUpdateDataAt: excpectationDidUpdateDataAtCalled,
  51.579 +                                     expectationDidRemoveDataAt: excpectationDidDeleteDataAtCalled)
  51.580 +        emailListVM.emailListViewModelDelegate = masterViewController
  51.581      }
  51.582  
  51.583      private func getSafeLastLookAt() -> Date {
  51.584 -        guard let safeLastLookedAt = folder?.lastLookedAt as Date? else {
  51.585 +        guard let safeLastLookedAt = inbox?.lastLookedAt as Date? else {
  51.586              XCTFail()
  51.587              return Date()
  51.588          }
  51.589 @@ -586,18 +535,19 @@
  51.590      }
  51.591  
  51.592      private func givenThereIsA(folderType: FolderType) {
  51.593 -        folder = Folder(name: "-", parent: folder, account: account, folderType: folderType)
  51.594 -        folder.save()
  51.595 +        inbox = Folder(name: "-", parent: inbox, account: account, folderType: folderType)
  51.596 +        inbox.save()
  51.597      }
  51.598  
  51.599      @discardableResult private func givenThereIsAMessageIn(folderType: FolderType) -> Message? {
  51.600          givenThereIsA(folderType: folderType)
  51.601 -        return TestUtil.createMessages(number: 1, engineProccesed: true, inFolder: folder).first
  51.602 +        let msg = TestUtil.createMessages(number: 1, engineProccesed: true, inFolder: inbox).first
  51.603 +        Session.main.commit()
  51.604 +        return msg
  51.605      }
  51.606  }
  51.607  
  51.608 -class TestMasterViewController: EmailListViewModelDelegate {
  51.609 -
  51.610 +private class TestMasterViewController: EmailListViewModelDelegate {
  51.611      var expectationUpdateViewCalled: XCTestExpectation?
  51.612      var excpectationDidInsertDataAtCalled: XCTestExpectation?
  51.613      var expectationDidUpdateDataAtCalled: XCTestExpectation?
  51.614 @@ -616,17 +566,14 @@
  51.615  
  51.616      func willReceiveUpdates(viewModel: EmailListViewModel) {
  51.617          //not yet defined
  51.618 -        XCTFail()
  51.619      }
  51.620  
  51.621      func allUpdatesReceived(viewModel: EmailListViewModel) {
  51.622          //not yet defined
  51.623 -        XCTFail()
  51.624      }
  51.625  
  51.626      func reloadData(viewModel: EmailListViewModel) {
  51.627          //not yet defined
  51.628 -        XCTFail()
  51.629      }
  51.630  
  51.631  
  51.632 @@ -667,7 +614,7 @@
  51.633                              toIndexPath: IndexPath) {
  51.634          XCTFail()
  51.635      }
  51.636 -//not exist anymore
  51.637 +    //not exist anymore
  51.638      func emailListViewModel(viewModel: EmailListViewModel,
  51.639                              didUpdateUndisplayedMessage message: Message) {
  51.640          XCTFail()
    52.1 --- a/pEpForiOSTests/Models/Message+FakeMessageTest.swift	Tue Jul 30 17:36:20 2019 +0200
    52.2 +++ b/pEpForiOSTests/Models/Message+FakeMessageTest.swift	Mon Aug 19 09:32:27 2019 +0200
    52.3 @@ -37,7 +37,7 @@
    52.4                      return
    52.5              }
    52.6              XCTAssertEqual(allCdMesgs.count, 1, "Exactly one faked message exists in CD")
    52.7 -            let all = folder.allMessagesNonThreaded()
    52.8 +            let all = folder.allMessages()
    52.9              XCTAssertEqual(all.count, 1, "Fake message is shown")
   52.10              guard let testee = all.first else {
   52.11                  XCTFail()
   52.12 @@ -49,23 +49,17 @@
   52.13  
   52.14      // MARK: - isFakeMessage
   52.15  
   52.16 -    func testIsFakeMessage() {
   52.17 -        for folderTpe in FolderType.allCases {
   52.18 -            deleteAllMessages()
   52.19 -            guard
   52.20 -                let folder = assureCleanFolderContainingExactlyOneFakeMessage(folderType: folderTpe)
   52.21 -                else {
   52.22 -                    XCTFail()
   52.23 -                    return
   52.24 -            }
   52.25 -            let all = folder.allMessagesNonThreaded()
   52.26 -            guard let testee = all.first else {
   52.27 -                XCTFail()
   52.28 -                return
   52.29 -            }
   52.30 -            XCTAssertTrue(testee.isFakeMessage,
   52.31 -                          "All fake messages in all folder types MUST be recognized")
   52.32 -        }
   52.33 +    func testIsFakeMessage_itIs() {
   52.34 +        let testee = CdMessage(context: moc)
   52.35 +        testee.uid = Int32(CdMessage.uidFakeResponsivenes)
   52.36 +        XCTAssertTrue(testee.isFakeMessage)
   52.37 +    }
   52.38 +
   52.39 +    func testIsFakeMessage_itIsNot() {
   52.40 +        let testee = CdMessage(context: moc)
   52.41 +        let randomPositiveUID = 666
   52.42 +        testee.uid = Int32(randomPositiveUID)
   52.43 +        XCTAssertFalse(testee.isFakeMessage)
   52.44      }
   52.45  
   52.46      // MARK: - saveForAppend
   52.47 @@ -185,7 +179,6 @@
   52.48      }
   52.49  
   52.50      private func deleteAllMessages() {
   52.51 -        let moc: NSManagedObjectContext = Stack.shared.mainContext
   52.52          moc.performAndWait {
   52.53              guard let allCdMesgs = CdMessage.all() as? [CdMessage] else {
   52.54                  return
    53.1 --- a/pEpForiOSTests/Models/SetOwnKeyViewModelTests.swift	Tue Jul 30 17:36:20 2019 +0200
    53.2 +++ b/pEpForiOSTests/Models/SetOwnKeyViewModelTests.swift	Mon Aug 19 09:32:27 2019 +0200
    53.3 @@ -16,9 +16,7 @@
    53.4  class SetOwnKeyViewModelTests: CoreDataDrivenTestBase {
    53.5      var backgroundQueue: OperationQueue!
    53.6  
    53.7 -    /**
    53.8 -     The fingerprint that will be part of the call to set_own_key.
    53.9 -     */
   53.10 +    ///The fingerprint that will be part of the call to set_own_key.
   53.11      let leonsFingerprint = "63FC29205A57EB3AEB780E846F239B0F19B9EE3B"
   53.12  
   53.13      // MARK: - setUp, tearDown
   53.14 @@ -26,7 +24,7 @@
   53.15      override func setUp() {
   53.16          super.setUp()
   53.17  
   53.18 -        XCTAssertTrue(PEPUtil.pEpClean())
   53.19 +        XCTAssertTrue(PEPUtils.pEpClean())
   53.20  
   53.21          self.backgroundQueue = OperationQueue()
   53.22      }
   53.23 @@ -38,10 +36,11 @@
   53.24      }
   53.25  
   53.26      // MARK: - Tests
   53.27 +
   53.28      func testSetOwnKeyDirectly() {
   53.29          doTestSetOwnKey() {
   53.30              let leon = PEPIdentity(address: "iostest003@peptest.ch",
   53.31 -                                   userID: UUID().uuidString,
   53.32 +                                   userID: CdIdentity.pEpOwnUserID,
   53.33                                     userName: "Leon Kowalski",
   53.34                                     isOwn: true)
   53.35              try! session.update(leon)
   53.36 @@ -59,8 +58,11 @@
   53.37              XCTAssertEqual(vm.rawErrorString, nil)
   53.38          }
   53.39      }
   53.40 +}
   53.41  
   53.42 -    // MARK: - Helpers
   53.43 +// MARK: - HELPER
   53.44 +
   53.45 +extension SetOwnKeyViewModelTests {
   53.46  
   53.47      /**
   53.48       - Note: If you need to manually verify something:
   53.49 @@ -72,7 +74,7 @@
   53.50      private func doTestSetOwnKey(afterDecryption: () -> ()) {
   53.51          let cdOwnAccount1 = DecryptionUtil.createLocalAccount(
   53.52              ownUserName: "Rick Deckard",
   53.53 -            ownUserID: "rick_deckard_uid",
   53.54 +            ownUserID: CdIdentity.pEpOwnUserID,
   53.55              ownEmailAddress: "iostest001@peptest.ch",
   53.56              context: moc)
   53.57  
   53.58 @@ -83,7 +85,7 @@
   53.59  
   53.60          let cdOwnAccount2 = DecryptionUtil.createLocalAccount(
   53.61              ownUserName: "Leon Kowalski",
   53.62 -            ownUserID: "leon_kowalski_uid",
   53.63 +            ownUserID: CdIdentity.pEpOwnUserID,
   53.64              ownEmailAddress: "iostest003@peptest.ch",
   53.65              context: moc)
   53.66          let leonIdent = cdOwnAccount2.account().user
    54.1 --- a/pEpForiOSTests/Models/Settings/KeySyncServiceHandshakeDelegateMoc.swift	Tue Jul 30 17:36:20 2019 +0200
    54.2 +++ b/pEpForiOSTests/Models/Settings/KeySyncServiceHandshakeDelegateMoc.swift	Mon Aug 19 09:32:27 2019 +0200
    54.3 @@ -11,13 +11,14 @@
    54.4  import PEPObjCAdapterFramework
    54.5  
    54.6  class KeySyncServiceHandshakeDelegateMoc: KeySyncServiceHandshakeDelegate {
    54.7 -
    54.8      var presenter: UIViewController?
    54.9  
   54.10      func showHandshake(me: PEPIdentity,
   54.11                         partner: PEPIdentity,
   54.12                         completion: ((PEPSyncHandshakeResult) -> ())?) { }
   54.13  
   54.14 +    func showCurrentlyGroupingDevices() {}
   54.15 +
   54.16      func cancelHandshake() {}
   54.17  
   54.18      func showSuccessfullyGrouped() {}
    55.1 --- a/pEpForiOSTests/Models/Settings/SettingsViewModelTest.swift	Tue Jul 30 17:36:20 2019 +0200
    55.2 +++ b/pEpForiOSTests/Models/Settings/SettingsViewModelTest.swift	Mon Aug 19 09:32:27 2019 +0200
    55.3 @@ -16,6 +16,11 @@
    55.4      var keySyncDeviceGroupServiceMoc: KeySyncDeviceGroupServiceMoc!
    55.5      var messageModelServiceMoc: MessageModelServiceMoc!
    55.6  
    55.7 +    func givenThereAreTwoAccounts() {
    55.8 +        _ = SecretTestData().createWorkingCdAccount(number: 1, context: moc)
    55.9 +        moc.saveAndLogErrors()
   55.10 +    }
   55.11 +
   55.12  
   55.13      //Number of sections corresponding to SettingsSectionViewModel.SectionType count
   55.14      let sections = 4
   55.15 @@ -54,9 +59,25 @@
   55.16          XCTAssertTrue(thereIsOneLessAccount)
   55.17      }
   55.18  
   55.19 -    func givenThereAreTwoAccounts() {
   55.20 -        _ = SecretTestData().createWorkingCdAccount(number: 1, context: moc)
   55.21 -        moc.saveAndLogErrors()
   55.22 +    func testDeleteAccountWithMoreThanOneAccountUpdatesDefaultAccount() {
   55.23 +
   55.24 +        givenThereAreTwoAccounts()
   55.25 +        setupViewModel()
   55.26 +
   55.27 +        let firstAccountPosition = (0,0)
   55.28 +        let secondAccountPosition = (0,0)
   55.29 +        let defaultAddress = (settingsVM[0][0] as? SettingsCellViewModel)?.account?.user.address
   55.30 +
   55.31 +        AppSettings.defaultAccount = defaultAddress
   55.32 +        XCTAssertEqual(AppSettings.defaultAccount, defaultAddress)
   55.33 +
   55.34 +        settingsVM.delete(section: firstAccountPosition.0, cell: firstAccountPosition.1)
   55.35 +
   55.36 +        XCTAssertNotEqual(AppSettings.defaultAccount, defaultAddress)
   55.37 +        XCTAssertNotNil(AppSettings.defaultAccount)
   55.38 +        let newDefaultAddress = (settingsVM[secondAccountPosition.0][secondAccountPosition.1] as? SettingsCellViewModel)?.account?.user.address
   55.39 +        XCTAssertEqual(AppSettings.defaultAccount, newDefaultAddress)
   55.40 +
   55.41      }
   55.42  
   55.43      func testLeaveDeviceGroupPressed() {
   55.44 @@ -68,6 +89,11 @@
   55.45  
   55.46          // THEN
   55.47          XCTAssertTrue(keySyncDeviceGroupServiceMoc.didCallLeaveDeviceGroup)
   55.48 +        guard let section = keySyncSection() else { return }
   55.49 +        for cell in section.cells {
   55.50 +            guard let cell = cell as? SettingsActionCellViewModel else { continue }
   55.51 +            XCTAssertFalse(cell.type == .leaveKeySyncGroup)
   55.52 +        }
   55.53      }
   55.54  
   55.55      func testKeySyncEnabledSetTrue() {
   55.56 @@ -75,12 +101,13 @@
   55.57          setupViewModel()
   55.58  
   55.59          // WHEN
   55.60 -        for section in settingsVM.sections {
   55.61 -            guard section.type == SettingsSectionViewModel.SectionType.keySync else { continue }
   55.62 -            for cell in section.cells {
   55.63 -                guard let cell = cell as? EnableKeySyncViewModel else { continue }
   55.64 -                cell.setSwitch(value: true)
   55.65 -            }
   55.66 +        guard let section = keySyncSection() else {
   55.67 +            XCTFail()
   55.68 +            return
   55.69 +        }
   55.70 +        for cell in section.cells {
   55.71 +            guard let cell = cell as? EnableKeySyncViewModel else { continue }
   55.72 +            cell.setSwitch(value: true)
   55.73          }
   55.74  
   55.75          // THEN
   55.76 @@ -112,4 +139,12 @@
   55.77          keySyncDeviceGroupServiceMoc = KeySyncDeviceGroupServiceMoc()
   55.78          settingsVM = SettingsViewModel(messageModelServiceMoc, keySyncDeviceGroupServiceMoc)
   55.79      }
   55.80 +
   55.81 +    private func keySyncSection() -> SettingsSectionViewModel? {
   55.82 +        for section in settingsVM.sections {
   55.83 +            guard section.type == SettingsSectionViewModel.SectionType.keySync else { continue }
   55.84 +            return section
   55.85 +        }
   55.86 +        return nil
   55.87 +    }
   55.88  }
    56.1 --- a/pEpForiOSTests/PepAdapterTests.swift	Tue Jul 30 17:36:20 2019 +0200
    56.2 +++ b/pEpForiOSTests/PepAdapterTests.swift	Mon Aug 19 09:32:27 2019 +0200
    56.3 @@ -24,7 +24,7 @@
    56.4      
    56.5      override func setUp() {
    56.6          super.setUp()
    56.7 -        XCTAssertTrue(PEPUtil.pEpClean())
    56.8 +        XCTAssertTrue(PEPUtils.pEpClean())
    56.9      }
   56.10      
   56.11      override func tearDown() {
    57.1 --- a/pEpForiOSTests/QualifyServerIsLocalServiceTest.swift	Tue Jul 30 17:36:20 2019 +0200
    57.2 +++ b/pEpForiOSTests/QualifyServerIsLocalServiceTest.swift	Mon Aug 19 09:32:27 2019 +0200
    57.3 @@ -12,6 +12,36 @@
    57.4  
    57.5  @testable import pEpForiOS
    57.6  
    57.7 +//!!!: must be moved to MM as the tested class is also in MM.
    57.8 +class QualifyServerIsLocalServiceTest: XCTestCase {
    57.9 +
   57.10 +    func testServerQualification() {
   57.11 +        XCTAssertEqual(isLocalServer(serverName: "localhost"), true)
   57.12 +        XCTAssertEqual(isLocalServer(serverName: "peptest.ch"), false)
   57.13 +    }
   57.14 +}
   57.15 +
   57.16 +// MARK: - HELPER
   57.17 +
   57.18 +extension QualifyServerIsLocalServiceTest {
   57.19 +
   57.20 +    private func isLocalServer(serverName: String) -> Bool? {
   57.21 +        let expQualified = expectation(description: "expQualified")
   57.22 +        let myDelegate = QualifyTestDelegate(expQualified: expQualified)
   57.23 +        let service = QualifyServerIsLocalService()
   57.24 +        service.delegate = myDelegate
   57.25 +        service.qualify(serverName: serverName)
   57.26 +
   57.27 +        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   57.28 +            XCTAssertNil(error)
   57.29 +            XCTAssertNil(myDelegate.error)
   57.30 +            XCTAssertEqual(myDelegate.serverName, serverName)
   57.31 +        })
   57.32 +
   57.33 +        return myDelegate.isLocal
   57.34 +    }
   57.35 +}
   57.36 +
   57.37  class QualifyTestDelegate: QualifyServerIsLocalServiceDelegate {
   57.38      let expQualified: XCTestExpectation
   57.39  
   57.40 @@ -30,26 +60,3 @@
   57.41          expQualified.fulfill()
   57.42      }
   57.43  }
   57.44 -
   57.45 -class QualifyServerIsLocalServiceTest: XCTestCase {
   57.46 -    func testServerQualification() {
   57.47 -        XCTAssertEqual(isLocalServer(serverName: "localhost"), true)
   57.48 -        XCTAssertEqual(isLocalServer(serverName: "peptest.ch"), false)
   57.49 -    }
   57.50 -
   57.51 -    func isLocalServer(serverName: String) -> Bool? {
   57.52 -        let expQualified = expectation(description: "expQualified")
   57.53 -        let myDelegate = QualifyTestDelegate(expQualified: expQualified)
   57.54 -        let service = QualifyServerIsLocalService()
   57.55 -        service.delegate = myDelegate
   57.56 -        service.qualify(serverName: serverName)
   57.57 -
   57.58 -        waitForExpectations(timeout: TestUtil.waitTime, handler: { error in
   57.59 -            XCTAssertNil(error)
   57.60 -            XCTAssertNil(myDelegate.error)
   57.61 -            XCTAssertEqual(myDelegate.serverName, serverName)
   57.62 -        })
   57.63 -
   57.64 -        return myDelegate.isLocal
   57.65 -    }
   57.66 -}
    58.1 --- a/pEpForiOSTests/ReplicationServiceTests.swift	Tue Jul 30 17:36:20 2019 +0200
    58.2 +++ b/pEpForiOSTests/ReplicationServiceTests.swift	Mon Aug 19 09:32:27 2019 +0200
    58.3 @@ -99,7 +99,7 @@
    58.4  ////            XCTAssertTrue(msg.isValidMessage())
    58.5  ////
    58.6  ////            let pEpRating = Int16(msg.pEpRatingInt ?? -1)
    58.7 -////            XCTAssertNotEqual(pEpRating, PEPUtil.pEpRatingNone)
    58.8 +////            XCTAssertNotEqual(pEpRating, PEPUtils.pEpRatingNone)
    58.9  ////            if !modelDelegate.contains(messageID: msg.messageID) {
   58.10  ////                XCTFail()
   58.11  ////            }
    59.1 --- a/pEpForiOSTests/TestUtils/CoreDataDrivenTestBase.swift	Tue Jul 30 17:36:20 2019 +0200
    59.2 +++ b/pEpForiOSTests/TestUtils/CoreDataDrivenTestBase.swift	Mon Aug 19 09:32:27 2019 +0200
    59.3 @@ -50,7 +50,7 @@
    59.4          imapSyncData?.sync?.close()
    59.5          Stack.shared.reset()
    59.6          PEPSession.cleanup()
    59.7 -        XCTAssertTrue(PEPUtil.pEpClean())
    59.8 +        XCTAssertTrue(PEPUtils.pEpClean())
    59.9          super.tearDown()
   59.10      }
   59.11  
    60.1 --- a/pEpForiOSTests/TestUtils/DecryptionUtil.swift	Tue Jul 30 17:36:20 2019 +0200
    60.2 +++ b/pEpForiOSTests/TestUtils/DecryptionUtil.swift	Mon Aug 19 09:32:27 2019 +0200
    60.3 @@ -14,6 +14,7 @@
    60.4  @testable import MessageModel
    60.5  
    60.6  class DecryptionUtil {
    60.7 +
    60.8      public static func decryptTheMessage(
    60.9          testCase: XCTestCase,
   60.10          backgroundQueue: OperationQueue,
   60.11 @@ -48,7 +49,7 @@
   60.12          }
   60.13  
   60.14          XCTAssertEqual(decryptDelegate.numberOfMessageDecryptAttempts, 1)
   60.15 -        Record.Context.main.refreshAllObjects()
   60.16 +        Stack.shared.mainContext.refreshAllObjects()
   60.17  
   60.18          guard
   60.19              let cdRecipients = cdMessage.to?.array as? [CdIdentity],
    61.1 --- a/pEpForiOSTests/TestUtils/Message+TestUtils.swift	Tue Jul 30 17:36:20 2019 +0200
    61.2 +++ b/pEpForiOSTests/TestUtils/Message+TestUtils.swift	Mon Aug 19 09:32:27 2019 +0200
    61.3 @@ -33,16 +33,16 @@
    61.4              dict[kPepLongMessageFormatted] = text as NSString
    61.5          }
    61.6  
    61.7 -        dict[kPepTo] = NSArray(array: to.map() { return PEPUtil.pEp(identity: $0) })
    61.8 -        dict[kPepCC] = NSArray(array: cc.map() { return PEPUtil.pEp(identity: $0) })
    61.9 -        dict[kPepBCC] = NSArray(array: bcc.map() { return PEPUtil.pEp(identity: $0) })
   61.10 +        dict[kPepTo] = NSArray(array: to.map() { return $0.pEpIdentity() })
   61.11 +        dict[kPepCC] = NSArray(array: cc.map() { return $0.pEpIdentity() })
   61.12 +        dict[kPepBCC] = NSArray(array: bcc.map() { return $0.pEpIdentity() })
   61.13  
   61.14 -        dict[kPepFrom]  = PEPUtil.pEpOptional(identity: from) as AnyObject
   61.15 +        dict[kPepFrom]  = PEPUtils.pEpOptional(identity: from) as AnyObject
   61.16          dict[kPepID] = messageID as AnyObject
   61.17          dict[kPepOutgoing] = outgoing as AnyObject?
   61.18  
   61.19          dict[kPepAttachments] = NSArray(array: attachments.map() {
   61.20 -            return PEPUtil.pEpAttachment(attachment: $0)
   61.21 +            return PEPUtils.pEpAttachment(attachment: $0)
   61.22          })
   61.23  
   61.24          return dict
    62.1 --- a/pEpForiOSTests/TestUtils/PEPAppUtilWrappers.swift	Tue Jul 30 17:36:20 2019 +0200
    62.2 +++ b/pEpForiOSTests/TestUtils/PEPAppUtilWrappers.swift	Mon Aug 19 09:32:27 2019 +0200
    62.3 @@ -13,19 +13,12 @@
    62.4  import PEPObjCAdapterFramework
    62.5  
    62.6  // TODO: Duplicate.
    62.7 -extension CdIdentity {
    62.8 -    public func pEpIdentity() -> PEPIdentity {
    62.9 -        return PEPUtil.pEpDict(cdIdentity: self)
   62.10 -    }
   62.11 -}
   62.12 -
   62.13 -// TODO: Duplicate.
   62.14  extension PEPSession {
   62.15      public func encrypt(pEpMessageDict: PEPMessageDict,
   62.16                          encryptionFormat: PEPEncFormat = .PEP,
   62.17                          forSelf: PEPIdentity? = nil) throws -> (PEPStatus, NSDictionary?) {
   62.18 -        return try PEPUtil.encrypt(
   62.19 -            pEpMessageDict: pEpMessageDict, encryptionFormat: encryptionFormat,
   62.20 -            forSelf: forSelf, session: self)
   62.21 +        return try PEPUtils.encrypt(pEpMessageDict: pEpMessageDict,
   62.22 +                                   encryptionFormat: encryptionFormat,
   62.23 +                                   forSelf: forSelf)
   62.24      }
   62.25  }
    63.1 --- a/pEpForiOSTests/TestUtils/TestUtil.swift	Tue Jul 30 17:36:20 2019 +0200
    63.2 +++ b/pEpForiOSTests/TestUtils/TestUtil.swift	Mon Aug 19 09:32:27 2019 +0200
    63.3 @@ -396,7 +396,7 @@
    63.4      @discardableResult static func createMessages(number: Int,
    63.5                                                    engineProccesed: Bool = true,
    63.6                                                    inFolder: Folder,
    63.7 -                                                  setUids: Bool = true) -> [Message]{
    63.8 +                                                  setUids: Bool = true) -> [Message] {
    63.9          var messages : [Message] = []
   63.10          for i in 1...number {
   63.11              let uid = setUids ? i : nil
    64.1 --- a/pEpForiOSTests/UI/Compose/Cells/BodyCell/BodyCellViewModelTest.swift	Tue Jul 30 17:36:20 2019 +0200
    64.2 +++ b/pEpForiOSTests/UI/Compose/Cells/BodyCell/BodyCellViewModelTest.swift	Mon Aug 19 09:32:27 2019 +0200
    64.3 @@ -405,6 +405,43 @@
    64.4          waitForExpectations(timeout: UnitTestUtils.waitTime)
    64.5      }
    64.6  
    64.7 +    func testShouldReplaceText_attachment_multipleRemove() {
    64.8 +        let attachmentsToRemoveCount = 10
    64.9 +        shouldReplaceText_attachment(remove: attachmentsToRemoveCount)
   64.10 +    }
   64.11 +    func shouldReplaceText_attachment (remove: Int) {
   64.12 +        var textBuilder = NSAttributedString(string: "Test text")
   64.13 +        let range = NSRange(location: 0, length: 0)
   64.14 +        var initialAttachments = [Attachment]()
   64.15 +
   64.16 +        for i in 0..<remove {
   64.17 +            let testAttachment = createTestAttachment(fileName: String(i), addImage: true)
   64.18 +            textBuilder = insertTextattachment(for: testAttachment, in: range, of: textBuilder)
   64.19 +            initialAttachments.append(testAttachment)
   64.20 +        }
   64.21 +
   64.22 +        let attachmentToRemoveRange = NSRange(location: 0, length: textBuilder.length)
   64.23 +        let expectedAttachmentsLeft = [Attachment]()
   64.24 +
   64.25 +        setupAssertionDelegates(initialPlaintext: nil,
   64.26 +                                initialAttributedText: nil,
   64.27 +                                initialInlinedAttachments: initialAttachments,
   64.28 +                                expectInsertCalled: nil,
   64.29 +                                inserted: nil,
   64.30 +                                expUserWantsToAddMediaCalled: expUserWantsToAddMediaCalled(mustBeCalled: false),
   64.31 +                                expUserWantsToAddDocumentCalled: expUserWantsToAddDocumentCalled(mustBeCalled: false),
   64.32 +                                expInlinedAttachmentsCalled: expInlinedAttachmentChanged(mustBeCalled: true),
   64.33 +                                inlined: expectedAttachmentsLeft,
   64.34 +                                expBodyChangedCalled: expBodyChangedCalled(mustBeCalled: false),
   64.35 +                                exectedPlain: nil,
   64.36 +                                exectedHtml: nil)
   64.37 +        let shouldReplace = vm.shouldReplaceText(in: attachmentToRemoveRange,
   64.38 +                                                 of: textBuilder,
   64.39 +                                                 with: "")
   64.40 +        XCTAssertTrue(shouldReplace, "Should alway be true")
   64.41 +        waitForExpectations(timeout: UnitTestUtils.waitTime)
   64.42 +    }
   64.43 +
   64.44      // MARK: handleUserClickedSelectMedia
   64.45  
   64.46      func testHandleUserClickedSelectMedia() {
    65.1 --- a/pEpForiOSTests/UI/Compose/ViewModel/ComposeViewModelTest.swift	Tue Jul 30 17:36:20 2019 +0200
    65.2 +++ b/pEpForiOSTests/UI/Compose/ViewModel/ComposeViewModelTest.swift	Mon Aug 19 09:32:27 2019 +0200
    65.3 @@ -1,5 +1,7 @@
    65.4  //!!!: needs love!
    65.5  
    65.6 +//!!!: All test using the assert() method crash randomly due to the known issue (composeviewModel is running stuff in background (e.g.calculatePepRating() , maybe more) which we are not waiting for. to fix: extract calculatePepRating() to a dependency and mock it or wait for it to be called.
    65.7 +
    65.8  ////
    65.9  ////  ComposeViewModelTest.swift
   65.10  ////  pEpForiOSTests
   65.11 @@ -75,6 +77,7 @@
   65.12  //        XCTAssertTrue(testee === resultDelegate)
   65.13  //    }
   65.14  //
   65.15 +//
   65.16  //    func testInit_stateSetupCorrectly() {
   65.17  //        let mode = ComposeUtil.ComposeMode.replyAll
   65.18  //        let vm = ComposeViewModel(resultDelegate: nil,
   65.19 @@ -411,35 +414,35 @@
   65.20  //    private var subjectCellViewModel: SubjectCellViewModel? {
   65.21  //        return viewmodel(ofType: SubjectCellViewModel.self) as? SubjectCellViewModel
   65.22  //    }
   65.23 -//
   65.24 -//    //!!!: crash
   65.25 -////    func testSubjectCellViewModelDidChangeSubject() {
   65.26 -////        assert(contentChangedMustBeCalled: true,
   65.27 -////               focusSwitchedMustBeCalled: false,
   65.28 -////               validatedStateChangedMustBeCalled: false,
   65.29 -////               modelChangedMustBeCalled: false,
   65.30 -////               sectionChangedMustBeCalled: false,
   65.31 -////               colorBatchNeedsUpdateMustBeCalled: false,
   65.32 -////               hideSuggestionsMustBeCalled: false,
   65.33 -////               showSuggestionsMustBeCalled: false,
   65.34 -////               showMediaAttachmentPickerMustBeCalled: false,
   65.35 -////               hideMediaAttachmentPickerMustBeCalled: false,
   65.36 -////               showDocumentAttachmentPickerMustBeCalled: false,
   65.37 -////               documentAttachmentPickerDonePickerCalled: false,
   65.38 -////               didComposeNewMailMustBeCalled: false,
   65.39 -////               didModifyMessageMustBeCalled: false,
   65.40 -////               didDeleteMessageMustBeCalled: false)
   65.41 -////        guard let subjectVm = subjectCellViewModel  else {
   65.42 -////            XCTFail()
   65.43 -////            return
   65.44 -////        }
   65.45 -////        let newSubject = "testSubjectCellViewModelDidChangeSubject content"
   65.46 -////        subjectVm.content = newSubject
   65.47 -////        vm?.subjectCellViewModelDidChangeSubject(subjectVm)
   65.48 -////        XCTAssertEqual(vm?.state.subject, newSubject)
   65.49 -////        waitForExpectations(timeout: UnitTestUtils.waitTime)
   65.50 -////    }
   65.51 -//
   65.52 +////
   65.53 +////    //!!!: crash
   65.54 +//////    func testSubjectCellViewModelDidChangeSubject() {
   65.55 +//////        assert(contentChangedMustBeCalled: true,
   65.56 +//////               focusSwitchedMustBeCalled: false,
   65.57 +//////               validatedStateChangedMustBeCalled: false,
   65.58 +//////               modelChangedMustBeCalled: false,
   65.59 +//////               sectionChangedMustBeCalled: false,
   65.60 +//////               colorBatchNeedsUpdateMustBeCalled: false,
   65.61 +//////               hideSuggestionsMustBeCalled: false,
   65.62 +//////               showSuggestionsMustBeCalled: false,
   65.63 +//////               showMediaAttachmentPickerMustBeCalled: false,
   65.64 +//////               hideMediaAttachmentPickerMustBeCalled: false,
   65.65 +//////               showDocumentAttachmentPickerMustBeCalled: false,
   65.66 +//////               documentAttachmentPickerDonePickerCalled: false,
   65.67 +//////               didComposeNewMailMustBeCalled: false,
   65.68 +//////               didModifyMessageMustBeCalled: false,
   65.69 +//////               didDeleteMessageMustBeCalled: false)
   65.70 +//////        guard let subjectVm = subjectCellViewModel  else {
   65.71 +//////            XCTFail()
   65.72 +//////            return
   65.73 +//////        }
   65.74 +//////        let newSubject = "testSubjectCellViewModelDidChangeSubject content"
   65.75 +//////        subjectVm.content = newSubject
   65.76 +//////        vm?.subjectCellViewModelDidChangeSubject(subjectVm)
   65.77 +//////        XCTAssertEqual(vm?.state.subject, newSubject)
   65.78 +//////        waitForExpectations(timeout: UnitTestUtils.waitTime)
   65.79 +//////    }
   65.80 +////
   65.81  //    // MARK: - AccountCellViewModelResultDelegate handling
   65.82  //
   65.83  //    private var accountCellViewModel: AccountCellViewModel? {
   65.84 @@ -490,17 +493,17 @@
   65.85  //        return nil
   65.86  //    }
   65.87  //
   65.88 -//    func testRecipientCellViewModelDidChangeRecipients_to() {
   65.89 -//        assertRecipientCellViewModelDidChangeRecipients(fieldType: .to)
   65.90 -//    }
   65.91 -//
   65.92 -//    func testRecipientCellViewModelDidChangeRecipients_cc() {
   65.93 -//        assertRecipientCellViewModelDidChangeRecipients(fieldType: .cc)
   65.94 -//    }
   65.95 -//
   65.96 -//    func testRecipientCellViewModelDidChangeRecipients_bcc() {
   65.97 -//        assertRecipientCellViewModelDidChangeRecipients(fieldType: .bcc)
   65.98 -//    }
   65.99 +////    func testRecipientCellViewModelDidChangeRecipients_to() {
  65.100 +////        assertRecipientCellViewModelDidChangeRecipients(fieldType: .to)
  65.101 +////    }
  65.102 +////
  65.103 +////    func testRecipientCellViewModelDidChangeRecipients_cc() {
  65.104 +////        assertRecipientCellViewModelDidChangeRecipients(fieldType: .cc)
  65.105 +////    }
  65.106 +////
  65.107 +////    func testRecipientCellViewModelDidChangeRecipients_bcc() {
  65.108 +////        assertRecipientCellViewModelDidChangeRecipients(fieldType: .bcc)
  65.109 +////    }
  65.110  //
  65.111  //    func testRecipientCellViewModelDidEndEditing() {
  65.112  //        assert(contentChangedMustBeCalled: false,
  65.113 @@ -586,20 +589,22 @@
  65.114  //    }
  65.115  // */
  65.116  //
  65.117 -//    func testShowCancelActionsv() {
  65.118 -//        let msg = message()
  65.119 -//        assert(originalMessage: msg)
  65.120 -//        guard let testee = vm?.showCancelActions else {
  65.121 -//            XCTFail()
  65.122 -//            return
  65.123 -//        }
  65.124 -//        XCTAssertFalse(testee)
  65.125 -//    }
  65.126 +////    func testShowCancelActionsv() {
  65.127 +////        let msg = message()
  65.128 +////        assert(originalMessage: msg)
  65.129 +////        guard let testee = vm?.showCancelActions else {
  65.130 +////            XCTFail()
  65.131 +////            return
  65.132 +////        }
  65.133 +////        XCTAssertFalse(testee)
  65.134 +////    }
  65.135  //
  65.136  //    func testShowCancelActions_edited() {
  65.137  //        let msg = message()
  65.138  //        assert(originalMessage: msg)
  65.139 -//        vm?.state.toRecipients = [Identity(address: "testShow@Cancel.Actions")]
  65.140 +//        let idet = Identity(address: "testShow@Cancel.Actions")
  65.141 +//        idet.save()
  65.142 +//        vm?.state.toRecipients = [idet]
  65.143  //        guard let testee = vm?.showCancelActions else {
  65.144  //            XCTFail()
  65.145  //            return
  65.146 @@ -607,7 +612,43 @@
  65.147  //        XCTAssertTrue(testee)
  65.148  //    }
  65.149  //
  65.150 -//    func testHandleSaveActionTriggered() {
  65.151 +////    func testHandleSaveActionTriggered() {
  65.152 +////        assert(originalMessage: nil,
  65.153 +////               contentChangedMustBeCalled: false,
  65.154 +////               focusSwitchedMustBeCalled: false,
  65.155 +////               validatedStateChangedMustBeCalled: false,
  65.156 +////               modelChangedMustBeCalled: false,
  65.157 +////               sectionChangedMustBeCalled: false,
  65.158 +////               colorBatchNeedsUpdateMustBeCalled: false,
  65.159 +////               hideSuggestionsMustBeCalled: false,
  65.160 +////               showSuggestionsMustBeCalled: false,
  65.161 +////               showMediaAttachmentPickerMustBeCalled: false,
  65.162 +////               hideMediaAttachmentPickerMustBeCalled: false,
  65.163 +////               showDocumentAttachmentPickerMustBeCalled: false,
  65.164 +////               documentAttachmentPickerDonePickerCalled: false,
  65.165 +////               didComposeNewMailMustBeCalled: false,
  65.166 +////               didModifyMessageMustBeCalled: false,
  65.167 +////               didDeleteMessageMustBeCalled: false)
  65.168 +////
  65.169 +////        let testSubject = UUID().uuidString + "testSubject"
  65.170 +////        vm?.state.subject = testSubject
  65.171 +////
  65.172 +////        vm?.handleSaveActionTriggered()
  65.173 +////
  65.174 +////        guard
  65.175 +////            let draftsFolder = drafts,
  65.176 +////            let testeeDrafted = Message.by(uid: 0,
  65.177 +////                                           folderName: draftsFolder.name,
  65.178 +////                                           accountAddress: account.user.address)
  65.179 +////            else {
  65.180 +////                XCTFail("Message not saved to drafts")
  65.181 +////                return
  65.182 +////        }
  65.183 +////        XCTAssertEqual(testeeDrafted.shortMessage, testSubject)
  65.184 +////        waitForExpectations(timeout: UnitTestUtils.waitTime)
  65.185 +////    }
  65.186 +//    func testHandleCancelActionTrigered() {
  65.187 +//        // GIVEN
  65.188  //        assert(originalMessage: nil,
  65.189  //               contentChangedMustBeCalled: false,
  65.190  //               focusSwitchedMustBeCalled: false,
  65.191 @@ -624,22 +665,33 @@
  65.192  //               didComposeNewMailMustBeCalled: false,
  65.193  //               didModifyMessageMustBeCalled: false,
  65.194  //               didDeleteMessageMustBeCalled: false)
  65.195 -//
  65.196 -//        let testSubject = UUID().uuidString + "testSubject"
  65.197 -//        vm?.state.subject = testSubject
  65.198 -//
  65.199 -//        vm?.handleSaveActionTriggered()
  65.200 -//
  65.201 -//        guard
  65.202 -//            let draftsFolder = drafts,
  65.203 -//            let testeeDrafted = Message.by(uid: 0,
  65.204 -//                                           folderName: draftsFolder.name,
  65.205 -//                                           accountAddress: account.user.address)
  65.206 -//            else {
  65.207 -//                XCTFail("Message not saved to drafts")
  65.208 +//        //do handshake
  65.209 +//        let storyboard = UIStoryboard(name: "Handshake", bundle: nil)
  65.210 +//        guard let handshakeViewController = storyboard.instantiateViewController(withIdentifier:
  65.211 +//            "HandshakeViewControllerID") as? HandshakeViewController else {
  65.212 +//                XCTFail()
  65.213  //                return
  65.214  //        }
  65.215 -//        XCTAssertEqual(testeeDrafted.shortMessage, testSubject)
  65.216 +//        vm?.setup(handshakeViewController: handshakeViewController)
  65.217 +//        let handShakeMessage = handshakeViewController.message
  65.218 +//        var handShakeMessageUID: Int?
  65.219 +//        var handshakeMessageFolderName: String?
  65.220 +//        handShakeMessage?.session.performAndWait {
  65.221 +//            handShakeMessageUID = handShakeMessage?.uid
  65.222 +//            handshakeMessageFolderName = handShakeMessage?.parent.name
  65.223 +//        }
  65.224 +//        guard let safeHandShakeMessageUID = handShakeMessageUID,
  65.225 +//            let safeHandshakeMessageFolderName = handshakeMessageFolderName else {
  65.226 +//                XCTFail()
  65.227 +//                return
  65.228 +//        }
  65.229 +//
  65.230 +//        // WHEN
  65.231 +//        vm?.handleDeleteActionTriggered()
  65.232 +//
  65.233 +//        // THEN
  65.234 +//        let notSavedMessage = Message.by(uid: safeHandShakeMessageUID, folderName: safeHandshakeMessageFolderName, accountAddress: account.user.address)
  65.235 +//        XCTAssertNil(notSavedMessage)
  65.236  //        waitForExpectations(timeout: UnitTestUtils.waitTime)
  65.237  //    }
  65.238  //
  65.239 @@ -676,39 +728,39 @@
  65.240  ////        waitForExpectations(timeout: UnitTestUtils.waitTime)
  65.241  ////    }
  65.242  //
  65.243 -//    func testHandleSaveActionTriggered_origDrafts() {
  65.244 -//        let testMessageId = UUID().uuidString + "testHandleSaveActionTriggered"
  65.245 -//        let originalMessage = message(inFolderOfType: .drafts)
  65.246 -//        originalMessage.messageID = testMessageId
  65.247 -//        originalMessage.from = account.user
  65.248 -//
  65.249 -//        assert(originalMessage: originalMessage,
  65.250 -//               contentChangedMustBeCalled: false,
  65.251 -//               focusSwitchedMustBeCalled: false,
  65.252 -//               validatedStateChangedMustBeCalled: false,
  65.253 -//               modelChangedMustBeCalled: false,
  65.254 -//               sectionChangedMustBeCalled: false,
  65.255 -//               colorBatchNeedsUpdateMustBeCalled: false,
  65.256 -//               hideSuggestionsMustBeCalled: false,
  65.257 -//               showSuggestionsMustBeCalled: false,
  65.258 -//               showMediaAttachmentPickerMustBeCalled: false,
  65.259 -//               hideMediaAttachmentPickerMustBeCalled: false,
  65.260 -//               showDocumentAttachmentPickerMustBeCalled: false,
  65.261 -//               documentAttachmentPickerDonePickerCalled: false,
  65.262 -//               didComposeNewMailMustBeCalled: false,
  65.263 -//               didModifyMessageMustBeCalled: true,
  65.264 -//               didDeleteMessageMustBeCalled: true)
  65.265 -//        vm?.handleSaveActionTriggered()
  65.266 -//        let msgWithTestMessageId = Message.by(uid: originalMessage.uid,
  65.267 -//                                              uuid: originalMessage.uuid,
  65.268 -//                                              folderName: originalMessage.parent.name,
  65.269 -//                                              accountAddress: account.user.address,
  65.270 -//                                              includingDeleted: true)
  65.271 -//        XCTAssertTrue(msgWithTestMessageId?.imapFlags.deleted ?? false,
  65.272 -//                     "The user edited draft. Technically we save a new message, thus the original" +
  65.273 -//            " must be deleted.")
  65.274 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  65.275 -//    }
  65.276 +////    func testHandleSaveActionTriggered_origDrafts() {
  65.277 +////        let testMessageId = UUID().uuidString + "testHandleSaveActionTriggered"
  65.278 +////        let originalMessage = message(inFolderOfType: .drafts)
  65.279 +////        originalMessage.messageID = testMessageId
  65.280 +////        originalMessage.from = account.user
  65.281 +////
  65.282 +////        assert(originalMessage: originalMessage,
  65.283 +////               contentChangedMustBeCalled: false,
  65.284 +////               focusSwitchedMustBeCalled: false,
  65.285 +////               validatedStateChangedMustBeCalled: false,
  65.286 +////               modelChangedMustBeCalled: false,
  65.287 +////               sectionChangedMustBeCalled: false,
  65.288 +////               colorBatchNeedsUpdateMustBeCalled: false,
  65.289 +////               hideSuggestionsMustBeCalled: false,
  65.290 +////               showSuggestionsMustBeCalled: false,
  65.291 +////               showMediaAttachmentPickerMustBeCalled: false,
  65.292 +////               hideMediaAttachmentPickerMustBeCalled: false,
  65.293 +////               showDocumentAttachmentPickerMustBeCalled: false,
  65.294 +////               documentAttachmentPickerDonePickerCalled: false,
  65.295 +////               didComposeNewMailMustBeCalled: false,
  65.296 +////               didModifyMessageMustBeCalled: true,
  65.297 +////               didDeleteMessageMustBeCalled: true)
  65.298 +////        vm?.handleSaveActionTriggered()
  65.299 +////        let msgWithTestMessageId = Message.by(uid: originalMessage.uid,
  65.300 +////                                              uuid: originalMessage.uuid,
  65.301 +////                                              folderName: originalMessage.parent.name,
  65.302 +////                                              accountAddress: account.user.address,
  65.303 +////                                              includingDeleted: true)
  65.304 +////        XCTAssertTrue(msgWithTestMessageId?.imapFlags.deleted ?? false,
  65.305 +////                     "The user edited draft. Technically we save a new message, thus the original" +
  65.306 +////            " must be deleted.")
  65.307 +////        waitForExpectations(timeout: UnitTestUtils.waitTime)
  65.308 +////    }
  65.309  //
  65.310  //    func testHandleDeleteActionTriggered_normal() {
  65.311  //        assert(originalMessage: nil,
  65.312 @@ -727,10 +779,33 @@
  65.313  //               didComposeNewMailMustBeCalled: false,
  65.314  //               didModifyMessageMustBeCalled: false,
  65.315  //               didDeleteMessageMustBeCalled: false)
  65.316 -//        vm?.handleSaveActionTriggered()
  65.317 +//        vm?.handleDeleteActionTriggered()
  65.318  //        waitForExpectations(timeout: UnitTestUtils.waitTime)
  65.319  //    }
  65.320  //
  65.321 +//    //!!!: crashes randomly due to the known issue (composeviewModel is running stuff in background (e.g.calculatePepRating() , maybe more) which we are not waiting for. to fix: extract calculatePepRating() to a dependency and mock it or wait for it to be called.
  65.322 +//
  65.323 +////    func testHandleSaveActionTriggered_normal() {
  65.324 +////        assert(originalMessage: nil,
  65.325 +////               contentChangedMustBeCalled: false,
  65.326 +////               focusSwitchedMustBeCalled: false,
  65.327 +////               validatedStateChangedMustBeCalled: false,
  65.328 +////               modelChangedMustBeCalled: false,
  65.329 +////               sectionChangedMustBeCalled: false,
  65.330 +////               colorBatchNeedsUpdateMustBeCalled: false,
  65.331 +////               hideSuggestionsMustBeCalled: false,
  65.332 +////               showSuggestionsMustBeCalled: false,
  65.333 +////               showMediaAttachmentPickerMustBeCalled: false,
  65.334 +////               hideMediaAttachmentPickerMustBeCalled: false,
  65.335 +////               showDocumentAttachmentPickerMustBeCalled: false,
  65.336 +////               documentAttachmentPickerDonePickerCalled: false,
  65.337 +////               didComposeNewMailMustBeCalled: false,
  65.338 +////               didModifyMessageMustBeCalled: false,
  65.339 +////               didDeleteMessageMustBeCalled: false)
  65.340 +////        vm?.handleSaveActionTriggered()
  65.341 +////        waitForExpectations(timeout: UnitTestUtils.waitTime)
  65.342 +////    }
  65.343 +//
  65.344  //    //!!!: crash
  65.345  ////    func testHandleDeleteActionTriggered_origOutbox() {
  65.346  ////        let testMessageId = UUID().uuidString + "testHandleDeleteActionTriggered_origOutbox"
  65.347 @@ -766,11 +841,11 @@
  65.348  //
  65.349  //    // MARK: - Suggestions
  65.350  //
  65.351 -//    func testSuggestViewModel() {
  65.352 -//        let testee = vm?.suggestViewModel()
  65.353 -//        XCTAssertNotNil(testee)
  65.354 -//        XCTAssertTrue(testee?.resultDelegate === vm)
  65.355 -//    }
  65.356 +////    func testSuggestViewModel() {
  65.357 +////        let testee = vm?.suggestViewModel()
  65.358 +////        XCTAssertNotNil(testee)
  65.359 +////        XCTAssertTrue(testee?.resultDelegate === vm)
  65.360 +////    }
  65.361  //
  65.362  //    // showSuggestions and hideSuggestions are tested altering recipients
  65.363  //
  65.364 @@ -800,30 +875,30 @@
  65.365  ////        waitForExpectations(timeout: UnitTestUtils.waitTime)
  65.366  ////    }
  65.367  //
  65.368 -//    func testShowSuggestionsScrollFocus_empty() {
  65.369 -//        let expectedSuggestionsVisibility = false
  65.370 -//        assert(contentChangedMustBeCalled: false,
  65.371 -//               focusSwitchedMustBeCalled: false,
  65.372 -//               validatedStateChangedMustBeCalled: false,
  65.373 -//               expectedIsValidated: nil,
  65.374 -//               modelChangedMustBeCalled: false,
  65.375 -//               sectionChangedMustBeCalled: false,
  65.376 -//               colorBatchNeedsUpdateMustBeCalled: false,
  65.377 -//               hideSuggestionsMustBeCalled: false,
  65.378 -//               showSuggestionsMustBeCalled: false,
  65.379 -//               suggestionsScrollFocusChangedMustBeCalled: true,
  65.380 -//               expectedNewSuggestionsScrollFocusIsVisible: expectedSuggestionsVisibility,
  65.381 -//               showMediaAttachmentPickerMustBeCalled: false,
  65.382 -//               hideMediaAttachmentPickerMustBeCalled: false,
  65.383 -//               showDocumentAttachmentPickerMustBeCalled: false,
  65.384 -//               documentAttachmentPickerDonePickerCalled: false,
  65.385 -//               didComposeNewMailMustBeCalled: false,
  65.386 -//               didModifyMessageMustBeCalled: false,
  65.387 -//               didDeleteMessageMustBeCalled: false)
  65.388 -//        let _ = vm?.suggestViewModel(SuggestViewModel(),
  65.389 -//                                     didToggleVisibilityTo: expectedSuggestionsVisibility)
  65.390 -//        waitForExpectations(timeout: UnitTestUtils.waitTime)
  65.391 -//    }
  65.392 +////    func testShowSuggestionsScrollFocus_empty() {
  65.393 +////        let expectedSuggestionsVisibility = false
  65.394 +////        assert(contentChangedMustBeCalled: false,
  65.395 +////               focusSwitchedMustBeCalled: false,
  65.396 +////               validatedStateChangedMustBeCalled: false,
  65.397 +////               expectedIsValidated: nil,
  65.398 +////               modelChangedMustBeCalled: false,
  65.399 +////               sectionChangedMustBeCalled: false,
  65.400 +////               colorBatchNeedsUpdateMustBeCalled: false,
  65.401 +////               hideSuggestionsMustBeCalled: false,
  65.402 +////               showSuggestionsMustBeCalled: false,
  65.403 +////               suggestionsScrollFocusChangedMustBeCalled: true,
  65.404 +////               expectedNewSuggestionsScrollFocusIsVisible: expectedSuggestionsVisibility,
  65.405 +////               showMediaAttachmentPickerMustBeCalled: false,
  65.406 +////               hideMediaAttachmentPickerMustBeCalled: false,
  65.407 +////               showDocumentAttachmentPickerMustBeCalled: false,
  65.408 +////               documentAttachmentPickerDonePickerCalled: false,
  65.409 +////               didComposeNewMailMustBeCalled: false,
  65.410 +////               didModifyMessageMustBeCalled: false,
  65.411 +////               didDeleteMessageMustBeCalled: false)
  65.412 +////        let _ = vm?.suggestViewModel(SuggestViewModel(),
  65.413 +////                                     didToggleVisibilityTo: expectedSuggestionsVisibility)
  65.414 +////        waitForExpectations(timeout: UnitTestUtils.waitTime)
  65.415 +////    }
  65.416  //
  65.417  //// MARK: - ComposeViewModelStateDelegate Handling
  65.418  //
  65.419 @@ -962,51 +1037,50 @@
  65.420  //
  65.421  //    // MARK: - handleUserClickedSendButton
  65.422  //
  65.423 -//    func testHandleUserClickedSendButton() {
  65.424 -//        assert()
  65.425 -//        let toRecipient = Identity(address: "testHandleUserClickedSend@Butt.on")
  65.426 -//        vm?.state.toRecipients = [toRecipient]
  65.427 -//        vm?.state.from = account.user
  65.428 -//        let outMsgsBefore = Folder.by(account: account, folderType: .outbox)?
  65.429 -//            .allMessagesNonThreaded()
  65.430 -//            .count ?? -1
  65.431 -//        vm?.handleUserClickedSendButton()
  65.432 -//        let outMsgsAfter = Folder.by(account: account, folderType: .outbox)?
  65.433 -//            .allMessagesNonThreaded()
  65.434 -//            .count ?? -1
  65.435 -//        XCTAssertEqual(outMsgsAfter, outMsgsBefore + 1)
  65.436 -//        XCTAssertGreaterThan(outMsgsAfter, 0)
  65.437 -//    }
  65.438 +////    func testHandleUserClickedSendButton() {
  65.439 +////        assert()
  65.440 +////        let toRecipient = Identity(address: "testHandleUserClickedSend@Butt.on")
  65.441 +////        vm?.state.toRecipients = [toRecipient]
  65.442 +////        vm?.state.from = account.user
  65.443 +////        let outMsgsBefore = Folder.by(account: account, folderType: .outbox)?
  65.444 +////            .allMessagesNonThreaded()
  65.445 +////            .count ?? -1
  65.446 +////        vm?.handleUserClickedSendButton()
  65.447 +////        let outMsgsAfter = Folder.by(account: account, folderType: .outbox)?
  65.448 +////            .allMessagesNonThreaded()
  65.449 +////            .count ?? -1
  65.450 +////        XCTAssertEqual(outMsgsAfter, outMsgsBefore + 1)
  65.451 +////        XCTAssertGreaterThan(outMsgsAfter, 0)
  65.452 +////    }
  65.453  //
  65.454 -//    func testHandleUserClickedSendButton_origDraft() {
  65.455 -//        let testMessageId = UUID().uuidString + #function
  65.456 -//        let originalMessage = draftMessage()
  65.457 -//        originalMessage.messageID = testMessageId
  65.458 -//        originalMessage.from = account.user
  65.459 -//        originalMessage.save()
  65.460 -//        XCTAssertNotNil(Message.by(uid: originalMessage.uid,
  65.461 -//                                   uuid: originalMessage.uuid,
  65.462 -//                                   folderName: originalMessage.parent.name,
  65.463 -//                                   accountAddress: account.user.address))
  65.464 -//        assert(originalMessage: originalMessage)
  65.465 -//        let toRecipient = Identity(address: "testHandleUserClickedSend@Butt.on")
  65.466 -//        vm?.state.toRecipients = [toRecipient]
  65.467 -//        vm?.state.from = account.user
  65.468 -//        vm?.handleUserClickedSendButton()
  65.469 -//        guard
  65.470 -//            let originalDraftedMessageDeleted =
  65.471 -//            Message.by(uid: originalMessage.uid,
  65.472 -//                       uuid: originalMessage.uuid,
  65.473 -//                       folderName: originalMessage.parent.name,
  65.474 -//                       accountAddress: account.user.address,
  65.475 -//                       includingDeleted: true)?.imapFlags.deleted
  65.476 -//            else {
  65.477 -//                XCTFail()
  65.478 -//                return
  65.479 -//        }
  65.480 -//        XCTAssertTrue(originalDraftedMessageDeleted,
  65.481 -//                      "original drafted message must be flagged deleted")
  65.482 -//    }
  65.483 +////    func testHandleUserClickedSendButton_origDraft() {
  65.484 +////        let testMessageId = UUID().uuidString + #function
  65.485 +////        let originalMessage = draftMessage()
  65.486 +////        originalMessage.messageID = testMessageId
  65.487 +////        originalMessage.from = account.user
  65.488 +////        originalMessage.save()
  65.489 +////        XCTAssertNotNil(Message.by(uid: originalMessage.uid,
  65.490 +////                                   uuid: originalMessage.uuid,
  65.491 +////                                   folderName: originalMessage.parent.name,
  65.492 +////                                   accountAddress: account.user.address))
  65.493 +////        assert(originalMessage: originalMessage)
  65.494 +////        let toRecipient = Identity(address: "testHandleUserClickedSend@Butt.on")
  65.495 +////        vm?.state.toRecipients = [toRecipient]
  65.496 +////        vm?.state.from = account.user
  65.497 +////        vm?.handleUserClickedSendButton()
  65.498 +////        guard
  65.499 +////            let originalDraftedMessageDeleted =
  65.500 +////            Message.by(uid: originalMessage.uid,
  65.501 +////                       uuid: originalMessage.uuid,
  65.502 +////                       folderName: originalMessage.parent.name,
  65.503 +////                       accountAddress: account.user.address)?.imapFlags.deleted
  65.504 +////            else {
  65.505 +////                XCTFail()
  65.506 +////                return
  65.507 +////        }
  65.508 +////        XCTAssertTrue(originalDraftedMessageDeleted,
  65.509 +////                      "original drafted message must be flagged deleted")
  65.510 +////    }
  65.511  //
  65.512  //    //!!!: crash
  65.513  ////    func testHandleUserClickedSendButton_origOutbox() {
  65.514 @@ -1151,7 +1225,7 @@
  65.515  //        waitForExpectations(timeout: UnitTestUtils.waitTime)
  65.516  //    }
  65.517  //
  65.518 -//    // MARK: - viewModel(for:)
  65.519 +////    // MARK: - viewModel(for:)
  65.520  //
  65.521  //    //!!!: crash
  65.522  ////    func testViewModelForIndexPath() {
  65.523 @@ -1184,19 +1258,19 @@
  65.524  ////        waitForExpectations(timeout: UnitTestUtils.waitTime)
  65.525  ////    }
  65.526  //
  65.527 -//    func testViewModelForIndexPath_notAlwaysWrapper() {
  65.528 -//        assert()
  65.529 -//        guard let wrapperVM = viewmodel(ofType: WrappedBccViewModel.self) else {
  65.530 -//                XCTFail("No VM")
  65.531 -//                return
  65.532 -//        }
  65.533 -//        let toRecipientsIdxPath = IndexPath(row: 0, section: 0)
  65.534 -//        guard let testee = vm?.viewModel(for: toRecipientsIdxPath) else {
  65.535 -//            XCTFail()
  65.536 -//            return
  65.537 -//        }
  65.538 -//        XCTAssertFalse(testee === wrapperVM)
  65.539 -//    }
  65.540 +////    func testViewModelForIndexPath_notAlwaysWrapper() {
  65.541 +////        assert()
  65.542 +////        guard let wrapperVM = viewmodel(ofType: WrappedBccViewModel.self) else {
  65.543 +////                XCTFail("No VM")
  65.544 +////                return
  65.545 +////        }
  65.546 +////        let toRecipientsIdxPath = IndexPath(row: 0, section: 0)
  65.547 +////        guard let testee = vm?.viewModel(for: toRecipientsIdxPath) else {
  65.548 +////            XCTFail()
  65.549 +////            return
  65.550 +////        }
  65.551 +////        XCTAssertFalse(testee === wrapperVM)
  65.552 +////    }
  65.553  //
  65.554  //    // MARK: - beforePickerFocus
  65.555  //
    66.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    66.2 +++ b/pEpForiOSTests/UI/KeySyncHandshake/KeySyncHandshakeViewModelTest.swift	Mon Aug 19 09:32:27 2019 +0200
    66.3 @@ -0,0 +1,220 @@
    66.4 +//
    66.5 +//  KeySyncHandshakeViewModelTest.swift
    66.6 +//  pEpForiOSTests
    66.7 +//
    66.8 +//  Created by Alejandro Gelos on 05/07/2019.
    66.9 +//  Copyright © 2019 p≡p Security S.A. All rights reserved.
   66.10 +//
   66.11 +
   66.12 +import XCTest
   66.13 +@testable import pEpForiOS
   66.14 +
   66.15 +final class KeySyncHandshakeViewModelTest: XCTestCase {
   66.16 +
   66.17 +    var keySyncHandshakeVM: KeySyncHandshakeViewModel?
   66.18 +    var actual: State?
   66.19 +    var expected: State?
   66.20 +
   66.21 +    override func setUp() {
   66.22 +        super.setUp()
   66.23 +
   66.24 +        keySyncHandshakeVM = KeySyncHandshakeViewModel(pEpSession: PEPSessionMoc())
   66.25 +        keySyncHandshakeVM?.fingerPrints(meFPR: "", partnerFPR: "")
   66.26 +        keySyncHandshakeVM?.delegate = self
   66.27 +
   66.28 +        setDefaultActualState()
   66.29 +        keySyncHandshakeVM?.completionHandler = { [weak self] action in
   66.30 +            self?.actual?.pressedAction = action
   66.31 +        }
   66.32 +        expected = nil
   66.33 +    }
   66.34 +
   66.35 +    override func tearDown() {
   66.36 +        keySyncHandshakeVM?.delegate = nil
   66.37 +        keySyncHandshakeVM = nil
   66.38 +        actual = nil
   66.39 +        expected = nil
   66.40 +
   66.41 +        super.tearDown()
   66.42 +    }
   66.43 +
   66.44 +    func testDidSelectLanguageToOtherOrSame() {
   66.45 +        // GIVEN
   66.46 +        guard let keySyncHandshakeVM = keySyncHandshakeVM else {
   66.47 +            XCTFail()
   66.48 +            return
   66.49 +        }
   66.50 +        expected = State(didCallClosePicker: true, didCallToUpdateTrustedWords: true)
   66.51 +
   66.52 +        // WHEN
   66.53 +        keySyncHandshakeVM.didSelect(languageRow: 0)
   66.54 +
   66.55 +        // THEN
   66.56 +        assertExpectations()
   66.57 +    }
   66.58 +
   66.59 +    func testDidPressActionAccept() {
   66.60 +        // GIVEN
   66.61 +        guard let keySyncHandshakeVM = keySyncHandshakeVM else {
   66.62 +            XCTFail()
   66.63 +            return
   66.64 +        }
   66.65 +        expected = State(didCallDissmissView: true, pressedAction: .accept)
   66.66 +
   66.67 +        // WHEN
   66.68 +        keySyncHandshakeVM.handle(action: .accept)
   66.69 +
   66.70 +        // THEN
   66.71 +        assertExpectations()
   66.72 +    }
   66.73 +
   66.74 +    func testDidPressActionChangeLanguage() {
   66.75 +        // GIVEN
   66.76 +        guard let keySyncHandshakeVM = keySyncHandshakeVM else {
   66.77 +            XCTFail()
   66.78 +            return
   66.79 +        }
   66.80 +        expected = State(didCallShowPicker: true, languagesToShow: ["", ""])
   66.81 +
   66.82 +        // WHEN
   66.83 +        keySyncHandshakeVM.handle(action: .changeLanguage)
   66.84 +
   66.85 +        // THEN
   66.86 +        assertExpectations()
   66.87 +    }
   66.88 +
   66.89 +    func testDidPressActionDecline() {
   66.90 +        // GIVEN
   66.91 +        guard let keySyncHandshakeVM = keySyncHandshakeVM else {
   66.92 +            XCTFail()
   66.93 +            return
   66.94 +        }
   66.95 +        expected = State(didCallDissmissView: true, pressedAction: .decline)
   66.96 +
   66.97 +        // WHEN
   66.98 +        keySyncHandshakeVM.handle(action: .decline)
   66.99 +
  66.100 +        // THEN
  66.101 +        assertExpectations()
  66.102 +    }
  66.103 +
  66.104 +    func testDidPressActionCancel() {
  66.105 +        // GIVEN
  66.106 +        guard let keySyncHandshakeVM = keySyncHandshakeVM else {
  66.107 +            XCTFail()
  66.108 +            return
  66.109 +        }
  66.110 +        expected = State(didCallDissmissView: true, pressedAction: .cancel)
  66.111 +
  66.112 +        // WHEN
  66.113 +        keySyncHandshakeVM.handle(action: .cancel)
  66.114 +
  66.115 +        // THEN
  66.116 +        assertExpectations()
  66.117 +    }
  66.118 +
  66.119 +    func testDidLongPressWords() {
  66.120 +        // GIVEN
  66.121 +        guard let keySyncHandshakeVM = keySyncHandshakeVM else {
  66.122 +            XCTFail()
  66.123 +            return
  66.124 +        }
  66.125 +        expected = State(didCallToUpdateTrustedWords: true, fullWordsVersion: true)
  66.126 +
  66.127 +        // WHEN
  66.128 +        keySyncHandshakeVM.didLongPressWords()
  66.129 +
  66.130 +        // THEN
  66.131 +        assertExpectations()
  66.132 +    }
  66.133 +}
  66.134 +
  66.135 +// MARK: - KeySyncHandshakeViewModelDelegate
  66.136 +
  66.137 +extension KeySyncHandshakeViewModelTest: KeySyncHandshakeViewModelDelegate {
  66.138 +    func dissmissView() {
  66.139 +        actual?.didCallDissmissView = true
  66.140 +    }
  66.141 +
  66.142 +    func closePicker() {
  66.143 +        actual?.didCallClosePicker = true
  66.144 +    }
  66.145 +
  66.146 +    func showPicker(withLanguages languages: [String]) {
  66.147 +        actual?.didCallShowPicker = true
  66.148 +        actual?.languagesToShow = languages
  66.149 +    }
  66.150 +
  66.151 +    func change(handshakeWordsTo: String) {
  66.152 +        actual?.didCallToUpdateTrustedWords = true
  66.153 +        actual?.fullWordsVersion = keySyncHandshakeVM?.fullTrustWords
  66.154 +    }
  66.155 +}
  66.156 +
  66.157 +// MARK: - Private
  66.158 +
  66.159 +extension KeySyncHandshakeViewModelTest {
  66.160 +    private func setDefaultActualState() {
  66.161 +        actual = State()
  66.162 +    }
  66.163 +
  66.164 +    private func assertExpectations() {
  66.165 +        guard let expected = expected,
  66.166 +            let actual = actual else {
  66.167 +                XCTFail()
  66.168 +                return
  66.169 +        }
  66.170 +
  66.171 +        //bools
  66.172 +        XCTAssertEqual(expected.didCallShowPicker, actual.didCallShowPicker)
  66.173 +        XCTAssertEqual(expected.didCallClosePicker, actual.didCallClosePicker)
  66.174 +        XCTAssertEqual(expected.didCallDissmissView, actual.didCallDissmissView)
  66.175 +        XCTAssertEqual(expected.didCallToUpdateTrustedWords, actual.didCallToUpdateTrustedWords)
  66.176 +
  66.177 +        //values
  66.178 +        XCTAssertEqual(expected.fullWordsVersion, actual.fullWordsVersion)
  66.179 +        XCTAssertEqual(expected.languagesToShow, actual.languagesToShow)
  66.180 +        XCTAssertEqual(expected.handShakeWords, actual.handShakeWords)
  66.181 +        XCTAssertEqual(expected.pressedAction, actual.pressedAction)
  66.182 +
  66.183 +        //In case some if missing or added but not checked
  66.184 +        XCTAssertEqual(expected, actual)
  66.185 +    }
  66.186 +}
  66.187 +
  66.188 +
  66.189 +// MARK: - Helper Structs
  66.190 +extension KeySyncHandshakeViewModelTest {
  66.191 +    struct State: Equatable {
  66.192 +        var didCallShowPicker: Bool
  66.193 +        var didCallClosePicker: Bool
  66.194 +        var didCallDissmissView: Bool
  66.195 +        var didCallToUpdateTrustedWords: Bool
  66.196 +
  66.197 +        var fullWordsVersion: Bool?
  66.198 +        var languagesToShow: [String]?
  66.199 +        var handShakeWords: String?
  66.200 +        var pressedAction: KeySyncHandshakeViewController.Action?
  66.201 +
  66.202 +        // Default value are default initial state
  66.203 +        init(didCallShowPicker: Bool = false,
  66.204 +             didCallClosePicker: Bool = false,
  66.205 +             didCallDissmissView: Bool = false,
  66.206 +             didCallToUpdateTrustedWords: Bool = false,
  66.207 +             fullWordsVersion: Bool = false,
  66.208 +             languagesToShow: [String] = [],
  66.209 +             handShakeWords: String = "",
  66.210 +             pressedAction: KeySyncHandshakeViewController.Action? = nil) {
  66.211 +
  66.212 +            self.didCallShowPicker = didCallShowPicker
  66.213 +            self.didCallClosePicker = didCallClosePicker
  66.214 +            self.didCallDissmissView = didCallDissmissView
  66.215 +            self.didCallToUpdateTrustedWords = didCallToUpdateTrustedWords
  66.216 +
  66.217 +            self.fullWordsVersion = fullWordsVersion
  66.218 +            self.languagesToShow = languagesToShow
  66.219 +            self.handShakeWords = handShakeWords
  66.220 +            self.pressedAction = pressedAction
  66.221 +        }
  66.222 +    }
  66.223 +}
    67.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    67.2 +++ b/pEpForiOSTests/UI/KeySyncHandshake/PEPSessionMoc.swift	Mon Aug 19 09:32:27 2019 +0200
    67.3 @@ -0,0 +1,146 @@
    67.4 +//
    67.5 +//  PEPSessionMoc.swift
    67.6 +//  pEpForiOSTests
    67.7 +//
    67.8 +//  Created by Alejandro Gelos on 09/07/2019.
    67.9 +//  Copyright © 2019 p≡p Security S.A. All rights reserved.
   67.10 +//
   67.11 +
   67.12 +import Foundation
   67.13 +import PEPObjCAdapterFramework
   67.14 +
   67.15 +class PEPSessionMoc: NSObject, PEPSessionProtocol  {
   67.16 +    func decryptMessageDict(_ messageDict: NSMutableDictionary, flags: UnsafeMutablePointer<PEPDecryptFlags>?, rating: UnsafeMutablePointer<PEPRating>?, extraKeys: AutoreleasingUnsafeMutablePointer<NSArray?>?, status: UnsafeMutablePointer<PEPStatus>?) throws -> [String : Any] {
   67.17 +        return [:]
   67.18 +    }
   67.19 +
   67.20 +    func decryptMessage(_ message: PEPMessage, flags: UnsafeMutablePointer<PEPDecryptFlags>?, rating: UnsafeMutablePointer<PEPRating>?, extraKeys: AutoreleasingUnsafeMutablePointer<NSArray?>?, status: UnsafeMutablePointer<PEPStatus>?) throws -> PEPMessage {
   67.21 +        return PEPMessage()
   67.22 +    }
   67.23 +
   67.24 +    func reEvaluateMessageDict(_ messageDict: [String : Any], xKeyList: [String]?, rating: UnsafeMutablePointer<PEPRating>, status: UnsafeMutablePointer<PEPStatus>?) throws {
   67.25 +    }
   67.26 +
   67.27 +    func reEvaluateMessage(_ message: PEPMessage, xKeyList: [String]?, rating: UnsafeMutablePointer<PEPRating>, status: UnsafeMutablePointer<PEPStatus>?) throws {
   67.28 +    }
   67.29 +
   67.30 +    func encryptMessageDict(_ messageDict: [String : Any], extraKeys: [String]?, encFormat: PEPEncFormat, status: UnsafeMutablePointer<PEPStatus>?) throws -> [String : Any] {
   67.31 +        return [:]
   67.32 +    }
   67.33 +
   67.34 +    func encryptMessage(_ message: PEPMessage, extraKeys: [String]?, encFormat: PEPEncFormat, status: UnsafeMutablePointer<PEPStatus>?) throws -> PEPMessage {
   67.35 +        return PEPMessage()
   67.36 +    }
   67.37 +
   67.38 +    func encryptMessage(_ message: PEPMessage, extraKeys: [String]?, status: UnsafeMutablePointer<PEPStatus>?) throws -> PEPMessage {
   67.39 +        return PEPMessage()
   67.40 +    }
   67.41 +
   67.42 +    func encryptMessageDict(_ messageDict: [String : Any], forSelf ownIdentity: PEPIdentity, extraKeys: [String]?, status: UnsafeMutablePointer<PEPStatus>?) throws -> [String : Any] {
   67.43 +        return [:]
   67.44 +    }
   67.45 +
   67.46 +    func encryptMessage(_ message: PEPMessage, forSelf ownIdentity: PEPIdentity, extraKeys: [String]?, status: UnsafeMutablePointer<PEPStatus>?) throws -> PEPMessage {
   67.47 +        return PEPMessage()
   67.48 +    }
   67.49 +
   67.50 +    func encryptMessageDict(_ messageDict: [String : Any], toFpr: String, encFormat: PEPEncFormat, flags: PEPDecryptFlags, status: UnsafeMutablePointer<PEPStatus>?) throws -> [String : Any] {
   67.51 +        return [:]
   67.52 +    }
   67.53 +
   67.54 +    func encryptMessage(_ message: PEPMessage, toFpr: String, encFormat: PEPEncFormat, flags: PEPDecryptFlags, status: UnsafeMutablePointer<PEPStatus>?) throws -> PEPMessage {
   67.55 +        return PEPMessage()
   67.56 +    }
   67.57 +
   67.58 +    func outgoingRating(for theMessage: PEPMessage) throws -> NSNumber {
   67.59 +        return 0
   67.60 +    }
   67.61 +
   67.62 +    func outgoingRatingPreview(for theMessage: PEPMessage) throws -> NSNumber {
   67.63 +        return 0
   67.64 +    }
   67.65 +
   67.66 +    func rating(for identity: PEPIdentity) throws -> NSNumber {
   67.67 +        return 0
   67.68 +    }
   67.69 +
   67.70 +    func trustwords(forFingerprint fingerprint: String, languageID: String, shortened: Bool) throws -> [Any] {
   67.71 +        return []
   67.72 +    }
   67.73 +
   67.74 +    func mySelf(_ identity: PEPIdentity) throws {
   67.75 +    }
   67.76 +
   67.77 +    func update(_ identity: PEPIdentity) throws {
   67.78 +    }
   67.79 +
   67.80 +    func trustPersonalKey(_ identity: PEPIdentity) throws {
   67.81 +    }
   67.82 +
   67.83 +    func keyMistrusted(_ identity: PEPIdentity) throws {
   67.84 +    }
   67.85 +
   67.86 +    func keyResetTrust(_ identity: PEPIdentity) throws {
   67.87 +    }
   67.88 +
   67.89 +    func importKey(_ keydata: String) throws -> [PEPIdentity] {
   67.90 +        return []
   67.91 +    }
   67.92 +
   67.93 +    func logTitle(_ title: String, entity: String, description: String?, comment: String?) throws {
   67.94 +    }
   67.95 +
   67.96 +    func getLog() throws -> String {
   67.97 +        return String()
   67.98 +    }
   67.99 +
  67.100 +    func getTrustwordsIdentity1(_ identity1: PEPIdentity, identity2: PEPIdentity, language: String?, full: Bool) throws -> String {
  67.101 +        return String()
  67.102 +    }
  67.103 +
  67.104 +    func getTrustwordsFpr1(_ fpr1: String, fpr2: String, language: String?, full: Bool) throws -> String {
  67.105 +        return String()
  67.106 +    }
  67.107 +
  67.108 +    func languageList() throws -> [PEPLanguage] {
  67.109 +        return [PEPLanguage(), PEPLanguage()]
  67.110 +    }
  67.111 +
  67.112 +    func rating(from string: String) -> PEPRating {
  67.113 +        return .fullyAnonymous
  67.114 +    }
  67.115 +
  67.116 +    func string(from rating: PEPRating) -> String {
  67.117 +        return String()
  67.118 +    }
  67.119 +
  67.120 +    func isPEPUser(_ identity: PEPIdentity) throws -> NSNumber {
  67.121 +        return 0
  67.122 +    }
  67.123 +
  67.124 +    func setOwnKey(_ identity: PEPIdentity, fingerprint: String) throws {
  67.125 +    }
  67.126 +
  67.127 +    func configurePassiveModeEnabled(_ enabled: Bool) {
  67.128 +    }
  67.129 +
  67.130 +    func setFlags(_ flags: PEPIdentityFlags, for identity: PEPIdentity) throws {
  67.131 +    }
  67.132 +
  67.133 +    func deliver(_ result: PEPSyncHandshakeResult, identitiesSharing: [PEPIdentity]?) throws {
  67.134 +    }
  67.135 +
  67.136 +    func trustOwnKeyIdentity(_ identity: PEPIdentity) throws {
  67.137 +    }
  67.138 +
  67.139 +    func color(from rating: PEPRating) -> PEPColor {
  67.140 +        return .green
  67.141 +    }
  67.142 +
  67.143 +    func keyReset(_ identity: PEPIdentity, fingerprint: String?) throws {
  67.144 +    }
  67.145 +
  67.146 +    func leaveDeviceGroupError() throws {
  67.147 +    }
  67.148 +
  67.149 +}
    68.1 --- a/pEpForiOSTests/UI/Util/AccountPickerViewModel/AccountPickerViewModelTest.swift	Tue Jul 30 17:36:20 2019 +0200
    68.2 +++ b/pEpForiOSTests/UI/Util/AccountPickerViewModel/AccountPickerViewModelTest.swift	Mon Aug 19 09:32:27 2019 +0200
    68.3 @@ -65,14 +65,6 @@
    68.4                              mustEqualSelected: true)
    68.5      }
    68.6  
    68.7 -    func testHandleUserSelected_oneAccount_shouldFail() {
    68.8 -        let firstRowIdx = 0
    68.9 -        let accountNotSaved = SecretTestData().createWorkingAccount(number: 1)
   68.10 -        assertUserSelection(selectIdx: firstRowIdx,
   68.11 -                            accountToCompare: accountNotSaved,
   68.12 -                            mustEqualSelected: false)
   68.13 -    }
   68.14 -
   68.15      func testHandleUserSelected_twoAccounts_correct() {
   68.16          let secondRowIdx = 1
   68.17          let _ = createAndSaveSecondAccount()
   68.18 @@ -84,7 +76,6 @@
   68.19  
   68.20      func testHandleUserSelected_twoAccounts_shouldFail() {
   68.21          let firstAccountIdx = 0
   68.22 -        let sndAccountIdx = 1
   68.23          let _ = createAndSaveSecondAccount()
   68.24  
   68.25          assertUserSelection(selectIdx: firstAccountIdx,
    69.1 --- a/pEpForiOSUITests/NewAccountSetupUITest.swift	Tue Jul 30 17:36:20 2019 +0200
    69.2 +++ b/pEpForiOSUITests/NewAccountSetupUITest.swift	Mon Aug 19 09:32:27 2019 +0200
    69.3 @@ -88,7 +88,6 @@
    69.4          app().launch()
    69.5  
    69.6          dismissInitialSystemAlerts()
    69.7 -
    69.8          let account1 = secretTestData().workingAccount1
    69.9          newAccountSetup(account: account1)
   69.10  
    70.1 --- a/subModules/pEpIOSToolbox/pEpIOSToolbox.xcodeproj/project.pbxproj	Tue Jul 30 17:36:20 2019 +0200
    70.2 +++ b/subModules/pEpIOSToolbox/pEpIOSToolbox.xcodeproj/project.pbxproj	Mon Aug 19 09:32:27 2019 +0200
    70.3 @@ -10,6 +10,7 @@
    70.4  		154944CF229A97AA00CC6431 /* SelfReferencingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 154944CE229A97AA00CC6431 /* SelfReferencingOperation.swift */; };
    70.5  		370DB3CF22804A3600BC929A /* NSSecureCoding+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370DB3CE22804A3600BC929A /* NSSecureCoding+Extension.swift */; };
    70.6  		378B76552240F93A00D6662C /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378B76542240F93A00D6662C /* TestUtils.swift */; };
    70.7 +		37903EE422FD667700E0AB39 /* String+pEp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37903EE322FD667700E0AB39 /* String+pEp.swift */; };
    70.8  		37904562223FA486006DAB3B /* NetworkReachabilityProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3790455E223FA486006DAB3B /* NetworkReachabilityProtocol.swift */; };
    70.9  		37904563223FA486006DAB3B /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3790455F223FA486006DAB3B /* Reachability.swift */; };
   70.10  		37904564223FA486006DAB3B /* NetworkReachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37904560223FA486006DAB3B /* NetworkReachability.swift */; };
   70.11 @@ -21,6 +22,7 @@
   70.12  		B70A3A5722005BE300EDCE61 /* NSRegularExpression+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B70A3A5622005BE300EDCE61 /* NSRegularExpression+Extension.swift */; };
   70.13  		B70A3A7322006B9F00EDCE61 /* SystemUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = B70A3A7222006B9E00EDCE61 /* SystemUtils.swift */; };
   70.14  		B70A3A77220091D400EDCE61 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = B70A3A75220091D400EDCE61 /* Logger.swift */; };
   70.15 +		B7371AA1230175B7007B37A3 /* StringTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7371AA0230175B7007B37A3 /* StringTest.swift */; };
   70.16  		B7465DBE22119D3A008A1708 /* Thread+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7465DBD22119D39008A1708 /* Thread+Extension.swift */; };
   70.17  		B7465DC622119EB1008A1708 /* Array+SortingAndSearching.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7465DC522119EB1008A1708 /* Array+SortingAndSearching.swift */; };
   70.18  		B7465DCC2211BEEA008A1708 /* Tuple.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7465DCB2211BEE9008A1708 /* Tuple.swift */; };
   70.19 @@ -59,6 +61,7 @@
   70.20  		154944CE229A97AA00CC6431 /* SelfReferencingOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SelfReferencingOperation.swift; path = ../../../../Submodules/pEpIOSToolbox/pEpIOSToolbox/Other/SelfReferencingOperation.swift; sourceTree = "<group>"; };
   70.21  		370DB3CE22804A3600BC929A /* NSSecureCoding+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSSecureCoding+Extension.swift"; sourceTree = "<group>"; };
   70.22  		378B76542240F93A00D6662C /* TestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TestUtils.swift; path = ../../../../Submodules/pEpIOSToolbox/pEpIOSToolboxTests/TestUtils/TestUtils.swift; sourceTree = "<group>"; };
   70.23 +		37903EE322FD667700E0AB39 /* String+pEp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "String+pEp.swift"; path = "../../../../Submodules/pEpIOSToolbox/pEpIOSToolbox/Foundation/String+pEp.swift"; sourceTree = "<group>"; };
   70.24  		3790455E223FA486006DAB3B /* NetworkReachabilityProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkReachabilityProtocol.swift; sourceTree = "<group>"; };
   70.25  		3790455F223FA486006DAB3B /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = "<group>"; };
   70.26  		37904560223FA486006DAB3B /* NetworkReachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkReachability.swift; sourceTree = "<group>"; };
   70.27 @@ -70,6 +73,7 @@
   70.28  		B70A3A5622005BE300EDCE61 /* NSRegularExpression+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSRegularExpression+Extension.swift"; sourceTree = "<group>"; };
   70.29  		B70A3A7222006B9E00EDCE61 /* SystemUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SystemUtils.swift; sourceTree = "<group>"; };
   70.30  		B70A3A75220091D400EDCE61 /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
   70.31 +		B7371AA0230175B7007B37A3 /* StringTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = StringTest.swift; path = ../../../../Submodules/pEpIOSToolbox/pEpIOSToolboxTests/Foundation/StringTest.swift; sourceTree = "<group>"; };
   70.32  		B7465DBD22119D39008A1708 /* Thread+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Thread+Extension.swift"; sourceTree = "<group>"; };
   70.33  		B7465DC522119EB1008A1708 /* Array+SortingAndSearching.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+SortingAndSearching.swift"; sourceTree = "<group>"; };
   70.34  		B7465DCB2211BEE9008A1708 /* Tuple.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tuple.swift; sourceTree = "<group>"; };
   70.35 @@ -119,6 +123,7 @@
   70.36  		15175ED8221E9F3800C91DB8 /* Foundation */ = {
   70.37  			isa = PBXGroup;
   70.38  			children = (
   70.39 +				B7371AA0230175B7007B37A3 /* StringTest.swift */,
   70.40  			);
   70.41  			path = Foundation;
   70.42  			sourceTree = "<group>";
   70.43 @@ -191,6 +196,7 @@
   70.44  				B7A46C5D220DBAF00027CCB5 /* OperationQueue+Extension.swift */,
   70.45  				B7465DBD22119D39008A1708 /* Thread+Extension.swift */,
   70.46  				B7465DC522119EB1008A1708 /* Array+SortingAndSearching.swift */,
   70.47 +				37903EE322FD667700E0AB39 /* String+pEp.swift */,
   70.48  			);
   70.49  			path = Foundation;
   70.50  			sourceTree = "<group>";
   70.51 @@ -415,6 +421,7 @@
   70.52  				B7911EC221F8694100D7F66F /* UIColor+Extension.swift in Sources */,
   70.53  				B753907E2212D6B500B1FCF9 /* CGSize+Extension.swift in Sources */,
   70.54  				B7DB7F522213120B003968DA /* SortedSet.swift in Sources */,
   70.55 +				37903EE422FD667700E0AB39 /* String+pEp.swift in Sources */,
   70.56  				B7911EC621F88AF800D7F66F /* UIImage+Extension.swift in Sources */,
   70.57  				B70A3A77220091D400EDCE61 /* Logger.swift in Sources */,
   70.58  				B7A46C56220DA5EB0027CCB5 /* Substring+Email.swift in Sources */,
   70.59 @@ -439,6 +446,7 @@
   70.60  				B7DB7F8422144E4E003968DA /* SortedSetTest.swift in Sources */,
   70.61  				37904569223FA52B006DAB3B /* NetworkReachibilityMock.swift in Sources */,
   70.62  				378B76552240F93A00D6662C /* TestUtils.swift in Sources */,
   70.63 +				B7371AA1230175B7007B37A3 /* StringTest.swift in Sources */,
   70.64  			);
   70.65  			runOnlyForDeploymentPostprocessing = 0;
   70.66  		};
    71.1 --- a/subModules/pEpIOSToolbox/pEpIOSToolbox/Foundation/String+Extensions.swift	Tue Jul 30 17:36:20 2019 +0200
    71.2 +++ b/subModules/pEpIOSToolbox/pEpIOSToolbox/Foundation/String+Extensions.swift	Mon Aug 19 09:32:27 2019 +0200
    71.3 @@ -315,6 +315,20 @@
    71.4              return String(dropLast(theLength - maxCount))
    71.5          }
    71.6      }
    71.7 +
    71.8 +    /// it's true when the string only contains digits
    71.9 +    public var isDigits: Bool {
   71.10 +        let digits = CharacterSet.decimalDigits
   71.11 +        return unicodeScalars.allSatisfy { digits.contains($0) }
   71.12 +    }
   71.13 +
   71.14 +    /// it's true when the string is only a backspace
   71.15 +    public var isBackspace: Bool {
   71.16 +        var backspace = CharacterSet()
   71.17 +        backspace.insert(Unicode.Scalar("\u{0008}"))
   71.18 +        return unicodeScalars.allSatisfy { backspace.contains($0) }
   71.19 +    }
   71.20 +
   71.21  }
   71.22  
   71.23  extension NSAttributedString {
    72.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    72.2 +++ b/subModules/pEpIOSToolbox/pEpIOSToolbox/Foundation/String+pEp.swift	Mon Aug 19 09:32:27 2019 +0200
    72.3 @@ -0,0 +1,20 @@
    72.4 +//
    72.5 +//  String+pEp.swift
    72.6 +//  pEpIOSToolbox
    72.7 +//
    72.8 +//  Created by Alejandro Gelos on 09/08/2019.
    72.9 +//  Copyright © 2019 pEp Security SA. All rights reserved.
   72.10 +//
   72.11 +
   72.12 +import UIKit
   72.13 +
   72.14 +extension String {
   72.15 +    public func paintPEPToPEPColour() -> NSAttributedString {
   72.16 +        let range = (self as NSString).range(of: "p≡p")
   72.17 +        let paintedText = NSMutableAttributedString(string: self)
   72.18 +
   72.19 +        paintedText.addAttribute(
   72.20 +            NSAttributedString.Key.foregroundColor, value: UIColor.pEpGreen, range: range)
   72.21 +        return paintedText
   72.22 +    }
   72.23 +}
    73.1 --- a/subModules/pEpIOSToolbox/pEpIOSToolbox/UIKit/UIColor+Extension.swift	Tue Jul 30 17:36:20 2019 +0200
    73.2 +++ b/subModules/pEpIOSToolbox/pEpIOSToolbox/UIKit/UIColor+Extension.swift	Mon Aug 19 09:32:27 2019 +0200
    73.3 @@ -13,6 +13,10 @@
    73.4      public static let pEpDarkGreenHex = "#1AAA50"
    73.5      public static let pEpRedHex = "#FF3B30"
    73.6      public static let pEpGreyHex = "#8e8e93"
    73.7 +    public static let pEpGreyLinesHex = "#9F9F9F"
    73.8 +    public static let pEpGreyButtonLinesHex = "#CDCED2"
    73.9 +    public static let pEpGreyTextHex = "#8A8A8F"
   73.10 +    public static let pEpGreyBackgroundHex = "#EDEEED"
   73.11      public static let pEpYellowHex = "#FFCC00"
   73.12      public static let pEpLightBackgroundHex = "#F2F2F2"
   73.13      public static let pEpNavigationBarColor = "#f7f7f7"
   73.14 @@ -23,6 +27,10 @@
   73.15      public static var pEpGray = UIColor(hexString: pEpGreyHex)
   73.16      public static var pEpYellow = UIColor(hexString: pEpYellowHex)
   73.17      public static var pEpNavigation = UIColor(hexString: pEpNavigationBarColor)
   73.18 +    public static var pEpGreyLines = UIColor(hexString: pEpGreyLinesHex)
   73.19 +    public static var pEpGreyButtonLines = UIColor(hexString: pEpGreyButtonLinesHex)
   73.20 +    public static var pEpGreyText = UIColor(hexString: pEpGreyTextHex)
   73.21 +    public static var pEpGreyBackground = UIColor(hexString: pEpGreyBackgroundHex)
   73.22  
   73.23      public static let AppleRed =
   73.24                  UIColor(red: 255/255.0, green: 59/255, blue: 48/255.0, alpha: 1.0)