Skip to content

Commit

Permalink
issue #910 Crash when opening Contacts screen (#971)
Browse files Browse the repository at this point in the history
Co-authored-by: Tom <[email protected]>
  • Loading branch information
ivan-ushakov and flowcrypt-machine-user authored Nov 10, 2021
1 parent 7be54dc commit 5c62780
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 24 deletions.
16 changes: 10 additions & 6 deletions FlowCrypt.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
2C124DB42728809100A2EFA6 /* ApiCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C124DB32728809100A2EFA6 /* ApiCall.swift */; };
2C2A3B4B2719EE6100B7F27B /* KeyServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2A3B4A2719EE6100B7F27B /* KeyServiceTests.swift */; };
2C2A3B4D2719EF7300B7F27B /* PassPhraseServiceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2A3B4C2719EF7300B7F27B /* PassPhraseServiceMock.swift */; };
2C595764273AC47400C7C055 /* RealmExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C595763273AC47400C7C055 /* RealmExtension.swift */; };
2C60AB0C272564D40040D7F2 /* InvalidStorageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C60AB0B272564D40040D7F2 /* InvalidStorageViewController.swift */; };
2CC12C3F273571B80021DDDF /* AttachmentManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC12C3E273571B80021DDDF /* AttachmentManager.swift */; };
32DCA00224982EDA88D69C6E /* AppErr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DCA4B11D4531B3B04D01D1 /* AppErr.swift */; };
Expand Down Expand Up @@ -446,6 +447,7 @@
2C124DB32728809100A2EFA6 /* ApiCall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiCall.swift; sourceTree = "<group>"; };
2C2A3B4A2719EE6100B7F27B /* KeyServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyServiceTests.swift; sourceTree = "<group>"; };
2C2A3B4C2719EF7300B7F27B /* PassPhraseServiceMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassPhraseServiceMock.swift; sourceTree = "<group>"; };
2C595763273AC47400C7C055 /* RealmExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealmExtension.swift; sourceTree = "<group>"; };
2C60AB0B272564D40040D7F2 /* InvalidStorageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvalidStorageViewController.swift; sourceTree = "<group>"; };
2CC12C3E273571B80021DDDF /* AttachmentManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentManager.swift; sourceTree = "<group>"; };
32DCA058652FD4616FB04FB6 /* SequenceExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SequenceExtensions.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -991,15 +993,16 @@
32DCAEF9FEEF84D4F0D4A516 /* Extensions */ = {
isa = PBXGroup;
children = (
32DCA7E0AFE19FACB0F233ED /* URLSessionExtension.swift */,
9FEED1B7230C08D700700F8E /* UIViewControllerExtensions.swift */,
518389C72726D7DD00131B2C /* UIViewController+Spinner.swift */,
9F31AB9D232BF2A600CF87EA /* UIColorExtension.swift */,
D2D27B78248A8694007346FA /* BigIntExtension.swift */,
9F2F217226B3269D0044E144 /* CombineExtensions.swift */,
518389C92726D8F700131B2C /* UIApplicationExtension.swift */,
51B0C7702729861C00124663 /* String+Extension.swift */,
51E4F0B427348E310017DABB /* Error+Extension.swift */,
2C595763273AC47400C7C055 /* RealmExtension.swift */,
51B0C7702729861C00124663 /* String+Extension.swift */,
518389C92726D8F700131B2C /* UIApplicationExtension.swift */,
9F31AB9D232BF2A600CF87EA /* UIColorExtension.swift */,
518389C72726D7DD00131B2C /* UIViewController+Spinner.swift */,
9FEED1B7230C08D700700F8E /* UIViewControllerExtensions.swift */,
32DCA7E0AFE19FACB0F233ED /* URLSessionExtension.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -2568,6 +2571,7 @@
5ADEDCBC23A4329000EC495E /* PublicKeyDetailViewController.swift in Sources */,
21489B80267CC39E00BDE4AC /* ClientConfigurationService.swift in Sources */,
D28655932423B4EE0066F52E /* MyMenuViewDecorator.swift in Sources */,
2C595764273AC47400C7C055 /* RealmExtension.swift in Sources */,
04B4728D1ECE29D200B8266F /* KeyInfo.swift in Sources */,
9F3EF32F23B172D300FA0CEF /* SearchViewController.swift in Sources */,
F191F621272511790053833E /* BlurViewController.swift in Sources */,
Expand Down
51 changes: 51 additions & 0 deletions FlowCrypt/Extensions/RealmExtension.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// RealmExtension.swift
// FlowCrypt
//
// Created by  Ivan Ushakov on 09.11.2021
// Copyright © 2017-present FlowCrypt a. s. All rights reserved.
//

import Realm
import RealmSwift

protocol RealmListDetachable {
func detached() -> Self
}

extension List: RealmListDetachable where Element: Object {
func detached() -> List<Element> {
let detached = self.detached
let result = List<Element>()
result.append(objectsIn: detached)
return result
}
}

extension Object {
// TODO Temporary solution from StackOverflow for https://github.com/FlowCrypt/flowcrypt-ios/issues/877
func detached() -> Self {
let detached = type(of: self).init()
for property in objectSchema.properties {
guard
property != objectSchema.primaryKeyProperty,
let value = value(forKey: property.name)
else { continue }

if let detachable = value as? Object {
detached.setValue(detachable.detached(), forKey: property.name)
} else if let list = value as? RealmListDetachable {
detached.setValue(list.detached(), forKey: property.name)
} else {
detached.setValue(value, forKey: property.name)
}
}
return detached
}
}

extension Sequence where Iterator.Element: Object {
var detached: [Element] {
return self.map({ $0.detached() })
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ final class KeyService: KeyServiceType {
// get keys associated with this account, freeze them to pass across threads
let keysInfo = storage.keysInfo().filter { $0.account == email }.map { object -> KeyInfo in
guard object.realm != nil else { return object }
return object.freeze()
return object.detached()
}
guard let foundKey = try await findKeyByUserEmail(keysInfo: keysInfo, email: email) else {
return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ extension LocalContactsProvider: LocalContactsProviderType {

func searchRecipient(with email: String) async throws -> RecipientWithSortedPubKeys? {
guard let recipientObject = find(with: email) else { return nil }
// TODO temporary fix for Realm thread problem
return try await parseRecipient(from: recipientObject.freeze())
return try await parseRecipient(from: recipientObject.detached())
}

func searchEmails(query: String) -> [String] {
Expand All @@ -87,10 +86,10 @@ extension LocalContactsProvider: LocalContactsProviderType {
}

func getAllRecipients() async throws -> [RecipientWithSortedPubKeys] {
let objects = Array(localContactsCache.realm.objects(RecipientObject.self))
let objects = localContactsCache.realm.objects(RecipientObject.self).detached
var recipients: [RecipientWithSortedPubKeys] = []
for object in objects {
recipients.append(try await parseRecipient(from: object.freeze()))
recipients.append(try await parseRecipient(from: object))
}
return recipients.sorted(by: { $0.email > $1.email })
}
Expand Down
4 changes: 4 additions & 0 deletions appium/tests/data/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export const CommonData = {
email: '[email protected]',
name: 'Dima'
},
secondContact: {
email: '[email protected]',
name: 'Demo'
},
bundleId: {
id: 'com.flowcrypt.as.ios.debug',
},
Expand Down
12 changes: 6 additions & 6 deletions appium/tests/screenobjects/contacts.screen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ class ContactsScreen extends BaseScreen {
return $(SELECTORS.EMPTY_CONTACTS_LIST);
}

contactEmail(email) {
return $(`~${email}`)
contactName(name) {
return $(`~${name}`)
}

checkContactScreen() {
Expand All @@ -41,12 +41,12 @@ class ContactsScreen extends BaseScreen {
ElementHelper.waitAndClick(this.backButton);
}

checkContact(email) {
this.contactEmail(email).waitForDisplayed();
checkContact(name) {
this.contactName(name).waitForDisplayed();
}

clickOnContact(email) {
ElementHelper.waitAndClick(this.contactEmail(email));
clickOnContact(name) {
ElementHelper.waitAndClick(this.contactName(name));
}
}

Expand Down
32 changes: 25 additions & 7 deletions appium/tests/specs/composeEmail/SelectRecipientByName.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,18 @@ describe('COMPOSE EMAIL: ', () => {

it('user is able to select recipient from contact list using contact name', () => {

const contactEmail = CommonData.contact.email;
const contactName = CommonData.contact.name;
const firstContactEmail = CommonData.contact.email;
const firstContactName = CommonData.contact.name;
const firstContactItemName = 'Dmitry at FlowCrypt';

const secondContactEmail = CommonData.secondContact.email;
const secondContactName = CommonData.secondContact.name;
const secondContactItemName = 'Demo key 2';

SplashScreen.login();
CreateKeyScreen.setPassPhrase();

// Go to Contacts screen
MenuBarScreen.clickMenuIcon();
MenuBarScreen.checkUserEmail();

Expand All @@ -35,12 +41,21 @@ describe('COMPOSE EMAIL: ', () => {
MenuBarScreen.clickMenuIcon();
MenuBarScreen.clickInboxButton();

// Add first contact
InboxScreen.clickCreateEmail();

NewMessageScreen.setAddRecipientByName(contactName, contactEmail);
NewMessageScreen.checkAddedRecipient(contactEmail);
NewMessageScreen.setAddRecipientByName(firstContactName, firstContactEmail);
NewMessageScreen.checkAddedRecipient(firstContactEmail);
NewMessageScreen.clickBackButton();

// Add second contact
InboxScreen.clickCreateEmail();

NewMessageScreen.setAddRecipientByName(secondContactName, secondContactEmail);
NewMessageScreen.checkAddedRecipient(secondContactEmail);
NewMessageScreen.clickBackButton();

// Go to Contacts screen
MenuBarScreen.clickMenuIcon();
MenuBarScreen.checkUserEmail();

Expand All @@ -49,10 +64,13 @@ describe('COMPOSE EMAIL: ', () => {
SettingsScreen.clickOnSettingItem('Contacts');

ContactScreen.checkContactScreen();
ContactScreen.checkContact(contactEmail);
ContactScreen.clickOnContact(contactEmail);
ContactScreen.checkContact(firstContactItemName);
ContactScreen.checkContact(secondContactItemName);

// Go to Contact screen
ContactScreen.clickOnContact(firstContactItemName);

ContactPublicKeyScreen.checkPgpUserId(contactEmail);
ContactPublicKeyScreen.checkPgpUserId(firstContactEmail);
ContactPublicKeyScreen.checkPublicKeyDetailsNotEmpty();
ContactPublicKeyScreen.clickOnFingerPrint();
ContactPublicKeyScreen.checkPublicKeyNotEmpty();
Expand Down

0 comments on commit 5c62780

Please sign in to comment.