From 748f3f4a9227ce70f14768faadc95e6145637446 Mon Sep 17 00:00:00 2001 From: Silbino Goncalves Matado Date: Thu, 8 Jun 2017 10:10:21 -0400 Subject: [PATCH] Smartlist: Add conversations persistence with Realm Add conversations persistence to save the history of messages using Realm and RxRealm libraries Refactor models to work with Realm : - Change properties declaration as dynamic vars - Change Arrays to List - Change Dictionaries to Objects - Add default values for non-optionals - Changed initializers for convenience initializers - Fixed Tests compilation using @testable import (classes linked twice exception throwed by Realm) - Bumped RxSwift version and fixed deprecated methods warning Change-Id: Ife98e48430740f80ffef9420d857f1ae6e4819d4 --- Ring/Cartfile | 2 +- Ring/Cartfile.resolved | 2 + Ring/Ring.xcodeproj/project.pbxproj | 45 +++---- Ring/Ring/Account/AccountConfigModel.swift | 14 +- .../Account/AccountCredentialsModel.swift | 28 ++-- Ring/Ring/Account/AccountModel.swift | 30 ++--- Ring/Ring/Account/AccountModelHelper.swift | 20 +-- Ring/Ring/AppDelegate.swift | 66 +--------- Ring/Ring/ContactModel.swift | 11 +- Ring/Ring/ContactViewModel.swift | 8 +- Ring/Ring/ConversationModel.swift | 14 +- Ring/Ring/ConversationViewController.swift | 7 +- Ring/Ring/ConversationViewModel.swift | 18 +-- Ring/Ring/ConversationsService.swift | 52 ++++---- Ring/Ring/DeviceModel.swift | 32 +++++ Ring/Ring/MessageModel.swift | 18 +-- Ring/Ring/MessagesService.swift | 122 ------------------ Ring/Ring/Ring.xcdatamodeld/.xccurrentversion | 5 +- Ring/Ring/RingPrefixHeader.pch | 7 + Ring/Ring/Services/AccountsService.swift | 24 ++-- Ring/Ring/SmartlistViewModel.swift | 14 +- Ring/RingTests/AccountModelHelperTests.swift | 4 +- Ring/RingTests/RingTests-Bridging-Header.h | 3 +- Ring/RingTests/RingTests.swift | 1 - 24 files changed, 212 insertions(+), 335 deletions(-) create mode 100644 Ring/Ring/DeviceModel.swift delete mode 100644 Ring/Ring/MessagesService.swift diff --git a/Ring/Cartfile b/Ring/Cartfile index 7848fe1..409b708 100644 --- a/Ring/Cartfile +++ b/Ring/Cartfile @@ -1,3 +1,3 @@ -github "ReactiveX/RxSwift" +github "RxSwiftCommunity/RxRealm" github "RxSwiftCommunity/RxDataSources" == 1.0.3 github "pkluz/PKHUD" diff --git a/Ring/Cartfile.resolved b/Ring/Cartfile.resolved index 318af5d..3e7f119 100644 --- a/Ring/Cartfile.resolved +++ b/Ring/Cartfile.resolved @@ -1,3 +1,5 @@ github "ReactiveX/RxSwift" "3.5.0" github "RxSwiftCommunity/RxDataSources" "1.0.3" +github "RxSwiftCommunity/RxRealm" "0.6.0" github "pkluz/PKHUD" "4.2.3" +github "realm/realm-cocoa" "v2.8.1" diff --git a/Ring/Ring.xcodeproj/project.pbxproj b/Ring/Ring.xcodeproj/project.pbxproj index 4259933..e951a32 100644 --- a/Ring/Ring.xcodeproj/project.pbxproj +++ b/Ring/Ring.xcodeproj/project.pbxproj @@ -39,7 +39,6 @@ 043866361D22D06500E06CE2 /* AccountTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043866351D22D06500E06CE2 /* AccountTableViewCell.swift */; }; 0438663B1D2313B700E06CE2 /* AccountDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0438663A1D2313B700E06CE2 /* AccountDetailsViewController.swift */; }; 043999F71D1C2D9D00E99CD9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043999F61D1C2D9D00E99CD9 /* AppDelegate.swift */; }; - 043999FA1D1C2D9D00E99CD9 /* Ring.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 043999F81D1C2D9D00E99CD9 /* Ring.xcdatamodeld */; }; 04399A031D1C2D9D00E99CD9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 04399A021D1C2D9D00E99CD9 /* Assets.xcassets */; }; 04399A111D1C2D9D00E99CD9 /* RingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04399A101D1C2D9D00E99CD9 /* RingTests.swift */; }; 04399A1C1D1C2D9D00E99CD9 /* RingUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04399A1B1D1C2D9D00E99CD9 /* RingUITests.swift */; }; @@ -100,20 +99,20 @@ 04399B151D1C341A00E99CD9 /* libyaml-cpp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 04399AE31D1C341A00E99CD9 /* libyaml-cpp.a */; }; 5516C29F1E71CEFF009D3D2D /* AccountModelHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5516C29E1E71CEFF009D3D2D /* AccountModelHelper.swift */; }; 5557FD4A1E81AE850043E394 /* AccountModelHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5557FD491E81AE850043E394 /* AccountModelHelperTests.swift */; }; - 5557FD4B1E81AECF0043E394 /* AccountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02B22DFA1DF755BB000358C9 /* AccountModel.swift */; }; - 5557FD4C1E81AF840043E394 /* AccountConfigModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02DD80C71E1EAD70009A3510 /* AccountConfigModel.swift */; }; 5557FD4D1E81AFF50043E394 /* ConfigKeyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02DD80CC1E1EB2E4009A3510 /* ConfigKeyModel.swift */; }; - 5557FD4E1E81B1F20043E394 /* AccountModelHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5516C29E1E71CEFF009D3D2D /* AccountModelHelper.swift */; }; - 5557FD4F1E81B2990043E394 /* AccountCredentialsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02DD80C91E1EAF1A009A3510 /* AccountCredentialsModel.swift */; }; 557086521E8ADB9D001A7CE4 /* SystemAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 557086511E8ADB9D001A7CE4 /* SystemAdapter.mm */; }; 562FB6CD1EFAD18A00C61A78 /* ConversationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 562FB6CC1EFAD18A00C61A78 /* ConversationViewController.swift */; }; 56308BA71EA00E5700660275 /* NameRegistrationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 56308BA61EA00E5700660275 /* NameRegistrationResponse.m */; }; 563AEC771EA664C0003A5641 /* RegistrationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 563AEC761EA664C0003A5641 /* RegistrationResponse.m */; }; + 564775831EE5CFC500A0C855 /* Realm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 564775811EE5CFC500A0C855 /* Realm.framework */; }; + 564775841EE5CFC500A0C855 /* RealmSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 564775821EE5CFC500A0C855 /* RealmSwift.framework */; }; 564C44591E8D7F8F000F92B1 /* LocalizedStringTableNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564C44581E8D7F8F000F92B1 /* LocalizedStringTableNames.swift */; }; 564C445B1E8EA44E000F92B1 /* Durations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564C445A1E8EA44E000F92B1 /* Durations.swift */; }; 564C44601E943C37000F92B1 /* NameRegistrationAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 564C445F1E943C37000F92B1 /* NameRegistrationAdapter.mm */; }; 564C44621E943DE6000F92B1 /* NameService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564C44611E943DE6000F92B1 /* NameService.swift */; }; 564C44641E943E1E000F92B1 /* NameRegistrationAdapterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564C44631E943E1E000F92B1 /* NameRegistrationAdapterDelegate.swift */; }; + 56559B0E1EE8777600BF20E1 /* RxRealm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 56559B0D1EE8777600BF20E1 /* RxRealm.framework */; }; + 56559B141EE89E7900BF20E1 /* DeviceModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56559B131EE89E7900BF20E1 /* DeviceModel.swift */; }; 5669A7FA1EA904AF003C7B93 /* SwitchCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5669A7F91EA904AF003C7B93 /* SwitchCell.xib */; }; 5669A7FC1EA904D2003C7B93 /* TextFieldCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5669A7FB1EA904D2003C7B93 /* TextFieldCell.xib */; }; 5669A7FE1EA904E4003C7B93 /* TextCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5669A7FD1EA904E4003C7B93 /* TextCell.xib */; }; @@ -207,7 +206,6 @@ 0438663A1D2313B700E06CE2 /* AccountDetailsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountDetailsViewController.swift; sourceTree = ""; }; 043999F31D1C2D9D00E99CD9 /* Ring.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ring.app; sourceTree = BUILT_PRODUCTS_DIR; }; 043999F61D1C2D9D00E99CD9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 043999F91D1C2D9D00E99CD9 /* Ring.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Ring.xcdatamodel; sourceTree = ""; }; 04399A001D1C2D9D00E99CD9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 04399A021D1C2D9D00E99CD9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 04399A051D1C2D9D00E99CD9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; @@ -286,6 +284,8 @@ 56308BA61EA00E5700660275 /* NameRegistrationResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NameRegistrationResponse.m; sourceTree = ""; }; 563AEC751EA664C0003A5641 /* RegistrationResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegistrationResponse.h; sourceTree = ""; }; 563AEC761EA664C0003A5641 /* RegistrationResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RegistrationResponse.m; sourceTree = ""; }; + 564775811EE5CFC500A0C855 /* Realm.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Realm.framework; path = Carthage/Build/iOS/Realm.framework; sourceTree = ""; }; + 564775821EE5CFC500A0C855 /* RealmSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RealmSwift.framework; path = Carthage/Build/iOS/RealmSwift.framework; sourceTree = ""; }; 564C44581E8D7F8F000F92B1 /* LocalizedStringTableNames.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizedStringTableNames.swift; sourceTree = ""; }; 564C445A1E8EA44E000F92B1 /* Durations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Durations.swift; sourceTree = ""; }; 564C445E1E943C37000F92B1 /* NameRegistrationAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NameRegistrationAdapter.h; sourceTree = ""; }; @@ -293,6 +293,8 @@ 564C44611E943DE6000F92B1 /* NameService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NameService.swift; sourceTree = ""; }; 564C44631E943E1E000F92B1 /* NameRegistrationAdapterDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NameRegistrationAdapterDelegate.swift; sourceTree = ""; }; 56559B161EEED50D00BF20E1 /* Colors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = ""; }; + 56559B0D1EE8777600BF20E1 /* RxRealm.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxRealm.framework; path = Carthage/Build/iOS/RxRealm.framework; sourceTree = ""; }; + 56559B131EE89E7900BF20E1 /* DeviceModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceModel.swift; sourceTree = ""; }; 5669A7F91EA904AF003C7B93 /* SwitchCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SwitchCell.xib; sourceTree = ""; }; 5669A7FB1EA904D2003C7B93 /* TextFieldCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TextFieldCell.xib; sourceTree = ""; }; 5669A7FD1EA904E4003C7B93 /* TextCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TextCell.xib; sourceTree = ""; }; @@ -337,6 +339,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 56559B0E1EE8777600BF20E1 /* RxRealm.framework in Frameworks */, + 564775831EE5CFC500A0C855 /* Realm.framework in Frameworks */, + 564775841EE5CFC500A0C855 /* RealmSwift.framework in Frameworks */, 56BBC9D41EDC7A6D00CDAF8B /* libargon2.a in Frameworks */, 568F56751EA7E5DE00132D7D /* PKHUD.framework in Frameworks */, 02674C851E0C757B0065EDF9 /* RxCocoa.framework in Frameworks */, @@ -451,6 +456,9 @@ 02AED8171DD4C4B000F740BA /* Frameworks */ = { isa = PBXGroup; children = ( + 56559B0D1EE8777600BF20E1 /* RxRealm.framework */, + 564775811EE5CFC500A0C855 /* Realm.framework */, + 564775821EE5CFC500A0C855 /* RealmSwift.framework */, 56BBC9D31EDC7A6D00CDAF8B /* libargon2.a */, 568F56721EA7E38F00132D7D /* PKHUD.framework */, 02674C801E0C757B0065EDF9 /* RxBlocking.framework */, @@ -495,6 +503,7 @@ 02DD80C71E1EAD70009A3510 /* AccountConfigModel.swift */, 02DD80C91E1EAF1A009A3510 /* AccountCredentialsModel.swift */, 02DD80CC1E1EB2E4009A3510 /* ConfigKeyModel.swift */, + 56559B131EE89E7900BF20E1 /* DeviceModel.swift */, ); name = Account; sourceTree = ""; @@ -599,7 +608,6 @@ 043999F61D1C2D9D00E99CD9 /* AppDelegate.swift */, 04399A021D1C2D9D00E99CD9 /* Assets.xcassets */, 04399A071D1C2D9D00E99CD9 /* Info.plist */, - 043999F81D1C2D9D00E99CD9 /* Ring.xcdatamodeld */, ); path = Ring; sourceTree = ""; @@ -956,6 +964,9 @@ "$(SRCROOT)/Carthage/Build/iOS/RxCocoa.framework", "$(SRCROOT)/Carthage/Build/iOS/PKHUD.framework", "$(SRCROOT)/Carthage/Build/iOS/RxDataSources.framework", + "$(SRCROOT)/Carthage/Build/iOS/Realm.framework", + "$(SRCROOT)/Carthage/Build/iOS/RealmSwift.framework", + "$(SRCROOT)/Carthage/Build/iOS/RxRealm.framework", ); outputPaths = ( ); @@ -986,6 +997,8 @@ 56BBC9E31EDDCC8100CDAF8B /* ConversationSection.swift in Sources */, 56BBC9A81ED7152300CDAF8B /* SmartlistViewController.swift in Sources */, 5516C29F1E71CEFF009D3D2D /* AccountModelHelper.swift in Sources */, + 56559B141EE89E7900BF20E1 /* DeviceModel.swift in Sources */, + 569D86581ECF62510064D88B /* ContactsAdapter.mm in Sources */, 56308BA71EA00E5700660275 /* NameRegistrationResponse.m in Sources */, 56AC64E11E80542300EA1AA9 /* TextFieldCell.swift in Sources */, 56AC64E31E805F0200EA1AA9 /* TextCell.swift in Sources */, @@ -1005,7 +1018,6 @@ 56BBC9B01ED7155700CDAF8B /* ConversationModel.swift in Sources */, 563AEC771EA664C0003A5641 /* RegistrationResponse.m in Sources */, 02B22DFD1DF755BB000358C9 /* CreateRingAccountViewModel.swift in Sources */, - 043999FA1D1C2D9D00E99CD9 /* Ring.xcdatamodeld in Sources */, 564C445B1E8EA44E000F92B1 /* Durations.swift in Sources */, 0438663B1D2313B700E06CE2 /* AccountDetailsViewController.swift in Sources */, 56BBC9DF1EDDC9D300CDAF8B /* LookupNameResponse.m in Sources */, @@ -1034,11 +1046,7 @@ buildActionMask = 2147483647; files = ( 024B61311DF7656A00C4F9DE /* FixtureFailInitDRingAdapter.mm in Sources */, - 5557FD4E1E81B1F20043E394 /* AccountModelHelper.swift in Sources */, - 5557FD4F1E81B2990043E394 /* AccountCredentialsModel.swift in Sources */, - 5557FD4B1E81AECF0043E394 /* AccountModel.swift in Sources */, 5557FD4A1E81AE850043E394 /* AccountModelHelperTests.swift in Sources */, - 5557FD4C1E81AF840043E394 /* AccountConfigModel.swift in Sources */, 024B61331DF765CA00C4F9DE /* DaemonService.swift in Sources */, 04399A111D1C2D9D00E99CD9 /* RingTests.swift in Sources */, 029CE9D71E1D8C860000C8E1 /* ServiceEventTests.swift in Sources */, @@ -1353,19 +1361,6 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ - -/* Begin XCVersionGroup section */ - 043999F81D1C2D9D00E99CD9 /* Ring.xcdatamodeld */ = { - isa = XCVersionGroup; - children = ( - 043999F91D1C2D9D00E99CD9 /* Ring.xcdatamodel */, - ); - currentVersion = 043999F91D1C2D9D00E99CD9 /* Ring.xcdatamodel */; - path = Ring.xcdatamodeld; - sourceTree = ""; - versionGroupType = wrapper.xcdatamodel; - }; -/* End XCVersionGroup section */ }; rootObject = 043999EB1D1C2D9D00E99CD9 /* Project object */; } diff --git a/Ring/Ring/Account/AccountConfigModel.swift b/Ring/Ring/Account/AccountConfigModel.swift index a3c5d3f..9e14201 100644 --- a/Ring/Ring/Account/AccountConfigModel.swift +++ b/Ring/Ring/Account/AccountConfigModel.swift @@ -18,6 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +import RealmSwift + /** The different states that an account can have during time. @@ -61,19 +63,12 @@ enum AccountType: String { - expose a clear interface to manipulate the configuration of an account - keep this configuration */ -struct AccountConfigModel { +class AccountConfigModel :Object { /** The collection of configuration elements. */ fileprivate var configValues = Dictionary() - /** - Default constructor. - */ - init() { - //~ Empty initializer - } - /** Constructor. @@ -81,7 +76,8 @@ struct AccountConfigModel { - Parameter details: an optional collection of configuration elements */ - init(withDetails details: Dictionary?) { + convenience init(withDetails details: Dictionary?) { + self.init() if details != nil { for (key, value) in details! { if let confKey = ConfigKey(rawValue: key) { diff --git a/Ring/Ring/Account/AccountCredentialsModel.swift b/Ring/Ring/Account/AccountCredentialsModel.swift index e4965b0..7180674 100644 --- a/Ring/Ring/Account/AccountCredentialsModel.swift +++ b/Ring/Ring/Account/AccountCredentialsModel.swift @@ -18,6 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +import RealmSwift + /** Errors that can be thrown when trying create an AccountCredentialsModel @@ -33,10 +35,10 @@ enum CredentialsError: Error { Its responsability: - keep the credentials of an account. */ -struct AccountCredentialsModel { - fileprivate(set) var username: String - fileprivate(set) var password: String - fileprivate(set) var realm: String +class AccountCredentialsModel :Object { + dynamic var username: String = "" + dynamic var password: String = "" + dynamic var accountRealm: String = "" /** Constructor. @@ -44,33 +46,35 @@ struct AccountCredentialsModel { - Parameters: - username: the username of the account - password: the password of the account - - realm : the realm of the account + - accountRealm : the realm of the account */ - init(withUsername username: String, password: String, realm: String) { + convenience init(withUsername username: String, password: String, accountRealm: String) { + self.init() self.username = username self.password = password - self.realm = realm + self.accountRealm = accountRealm } /** Constructor. - Parameter raw: raw data to populate the credentials. This collection must contain all the - needed elements (username, password, realm). + needed elements (username, password, accountRealm). - Throws: CredentialsError */ - init(withRawaData raw: Dictionary) throws { + convenience init(withRawaData raw: Dictionary) throws { + self.init() let username = raw[ConfigKey.AccountUsername.rawValue] let password = raw[ConfigKey.AccountPassword.rawValue] - let realm = raw[ConfigKey.AccountRealm.rawValue] + let accountRealm = raw[ConfigKey.AccountRealm.rawValue] - if username == nil || password == nil || realm == nil { + if username == nil || password == nil || accountRealm == nil { throw CredentialsError.NotEnoughData } self.username = username! self.password = password! - self.realm = realm! + self.accountRealm = accountRealm! } } diff --git a/Ring/Ring/Account/AccountModel.swift b/Ring/Ring/Account/AccountModel.swift index c983b68..70f8ddd 100644 --- a/Ring/Ring/Account/AccountModel.swift +++ b/Ring/Ring/Account/AccountModel.swift @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +import RealmSwift import Foundation /** @@ -31,32 +32,31 @@ enum AccountModelError: Error { /** A class representing an account. */ -class AccountModel : Equatable { +class AccountModel : Object { // MARK: Public members - let id: String - - var registeringUsername = false - var devices = Dictionary() - var details: AccountConfigModel - var volatileDetails: AccountConfigModel - var credentialDetails = Array() + dynamic var id: String = "" + dynamic var registeringUsername = false + dynamic var details: AccountConfigModel? + dynamic var volatileDetails: AccountConfigModel? + let credentialDetails = List() + let devices = List() // MARK: Init - init(withAccountId accountId: String) { + convenience init(withAccountId accountId: String) { + self.init() self.id = accountId - self.details = AccountConfigModel() - self.volatileDetails = AccountConfigModel() } - init(withAccountId accountId: String, + convenience init(withAccountId accountId: String, details: AccountConfigModel, volatileDetails: AccountConfigModel, - credentials: [AccountCredentialsModel], - devices: Dictionary) throws { + credentials: List, + devices: [DeviceModel]) throws { + self.init() self.id = accountId self.details = details self.volatileDetails = volatileDetails - self.devices = devices + self.devices.append(objectsIn: devices) } public static func ==(lhs: AccountModel, rhs: AccountModel) -> Bool { diff --git a/Ring/Ring/Account/AccountModelHelper.swift b/Ring/Ring/Account/AccountModelHelper.swift index 202bdeb..041d9d6 100644 --- a/Ring/Ring/Account/AccountModelHelper.swift +++ b/Ring/Ring/Account/AccountModelHelper.swift @@ -40,9 +40,9 @@ struct AccountModelHelper { */ func isAccountSip() -> Bool { let sipString = AccountType.SIP.rawValue - let accountType = self.account.details + let accountType = self.account.details? .get(withConfigKeyModel: ConfigKeyModel.init(withKey: .AccountType)) - return sipString.compare(accountType) == ComparisonResult.orderedSame + return sipString.compare(accountType!) == ComparisonResult.orderedSame } /** @@ -52,9 +52,9 @@ struct AccountModelHelper { */ func isAccountRing() -> Bool { let ringString = AccountType.Ring.rawValue - let accountType = self.account.details + let accountType = self.account.details? .get(withConfigKeyModel: ConfigKeyModel.init(withKey: .AccountType)) - return ringString.compare(accountType) == ComparisonResult.orderedSame + return ringString.compare(accountType!) == ComparisonResult.orderedSame } /** @@ -63,7 +63,7 @@ struct AccountModelHelper { - Returns: true if the account is enabled, false otherwise. */ func isEnabled() -> Bool { - return (self.account.details + return (self.account.details! .getBool(forConfigKeyModel: ConfigKeyModel.init(withKey: .AccountEnable))) } @@ -73,7 +73,7 @@ struct AccountModelHelper { - Returns: the registration state of the account as a String. */ func getRegistrationState() -> String { - return (self.account.volatileDetails + return (self.account.volatileDetails! .get(withConfigKeyModel: ConfigKeyModel.init(withKey: .AccountRegistrationStatus))) } @@ -124,12 +124,12 @@ struct AccountModelHelper { var ringId :String? { let accountUsernameKey = ConfigKeyModel(withKey: ConfigKey.AccountUsername) - let accountUsername = self.account.details.get(withConfigKeyModel: accountUsernameKey) + let accountUsername = self.account.details?.get(withConfigKeyModel: accountUsernameKey) let ringIdPrefix = "ring:" - if accountUsername.contains(ringIdPrefix) { - let index = accountUsername.range(of: ringIdPrefix)?.upperBound - return accountUsername.substring(from: index!) + if accountUsername!.contains(ringIdPrefix) { + let index = accountUsername?.range(of: ringIdPrefix)?.upperBound + return accountUsername?.substring(from: index!) } else { return nil } diff --git a/Ring/Ring/AppDelegate.swift b/Ring/Ring/AppDelegate.swift index 1baaf74..783b1b8 100644 --- a/Ring/Ring/AppDelegate.swift +++ b/Ring/Ring/AppDelegate.swift @@ -20,7 +20,7 @@ */ import UIKit -import CoreData +import RealmSwift @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { @@ -56,73 +56,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } func applicationWillTerminate(_ application: UIApplication) { - self.saveContext() self.stopDaemon() } - // MARK: - Core Data stack - - lazy var applicationDocumentsDirectory: URL = { - // The directory the application uses to store the Core Data store file. This code uses a directory named "cx.ring.Ring" in the application's documents Application Support directory. - let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) - return urls[urls.count - 1] - }() - - lazy var managedObjectModel: NSManagedObjectModel = { - // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model. - let modelURL = Bundle.main.url(forResource: "Ring", withExtension: "momd")! - return NSManagedObjectModel(contentsOf: modelURL)! - }() - - lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = { - // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail. - // Create the coordinator and store - let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) - let url = self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite") - var failureReason = "There was an error creating or loading the application's saved data." - do { - try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil) - } catch { - // Report any error we got. - var dict = [String: AnyObject]() - dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject? - dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject? - - dict[NSUnderlyingErrorKey] = error as NSError - let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict) - // Replace this with code to handle the error appropriately. - // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)") - abort() - } - - return coordinator - }() - - lazy var managedObjectContext: NSManagedObjectContext = { - // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail. - let coordinator = self.persistentStoreCoordinator - var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) - managedObjectContext.persistentStoreCoordinator = coordinator - return managedObjectContext - }() - - // MARK: - Core Data Saving support - - func saveContext () { - if managedObjectContext.hasChanges { - do { - try managedObjectContext.save() - } catch { - // Replace this implementation with code to handle the error appropriately. - // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - let nserror = error as NSError - NSLog("Unresolved error \(nserror), \(nserror.userInfo)") - abort() - } - } - } - // MARK: - Ring Daemon fileprivate func startDaemon() { diff --git a/Ring/Ring/ContactModel.swift b/Ring/Ring/ContactModel.swift index c44d841..d18eb95 100644 --- a/Ring/Ring/ContactModel.swift +++ b/Ring/Ring/ContactModel.swift @@ -18,14 +18,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -import UIKit +import RealmSwift -class ContactModel { +class ContactModel :Object { - var ringId: String - var userName: String? + dynamic var ringId: String = "" + dynamic var userName: String? - init(withRingId ringId: String) { + convenience init(withRingId ringId: String) { + self.init() self.ringId = ringId } diff --git a/Ring/Ring/ContactViewModel.swift b/Ring/Ring/ContactViewModel.swift index bcb6dab..1797f65 100644 --- a/Ring/Ring/ContactViewModel.swift +++ b/Ring/Ring/ContactViewModel.swift @@ -19,12 +19,14 @@ */ import RxSwift +import RealmSwift class ContactViewModel { private let nameService = AppDelegate.nameService private let disposeBag = DisposeBag() private let contact: ContactModel + private let realm = try! Realm() let userName = Variable("") @@ -46,7 +48,11 @@ class ContactViewModel { return lookupNameResponse.address != nil && lookupNameResponse.address == self.contact.ringId }).subscribe(onNext: { [unowned self] lookupNameResponse in if lookupNameResponse.state == .found { - self.contact.userName = lookupNameResponse.name + + try! self.realm.write { + self.contact.userName = lookupNameResponse.name + } + self.userName.value = lookupNameResponse.name } else { self.userName.value = lookupNameResponse.address diff --git a/Ring/Ring/ConversationModel.swift b/Ring/Ring/ConversationModel.swift index d1624cd..e5a718c 100644 --- a/Ring/Ring/ConversationModel.swift +++ b/Ring/Ring/ConversationModel.swift @@ -18,15 +18,17 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -class ConversationModel { +import RealmSwift - var messages = [MessageModel]() - var recipient: ContactModel - var accountId: String +class ConversationModel :Object { - init(withRecipient recipient: ContactModel, accountId: String) { + let messages = List() + dynamic var recipient :ContactModel? + dynamic var accountId: String = "" + + convenience init(withRecipient recipient: ContactModel, accountId: String) { + self.init() self.recipient = recipient self.accountId = accountId } - } diff --git a/Ring/Ring/ConversationViewController.swift b/Ring/Ring/ConversationViewController.swift index 222b671..2c5240c 100644 --- a/Ring/Ring/ConversationViewController.swift +++ b/Ring/Ring/ConversationViewController.swift @@ -100,11 +100,12 @@ class ConversationViewController: UIViewController, UITextFieldDelegate { forCellReuseIdentifier: "MessageCellId") //Bind the TableView to the ViewModel - self.viewModel?.messages.bind(to: tableView.rx.items(cellIdentifier: "MessageCellId", cellType: MessageCell.self)) - { index, messageViewModel, cell in + self.viewModel?.messages + .bind(to: tableView.rx.items(cellIdentifier: "MessageCellId", + cellType: MessageCell.self)) { index, messageViewModel, cell in cell.messageLabel.text = messageViewModel.content cell.bubblePosition = messageViewModel.bubblePosition() - }.addDisposableTo(disposeBag) + }.addDisposableTo(disposeBag) //Scroll to bottom when reloaded self.tableView.rx.methodInvoked(#selector(UITableView.reloadData)).subscribe(onNext: { element in diff --git a/Ring/Ring/ConversationViewModel.swift b/Ring/Ring/ConversationViewModel.swift index bc0a0bf..207becb 100644 --- a/Ring/Ring/ConversationViewModel.swift +++ b/Ring/Ring/ConversationViewModel.swift @@ -20,10 +20,12 @@ import UIKit import RxSwift +import RealmSwift class ConversationViewModel { let conversation: ConversationModel + let realm = try! Realm() //Displays the entire date ( for messages received before the current week ) private let dateFormatter = DateFormatter() @@ -46,7 +48,7 @@ class ConversationViewModel { hourFormatter.dateFormat = "HH:mm" //Create observable from sorted conversations and flatMap them to view models - self.messages = self.conversationsService.conversations.asObservable().map({ conversations in + self.messages = self.conversationsService.conversations.map({ conversations in return conversations.filter({ currentConversation in return currentConversation.recipient == conversation.recipient }).flatMap({ conversation in @@ -55,20 +57,20 @@ class ConversationViewModel { }) }) }).observeOn(MainScheduler.instance) - } lazy var userName: Variable = { - - if let userName = self.conversation.recipient.userName { + if let userName = self.conversation.recipient?.userName { return Variable(userName) } else { - let tmp :Variable = ContactHelper.lookupUserName(forRingId: self.conversation.recipient.ringId, + let tmp :Variable = ContactHelper.lookupUserName(forRingId: self.conversation.recipient!.ringId, nameService: AppDelegate.nameService, disposeBag: self.disposeBag) tmp.asObservable().subscribe(onNext: { userNameFound in - self.conversation.recipient.userName = userNameFound + try! self.realm.write { + self.conversation.recipient?.userName = userNameFound + } }).addDisposableTo(self.disposeBag) return tmp @@ -134,10 +136,10 @@ class ConversationViewModel { self.conversationsService .sendMessage(withContent: content, from: accountService.currentAccount!, - to: self.conversation.recipient) + to: self.conversation.recipient!) .subscribe(onCompleted: { let accountHelper = AccountModelHelper(withAccount: self.accountService.currentAccount!) - self.saveMessage(withContent: content, byAuthor: accountHelper.ringId!, toConversationWith: self.conversation.recipient.ringId) + self.saveMessage(withContent: content, byAuthor: accountHelper.ringId!, toConversationWith: (self.conversation.recipient?.ringId)!) }).addDisposableTo(disposeBag) } diff --git a/Ring/Ring/ConversationsService.swift b/Ring/Ring/ConversationsService.swift index 5d293dd..1d802fa 100644 --- a/Ring/Ring/ConversationsService.swift +++ b/Ring/Ring/ConversationsService.swift @@ -20,17 +20,22 @@ import UIKit import RxSwift +import RealmSwift class ConversationsService: MessagesAdapterDelegate { fileprivate let messageAdapter :MessagesAdapter fileprivate let disposeBag = DisposeBag() fileprivate let textPlainMIMEType = "text/plain" + fileprivate let realm :Realm = try! Realm() + fileprivate let results :Results! - var conversations = Variable([ConversationModel]()) + var conversations :Observable> init(withMessageAdapter messageAdapter: MessagesAdapter) { self.messageAdapter = messageAdapter + self.results = realm.objects(ConversationModel.self) + self.conversations = Observable.collection(from: results) MessagesAdapter.delegate = self } @@ -54,8 +59,14 @@ class ConversationsService: MessagesAdapterDelegate { }) } - func addConversation(conversation: ConversationModel) { - self.conversations.value.append(conversation) + func addConversation(conversation: ConversationModel) -> Completable { + return Completable.create(subscribe: { [unowned self] completable in + try! self.realm.write { + self.realm.add(conversation) + } + completable(.completed) + return Disposables.create { } + }) } func saveMessage(withContent content: String, @@ -64,27 +75,26 @@ class ConversationsService: MessagesAdapterDelegate { currentAccountId: String) -> Completable { return Completable.create(subscribe: { [unowned self] completable in - let message = MessageModel(withId: nil, receivedDate: Date(), content: content, author: author) + let message = MessageModel(withId: 0, receivedDate: Date(), content: content, author: author) //Get conversations for this sender - var currentConversation = self.conversations.value.filter({ conversation in - return conversation.recipient.ringId == recipientRingId + var currentConversation = self.results.filter({ conversation in + return conversation.recipient?.ringId == recipientRingId }).first - //Get the current array of conversations - var currentConversations = self.conversations.value - //Create a new conversation for this sender if not exists if currentConversation == nil { currentConversation = ConversationModel(withRecipient: ContactModel(withRingId: recipientRingId), accountId: currentAccountId) - currentConversations.append(currentConversation!) + + try! self.realm.write { + self.realm.add(currentConversation!) + } } //Add the received message into the conversation - currentConversation?.messages.append(message) - - //Upate the value of the Variable - self.conversations.value = currentConversations + try! self.realm.write { + currentConversation?.messages.append(message) + } completable(.completed) @@ -99,23 +109,19 @@ class ConversationsService: MessagesAdapterDelegate { func setMessagesAsRead(forConversation conversation: ConversationModel) -> Completable { - return Completable.create(subscribe: { completable in - - //Get the current array of conversations - let currentConversations = self.conversations.value + return Completable.create(subscribe: { [unowned self] completable in //Filter unread messages let unreadMessages = conversation.messages.filter({ messages in return messages.status != .read }) - for message in unreadMessages { - message.status = .read + try! self.realm.write { + for message in unreadMessages { + message.status = .read + } } - //Upate the value of the Variable - self.conversations.value = currentConversations - completable(.completed) return Disposables.create { } diff --git a/Ring/Ring/DeviceModel.swift b/Ring/Ring/DeviceModel.swift new file mode 100644 index 0000000..3998bdc --- /dev/null +++ b/Ring/Ring/DeviceModel.swift @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2017 Savoir-faire Linux Inc. + * + * Author: Silbino Gonçalves Matado + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import RealmSwift +import RxRealm + +class DeviceModel: Object { + + dynamic var deviceId = "" + + convenience init(withDeviceId deviceId: String) { + self.init() + self.deviceId = deviceId + } +} diff --git a/Ring/Ring/MessageModel.swift b/Ring/Ring/MessageModel.swift index 000e485..bc1d91d 100644 --- a/Ring/Ring/MessageModel.swift +++ b/Ring/Ring/MessageModel.swift @@ -18,20 +18,22 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -class MessageModel { +import RealmSwift - var id: UInt64? - var receivedDate: Date - var content: String - var author: String - var status: MessageStatus +class MessageModel: Object { - init(withId id: UInt64?, receivedDate: Date, content: String, author: String) { + dynamic var id: Int64 = 0 + dynamic var receivedDate: Date = Date() + dynamic var content: String = "" + dynamic var author: String = "" + dynamic var status: MessageStatus = .unknown + + convenience init(withId id: Int64, receivedDate: Date, content: String, author: String) { + self.init() self.id = id self.receivedDate = receivedDate self.content = content self.author = author self.status = .unknown } - } diff --git a/Ring/Ring/MessagesService.swift b/Ring/Ring/MessagesService.swift deleted file mode 100644 index 4fbfafb..0000000 --- a/Ring/Ring/MessagesService.swift +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2017 Savoir-faire Linux Inc. - * - * Author: Silbino Gonçalves Matado - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -import UIKit -import RxSwift - -class MessagesService: MessagesAdapterDelegate { - - fileprivate let messageAdapter :MessagesAdapter - fileprivate let disposeBag = DisposeBag() - fileprivate let textPlainMIMEType = "text/plain" - - var conversations = Variable([ConversationModel]()) - - init(withMessageAdapter messageAdapter: MessagesAdapter) { - self.messageAdapter = messageAdapter - MessagesAdapter.delegate = self - - } - - func sendMessage(withContent content: String, from senderAccount: AccountModel, to recipient: ContactModel) { - - let contentDict = [textPlainMIMEType : content] - self.messageAdapter.sendMessage(withContent: contentDict, withAccountId: senderAccount.id, to: recipient.ringId) - - let accountHelper = AccountModelHelper(withAccount: senderAccount) - - if accountHelper.ringId! != recipient.ringId { - self.addMessage(withContent: content, byAuthor: accountHelper.ringId!, toConversationWith: recipient.ringId) - } - } - - func addConversation(conversation: ConversationModel) { - self.conversations.value.append(conversation) - } - - fileprivate func addMessage(withContent content: String, byAuthor author: String, toConversationWith account: String) { - - let message = MessageModel(withId: nil, receivedDate: Date(), content: content, author: author) - - if author != account { - message.status = .read - } - - //Get conversations for this sender - var currentConversation = self.conversations.value.filter({ conversation in - return conversation.recipient.ringId == account - }).first - - //Get the current array of conversations - var currentConversations = self.conversations.value - - //Create a new conversation for this sender if not exists - if currentConversation == nil { - currentConversation = ConversationModel(withRecipient: ContactModel(withRingId: account)) - currentConversations.append(currentConversation!) - } - - //Add the received message into the conversation - currentConversation?.messages.append(message) - currentConversation?.lastMessageDate = message.receivedDate - - //Upate the value of the Variable - self.conversations.value = currentConversations - - } - - func status(forMessageId messageId: UInt64) -> MessageStatus { - return self.messageAdapter.status(forMessageId: messageId) - } - - func setMessagesAsRead(forConversation conversation: ConversationModel) { - - //Get the current array of conversations - let currentConversations = self.conversations.value - - //Filter unread messages - let unreadMessages = conversation.messages.filter({ messages in - return messages.status != .read - }) - - for message in unreadMessages { - message.status = .read - } - - //Upate the value of the Variable - self.conversations.value = currentConversations - } - - //MARK: Message Adapter delegate - - func didReceiveMessage(_ message: Dictionary, from senderAccount: String, - to receiverAccountId: String) { - - if let content = message[textPlainMIMEType] { - self.addMessage(withContent: content, byAuthor: senderAccount, toConversationWith: senderAccount) - } - } - - func messageStatusChanged(_ status: MessageStatus, for messageId: UInt64, - from senderAccountId: String, to receiverAccount: String) { - - print("messageStatusChanged: \(status.rawValue) for: \(messageId) from: \(senderAccountId) to: \(receiverAccount)") - } -} diff --git a/Ring/Ring/Ring.xcdatamodeld/.xccurrentversion b/Ring/Ring/Ring.xcdatamodeld/.xccurrentversion index 93b0b14..0c67376 100644 --- a/Ring/Ring/Ring.xcdatamodeld/.xccurrentversion +++ b/Ring/Ring/Ring.xcdatamodeld/.xccurrentversion @@ -1,8 +1,5 @@ - - _XCCurrentVersionName - Ring.xcdatamodel - + diff --git a/Ring/Ring/RingPrefixHeader.pch b/Ring/Ring/RingPrefixHeader.pch index 016b63b..7309a44 100644 --- a/Ring/Ring/RingPrefixHeader.pch +++ b/Ring/Ring/RingPrefixHeader.pch @@ -24,4 +24,11 @@ #import #import +/* + * Because Realm does not support mix ObjC/Swift projects. This workaround fixes compilation failure + * due to the RealmSwiftObject missing declartion from *-Swift.h + * See https://github.com/realm/realm-cocoa/issues/3073 for more details + */ +#define RealmSwiftObject NSObject + #endif diff --git a/Ring/Ring/Services/AccountsService.swift b/Ring/Ring/Services/AccountsService.swift index 3595848..9c6014c 100644 --- a/Ring/Ring/Services/AccountsService.swift +++ b/Ring/Ring/Services/AccountsService.swift @@ -21,6 +21,7 @@ import RxCocoa import RxSwift +import RealmSwift enum AddAccountError: Error { case TemplateNotConform @@ -141,10 +142,12 @@ class AccountsService: AccountAdapterDelegate { for account in accountList { account.details = self.getAccountDetails(fromAccountId: account.id) account.volatileDetails = self.getVolatileAccountDetails(fromAccountId: account.id) - account.devices = getKnownRingDevices(fromAccountId: account.id) + account.devices.append(objectsIn: getKnownRingDevices(fromAccountId: account.id)) do { - account.credentialDetails = try self.getAccountCredentials(fromAccountId: account.id) + let credentialDetails = try self.getAccountCredentials(fromAccountId: account.id) + account.credentialDetails.removeAll() + account.credentialDetails.append(objectsIn: credentialDetails) } catch { print(error) } @@ -259,12 +262,12 @@ class AccountsService: AccountAdapterDelegate { - Returns: the list of credentials. */ - func getAccountCredentials(fromAccountId id: String) throws -> [AccountCredentialsModel] { + func getAccountCredentials(fromAccountId id: String) throws -> List { let creds: NSArray = accountAdapter.getCredentials(id) as NSArray let rawCredentials = creds as NSArray? as? Array> ?? nil if let rawCredentials = rawCredentials { - var credentialsList = [AccountCredentialsModel]() + let credentialsList = List() for rawCredentials in rawCredentials { do { let credentials = try AccountCredentialsModel(withRawaData: rawCredentials) @@ -290,10 +293,15 @@ class AccountsService: AccountAdapterDelegate { - Returns: the known Ring devices. */ - func getKnownRingDevices(fromAccountId id: String) -> Dictionary { - let devices: NSDictionary = accountAdapter.getKnownRingDevices(id) as NSDictionary - let ringDevices = devices as NSDictionary? as? Dictionary ?? nil - return ringDevices! + func getKnownRingDevices(fromAccountId id: String) -> [DeviceModel] { + let knownRingDevices = accountAdapter.getKnownRingDevices(id)! as NSDictionary + + var devices = [DeviceModel]() + for key in knownRingDevices.allKeys { + devices.append(DeviceModel(withDeviceId: key as! String)) + } + + return devices } /** diff --git a/Ring/Ring/SmartlistViewModel.swift b/Ring/Ring/SmartlistViewModel.swift index d2ec0f4..79e3326 100644 --- a/Ring/Ring/SmartlistViewModel.swift +++ b/Ring/Ring/SmartlistViewModel.swift @@ -18,7 +18,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -import UIKit import RxSwift class SmartlistViewModel { @@ -63,7 +62,7 @@ class SmartlistViewModel { //Get the current ConversationViewModel if exists or create it if let foundConversationViewModel = self.conversationViewModels.filter({ conversationViewModel in - return conversationViewModel.conversation === conversationModel + return conversationViewModel.conversation.isEqual(conversationModel) }).first { conversationViewModel = foundConversationViewModel } else { @@ -114,11 +113,11 @@ class SmartlistViewModel { }).addDisposableTo(disposeBag) //Observe username lookup - self.nameService.usernameLookupStatus.subscribe(onNext: { usernameLookupStatus in + self.nameService.usernameLookupStatus.observeOn(MainScheduler.instance).subscribe(onNext: { usernameLookupStatus in if usernameLookupStatus.state == .found && (usernameLookupStatus.name == self.searchBarText.value ) { if let conversation = self.conversationViewModels.filter({ conversationViewModel in - conversationViewModel.conversation.recipient.ringId == usernameLookupStatus.address + conversationViewModel.conversation.recipient?.ringId == usernameLookupStatus.address }).first { self.contactFoundConversation.value = conversation } else { @@ -154,7 +153,7 @@ class SmartlistViewModel { //Filter conversations by user name or RingId let filteredConversations = self.conversationViewModels.filter({ conversationViewModel in - if let recipientUserName = conversationViewModel.conversation.recipient.userName { + if let recipientUserName = conversationViewModel.conversation.recipient?.userName { return recipientUserName.lowercased().hasPrefix(text.lowercased()) } else { return false @@ -176,7 +175,10 @@ class SmartlistViewModel { if !self.conversationViewModels.contains(where: { viewModel in return viewModel === selectedItem }) { - self.conversationsService.addConversation(conversation: selectedItem.conversation) + self.conversationsService + .addConversation(conversation: selectedItem.conversation) + .subscribe() + .addDisposableTo(disposeBag) } } } diff --git a/Ring/RingTests/AccountModelHelperTests.swift b/Ring/RingTests/AccountModelHelperTests.swift index 0032c85..889c751 100644 --- a/Ring/RingTests/AccountModelHelperTests.swift +++ b/Ring/RingTests/AccountModelHelperTests.swift @@ -19,6 +19,8 @@ */ import XCTest +import RealmSwift +@testable import Ring /** A test class designed to validate that the AccountModel helper runs as expected. @@ -163,7 +165,7 @@ class AccountModelHelperTests: XCTestCase { XCTAssertEqual(modifiedAccount.credentialDetails.count, 1) XCTAssertEqual(modifiedAccount.credentialDetails[0].username, username) XCTAssertEqual(modifiedAccount.credentialDetails[0].password, password) - XCTAssertEqual(modifiedAccount.credentialDetails[0].realm, realm) + XCTAssertEqual(modifiedAccount.credentialDetails[0].accountRealm, realm) modifiedAccount = helper.setCredentials(nil) XCTAssertEqual(modifiedAccount.credentialDetails.count, 0) diff --git a/Ring/RingTests/RingTests-Bridging-Header.h b/Ring/RingTests/RingTests-Bridging-Header.h index 210891e..dbf20cd 100644 --- a/Ring/RingTests/RingTests-Bridging-Header.h +++ b/Ring/RingTests/RingTests-Bridging-Header.h @@ -24,7 +24,6 @@ // expose to Swift. // -#import "AccountAdapter.h" -#import "DRingAdapter.h" +#import "Ring-Bridging-Header.h" #import "FixtureFailInitDRingAdapter.h" #import "FixtureFailStartDRingAdapter.h" diff --git a/Ring/RingTests/RingTests.swift b/Ring/RingTests/RingTests.swift index 50df5e8..f4f77cf 100644 --- a/Ring/RingTests/RingTests.swift +++ b/Ring/RingTests/RingTests.swift @@ -7,7 +7,6 @@ // import XCTest -@testable import Ring class RingTests: XCTestCase { -- GitLab