Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 29 additions & 13 deletions FlowCrypt.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
2C141B2F274578C20038A3F8 /* Keypair.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C141B2E274578C20038A3F8 /* Keypair.swift */; };
2C2A3B4B2719EE6100B7F27B /* KeyServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2A3B4A2719EE6100B7F27B /* KeyServiceTests.swift */; };
2C2A3B4D2719EF7300B7F27B /* PassPhraseServiceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2A3B4C2719EF7300B7F27B /* PassPhraseServiceMock.swift */; };
2C2B058927A07F7B00A406F0 /* ObjcException.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C2B058827A07F7B00A406F0 /* ObjcException.m */; };
2C2D0B95275FDF6B0052771D /* Version6SchemaMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2D0B94275FDF6B0052771D /* Version6SchemaMigration.swift */; };
2C4E60F72757D91A00DE5770 /* EncryptedStorageMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C4E60F62757D91A00DE5770 /* EncryptedStorageMock.swift */; };
2C60AB0C272564D40040D7F2 /* InvalidStorageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C60AB0B272564D40040D7F2 /* InvalidStorageViewController.swift */; };
Expand Down Expand Up @@ -464,6 +465,9 @@
2C141B2E274578C20038A3F8 /* Keypair.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keypair.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>"; };
2C2B058627A07F7A00A406F0 /* FlowCrypt-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FlowCrypt-Bridging-Header.h"; sourceTree = "<group>"; };
2C2B058727A07F7B00A406F0 /* ObjcException.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ObjcException.h; sourceTree = "<group>"; };
2C2B058827A07F7B00A406F0 /* ObjcException.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ObjcException.m; sourceTree = "<group>"; };
2C2D0B94275FDF6B0052771D /* Version6SchemaMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Version6SchemaMigration.swift; sourceTree = "<group>"; };
2C4E60F62757D91A00DE5770 /* EncryptedStorageMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptedStorageMock.swift; sourceTree = "<group>"; };
2C60AB0B272564D40040D7F2 /* InvalidStorageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvalidStorageViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1004,6 +1008,15 @@
path = "Key Services";
sourceTree = "<group>";
};
2C2B058A27A0809400A406F0 /* ObjC */ = {
isa = PBXGroup;
children = (
2C2B058727A07F7B00A406F0 /* ObjcException.h */,
2C2B058827A07F7B00A406F0 /* ObjcException.m */,
);
path = ObjC;
sourceTree = "<group>";
};
2DC3601602226D74335E18F4 /* Pods */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1715,20 +1728,22 @@
C132B9B21EC2DBD800763715 /* FlowCrypt */ = {
isa = PBXGroup;
children = (
9F1C90ED26F236BE0046E7D7 /* FlowCryptEnterprise.entitlements */,
A3B7C31623E437370022D628 /* FlowCryptRelease.entitlements */,
A3B7C31523E436E10022D628 /* FlowCrypt.entitlements */,
32DCA376D595968F13A3347A /* Core */,
C132B9C51EC2DCAB00763715 /* Controllers */,
04B4728A1ECE29D200B8266F /* Models */,
9FD22A1D230FEEC8005067A6 /* Common UI */,
C132B9C61EC2DCC000763715 /* Functionality */,
A3DAD60C22E469E400F2C4CD /* Resources */,
9F8220D32633661C004B2009 /* App */,
C132B9B31EC2DBD800763715 /* AppDelegate.swift */,
C132B9BA1EC2DBD800763715 /* Assets.xcassets */,
949ED9412303E3B400530579 /* Colors.xcassets */,
9FD22A1D230FEEC8005067A6 /* Common UI */,
C132B9C51EC2DCAB00763715 /* Controllers */,
32DCA376D595968F13A3347A /* Core */,
2C2B058627A07F7A00A406F0 /* FlowCrypt-Bridging-Header.h */,
A3B7C31523E436E10022D628 /* FlowCrypt.entitlements */,
9F1C90ED26F236BE0046E7D7 /* FlowCryptEnterprise.entitlements */,
A3B7C31623E437370022D628 /* FlowCryptRelease.entitlements */,
C132B9C61EC2DCC000763715 /* Functionality */,
C132B9BF1EC2DBD800763715 /* Info.plist */,
04B4728A1ECE29D200B8266F /* Models */,
2C2B058A27A0809400A406F0 /* ObjC */,
A3DAD60C22E469E400F2C4CD /* Resources */,
9F38619B27A181CE00851419 /* .swiftformat */,
);
path = FlowCrypt;
Expand Down Expand Up @@ -2281,7 +2296,7 @@
};
C132B9AF1EC2DBD800763715 = {
CreatedOnToolsVersion = 8.3.2;
LastSwiftMigration = 1020;
LastSwiftMigration = 1320;
ProvisioningStyle = Automatic;
};
D204DB9D23FB35700083B9D6 = {
Expand Down Expand Up @@ -2606,6 +2621,7 @@
C192421F1EC48B6900C3D251 /* SetupBackupsViewController.swift in Sources */,
9F0C3C2623194E0A00299985 /* FolderViewModel.swift in Sources */,
F8678DCC2722143300BB1710 /* GmailService+draft.swift in Sources */,
2C2B058927A07F7B00A406F0 /* ObjcException.m in Sources */,
21CE25E62650070300ADFF4B /* WkdUrlConstructor.swift in Sources */,
9FC411212595EA12001180A8 /* MessageSearchProvider.swift in Sources */,
9F778E7E27162038001D4B21 /* MessageThread.swift in Sources */,
Expand Down Expand Up @@ -3062,7 +3078,7 @@
PRODUCT_NAME = FlowCrypt;
PROVISIONING_PROFILE_SPECIFIER = "";
STRIP_INSTALLED_PRODUCT = NO;
SWIFT_OBJC_BRIDGING_HEADER = "";
SWIFT_OBJC_BRIDGING_HEADER = "FlowCrypt/FlowCrypt-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_PRECOMPILE_BRIDGING_HEADER = NO;
SWIFT_VERSION = 5.0;
Expand Down Expand Up @@ -3447,7 +3463,7 @@
PRODUCT_NAME = FlowCrypt;
PROVISIONING_PROFILE_SPECIFIER = "";
STRIP_INSTALLED_PRODUCT = NO;
SWIFT_OBJC_BRIDGING_HEADER = "";
SWIFT_OBJC_BRIDGING_HEADER = "FlowCrypt/FlowCrypt-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_PRECOMPILE_BRIDGING_HEADER = NO;
SWIFT_VERSION = 5.0;
Expand Down Expand Up @@ -3506,7 +3522,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.flowcrypt.as.ios.consumer;
PRODUCT_NAME = FlowCrypt;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "";
SWIFT_OBJC_BRIDGING_HEADER = "FlowCrypt/FlowCrypt-Bridging-Header.h";
SWIFT_PRECOMPILE_BRIDGING_HEADER = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
Expand Down
16 changes: 14 additions & 2 deletions FlowCrypt/Controllers/Inbox/InboxViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -452,10 +452,22 @@ extension InboxViewController: MsgListViewController {
tableNode.reloadData()
} else {
state = .fetched(.byNumber(total: newTotalNumber))
tableNode.deleteRows(at: [IndexPath(row: index, section: 0)], with: .left)
do {
try ObjcException.catch {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please explain how is this fix related to the crash fix?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like there is an issue related to the index of the row that we try to update

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As @tomholub said, we want to avoid having crash here. Even if issue is still here, we should not crash.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have an issue to solve the root cause of the crash?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

absolutely. Let's solve it in this PR as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since all UI is async with Texture, I guess that using message index is unsafe. We should move to message ID, as this is the only way to know that message we want to delete is the same message.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would suggest to investigate root cause of the issue.

Also if there was only one message before moving to trash we need to update whole state of the controller.

self.tableNode.deleteRows(at: [IndexPath(row: index, section: 0)], with: .left)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Data source for table node should be updated with correct amount of inboxInput then reload should be performed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Data source is updated above:

inboxInput.remove(at: index)

We could reload table node with reloadData(), but crash means that we are in invalid state. We could move from index to some kind of message ID.

But I guess that this PR is more about handling Objc exceptions in Swift.

Copy link
Collaborator

@tomholub tomholub Jan 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While originally it was indeed primaryli about handling of ObjC exceptions, that was in part because we didn't know any better.

If the actual root cause of this exception can be fixed, that is even better and more appropriate. Both should be done. We didn't know that, so we just focused on handling, but Anton has experience with Texture specifically to notice that this is something we should be able to fix directly as well.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's something more specific than just updating data source, as this crash happens very rarely.
Implementing ObjC exception handling should help us to find root cause of this issue.

May be switching from index to message id (as @ivan-ushakov proposed) will fix this crash, but we can't be 100% sure as currently we're not able to reproduce this issue.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the goal is to collect more information about the crash, I suppose we should have it show a modal similar to other crash scenarios? Then some user may take a screenshot and send it to us for debugging.

As currently, it will just be logged then forgotten.

}
} catch {
showAlert(message: "Failed to remove message at \(index) in fetched state: \(error)")
}
}
default:
tableNode.deleteRows(at: [IndexPath(row: index, section: 0)], with: .left)
do {
try ObjcException.catch {
self.tableNode.deleteRows(at: [IndexPath(row: index, section: 0)], with: .left)
}
} catch {
showAlert(message: "Failed to remove message at \(index) in \(state): \(error)")
}
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions FlowCrypt/FlowCrypt-Bridging-Header.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//

#import "ObjcException.h"
19 changes: 19 additions & 0 deletions FlowCrypt/ObjC/ObjcException.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// ObjcException.h
// FlowCrypt
//
// Created by  Ivan Ushakov on 25.01.2022
// Copyright © 2017-present FlowCrypt a. s. All rights reserved.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface ObjcException : NSObject

+ (BOOL)catchException:(void(^)(void))tryBlock error:(__autoreleasing NSError **)error;

@end

NS_ASSUME_NONNULL_END
40 changes: 40 additions & 0 deletions FlowCrypt/ObjC/ObjcException.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// ObjcException.m
// FlowCrypt
//
// Created by  Ivan Ushakov on 25.01.2022
// Copyright © 2017-present FlowCrypt a. s. All rights reserved.
//

#import "ObjcException.h"

@implementation ObjcException

+ (BOOL)catchException:(void(^)(void))tryBlock error:(__autoreleasing NSError **)error {
@try
{
tryBlock();
return YES;
}
@catch (NSException *exception)
{
NSMutableDictionary *userInfo = [NSMutableDictionary new];
if (exception.userInfo != NULL)
{
userInfo = [[NSMutableDictionary alloc] initWithDictionary:exception.userInfo];
}

if (exception.reason != nil)
{
if (![userInfo.allKeys containsObject:NSLocalizedFailureReasonErrorKey])
{
[userInfo setObject:exception.reason forKey:NSLocalizedFailureReasonErrorKey];
}
}

*error = [[NSError alloc] initWithDomain:exception.name code:0 userInfo:userInfo];
return NO;
}
}

@end