diff --git a/Frameworks/NynjaUIKit/NynjaUIKit.xcodeproj/project.pbxproj b/Frameworks/NynjaUIKit/NynjaUIKit.xcodeproj/project.pbxproj index 7b09f72343802f8e252f3b72b4142130de4341f3..6c1fa3dd90b9fea6199b109577608d13731cf863 100644 --- a/Frameworks/NynjaUIKit/NynjaUIKit.xcodeproj/project.pbxproj +++ b/Frameworks/NynjaUIKit/NynjaUIKit.xcodeproj/project.pbxproj @@ -36,6 +36,8 @@ /* Begin PBXFileReference section */ 1820AD65D6897E3E306C16A2 /* Pods-NynjaUIKit.translate.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NynjaUIKit.translate.xcconfig"; path = "../../Pods/Target Support Files/Pods-NynjaUIKit/Pods-NynjaUIKit.translate.xcconfig"; sourceTree = ""; }; + 1D38BAE43B22E6C930DB3980 /* Pods-NynjaUIKit.release-debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NynjaUIKit.release-debug.xcconfig"; path = "../../Pods/Target Support Files/Pods-NynjaUIKit/Pods-NynjaUIKit.release-debug.xcconfig"; sourceTree = ""; }; + 600B9308D041B8E4DE0DBEF1 /* Pods-NynjaUIKit.loaddb.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NynjaUIKit.loaddb.xcconfig"; path = "../../Pods/Target Support Files/Pods-NynjaUIKit/Pods-NynjaUIKit.loaddb.xcconfig"; sourceTree = ""; }; 7336042AC840197E622730FD /* Pods-NynjaUIKit.prerelease.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NynjaUIKit.prerelease.xcconfig"; path = "../../Pods/Target Support Files/Pods-NynjaUIKit/Pods-NynjaUIKit.prerelease.xcconfig"; sourceTree = ""; }; 8514D4C120EE27080002378A /* NynjaUIKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NynjaUIKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8514D4C420EE27080002378A /* NynjaUIKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NynjaUIKit.h; sourceTree = ""; }; @@ -66,7 +68,7 @@ B90E6396110C47D18FB00838 /* Pods-NynjaUIKit.dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NynjaUIKit.dev.xcconfig"; path = "../../Pods/Target Support Files/Pods-NynjaUIKit/Pods-NynjaUIKit.dev.xcconfig"; sourceTree = ""; }; C6C80841C9BA48F16147BAAE /* Pods_NynjaUIKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_NynjaUIKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C90742AD8E6E2E817F7DB1E9 /* Pods-NynjaUIKit.channels.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NynjaUIKit.channels.xcconfig"; path = "../../Pods/Target Support Files/Pods-NynjaUIKit/Pods-NynjaUIKit.channels.xcconfig"; sourceTree = ""; }; - DA270E65F48C8FD1EF3BF945 /* Pods-NynjaUIKit.spotify.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NynjaUIKit.spotify.xcconfig"; path = "../../Pods/Target Support Files/Pods-NynjaUIKit/Pods-NynjaUIKit.spotify.xcconfig"; sourceTree = ""; }; + E05C445073991174833FDBC7 /* Pods-NynjaUIKit.spotify.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NynjaUIKit.spotify.xcconfig"; path = "../../Pods/Target Support Files/Pods-NynjaUIKit/Pods-NynjaUIKit.spotify.xcconfig"; sourceTree = ""; }; E966C049045184395EBB9166 /* Pods-NynjaUIKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NynjaUIKit.release.xcconfig"; path = "../../Pods/Target Support Files/Pods-NynjaUIKit/Pods-NynjaUIKit.release.xcconfig"; sourceTree = ""; }; F6A55196057E1D4A2F24AC17 /* Pods-NynjaUIKit.devautotests.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NynjaUIKit.devautotests.xcconfig"; path = "../../Pods/Target Support Files/Pods-NynjaUIKit/Pods-NynjaUIKit.devautotests.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -92,7 +94,9 @@ F6A55196057E1D4A2F24AC17 /* Pods-NynjaUIKit.devautotests.xcconfig */, E966C049045184395EBB9166 /* Pods-NynjaUIKit.release.xcconfig */, 7336042AC840197E622730FD /* Pods-NynjaUIKit.prerelease.xcconfig */, - DA270E65F48C8FD1EF3BF945 /* Pods-NynjaUIKit.spotify.xcconfig */, + 600B9308D041B8E4DE0DBEF1 /* Pods-NynjaUIKit.loaddb.xcconfig */, + E05C445073991174833FDBC7 /* Pods-NynjaUIKit.spotify.xcconfig */, + 1D38BAE43B22E6C930DB3980 /* Pods-NynjaUIKit.release-debug.xcconfig */, ); name = Pods; sourceTree = ""; @@ -436,7 +440,7 @@ /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ - 5B4DFF722191A0C100E89D17 /* Spotify */ = { + 2611CEEC21807A6E00FFD4DD /* LoadDB */ = { isa = XCBuildConfiguration; baseConfigurationReference = B90E6396110C47D18FB00838 /* Pods-NynjaUIKit.dev.xcconfig */; buildSettings = { @@ -492,11 +496,11 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Spotify; + name = LoadDB; }; - 5B4DFF732191A0C100E89D17 /* Spotify */ = { + 2611CEED21807A6E00FFD4DD /* LoadDB */ = { isa = XCBuildConfiguration; - baseConfigurationReference = DA270E65F48C8FD1EF3BF945 /* Pods-NynjaUIKit.spotify.xcconfig */; + baseConfigurationReference = 600B9308D041B8E4DE0DBEF1 /* Pods-NynjaUIKit.loaddb.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Manual; @@ -519,11 +523,11 @@ SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; - name = Spotify; + name = LoadDB; }; - 8514D4C820EE27080002378A /* Dev */ = { + 4B31B845219DE57F00837B59 /* Release-Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B90E6396110C47D18FB00838 /* Pods-NynjaUIKit.dev.xcconfig */; + baseConfigurationReference = E966C049045184395EBB9166 /* Pods-NynjaUIKit.release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -577,11 +581,11 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Dev; + name = "Release-Debug"; }; - 8514D4CB20EE27080002378A /* Dev */ = { + 4B31B846219DE57F00837B59 /* Release-Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B90E6396110C47D18FB00838 /* Pods-NynjaUIKit.dev.xcconfig */; + baseConfigurationReference = 1D38BAE43B22E6C930DB3980 /* Pods-NynjaUIKit.release-debug.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Manual; @@ -604,11 +608,11 @@ SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; - name = Dev; + name = "Release-Debug"; }; - 85631BEE20EFC4F30002BE51 /* Prerelease */ = { + 4B31B847219DE58D00837B59 /* Spotify */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7336042AC840197E622730FD /* Pods-NynjaUIKit.prerelease.xcconfig */; + baseConfigurationReference = B90E6396110C47D18FB00838 /* Pods-NynjaUIKit.dev.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -662,11 +666,11 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Prerelease; + name = Spotify; }; - 85631BEF20EFC4F30002BE51 /* Prerelease */ = { + 4B31B848219DE58D00837B59 /* Spotify */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7336042AC840197E622730FD /* Pods-NynjaUIKit.prerelease.xcconfig */; + baseConfigurationReference = E05C445073991174833FDBC7 /* Pods-NynjaUIKit.spotify.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Manual; @@ -689,11 +693,11 @@ SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; - name = Prerelease; + name = Spotify; }; - 85631BF020EFC4FC0002BE51 /* Release */ = { + 8514D4C820EE27080002378A /* Dev */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E966C049045184395EBB9166 /* Pods-NynjaUIKit.release.xcconfig */; + baseConfigurationReference = B90E6396110C47D18FB00838 /* Pods-NynjaUIKit.dev.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -747,11 +751,11 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Release; + name = Dev; }; - 85631BF120EFC4FC0002BE51 /* Release */ = { + 8514D4CB20EE27080002378A /* Dev */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E966C049045184395EBB9166 /* Pods-NynjaUIKit.release.xcconfig */; + baseConfigurationReference = B90E6396110C47D18FB00838 /* Pods-NynjaUIKit.dev.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Manual; @@ -774,11 +778,11 @@ SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; - name = Release; + name = Dev; }; - 85631BF220EFC50F0002BE51 /* DevAutoTests */ = { + 85631BEE20EFC4F30002BE51 /* Prerelease */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F6A55196057E1D4A2F24AC17 /* Pods-NynjaUIKit.devautotests.xcconfig */; + baseConfigurationReference = 7336042AC840197E622730FD /* Pods-NynjaUIKit.prerelease.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -832,11 +836,11 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = DevAutoTests; + name = Prerelease; }; - 85631BF320EFC50F0002BE51 /* DevAutoTests */ = { + 85631BEF20EFC4F30002BE51 /* Prerelease */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F6A55196057E1D4A2F24AC17 /* Pods-NynjaUIKit.devautotests.xcconfig */; + baseConfigurationReference = 7336042AC840197E622730FD /* Pods-NynjaUIKit.prerelease.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Manual; @@ -859,11 +863,11 @@ SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; - name = DevAutoTests; + name = Prerelease; }; - 85631BF620EFC7EA0002BE51 /* Channels */ = { + 85631BF020EFC4FC0002BE51 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C90742AD8E6E2E817F7DB1E9 /* Pods-NynjaUIKit.channels.xcconfig */; + baseConfigurationReference = E966C049045184395EBB9166 /* Pods-NynjaUIKit.release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -917,11 +921,11 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Channels; + name = Release; }; - 85631BF720EFC7EA0002BE51 /* Channels */ = { + 85631BF120EFC4FC0002BE51 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C90742AD8E6E2E817F7DB1E9 /* Pods-NynjaUIKit.channels.xcconfig */; + baseConfigurationReference = E966C049045184395EBB9166 /* Pods-NynjaUIKit.release.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Manual; @@ -944,11 +948,11 @@ SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; - name = Channels; + name = Release; }; - 85631BFC20EFC95E0002BE51 /* Translate */ = { + 85631BF220EFC50F0002BE51 /* DevAutoTests */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 1820AD65D6897E3E306C16A2 /* Pods-NynjaUIKit.translate.xcconfig */; + baseConfigurationReference = F6A55196057E1D4A2F24AC17 /* Pods-NynjaUIKit.devautotests.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -1002,11 +1006,11 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Translate; + name = DevAutoTests; }; - 85631BFD20EFC95E0002BE51 /* Translate */ = { + 85631BF320EFC50F0002BE51 /* DevAutoTests */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 1820AD65D6897E3E306C16A2 /* Pods-NynjaUIKit.translate.xcconfig */; + baseConfigurationReference = F6A55196057E1D4A2F24AC17 /* Pods-NynjaUIKit.devautotests.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Manual; @@ -1029,7 +1033,7 @@ SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; - name = Translate; + name = DevAutoTests; }; /* End XCBuildConfiguration section */ @@ -1038,11 +1042,11 @@ isa = XCConfigurationList; buildConfigurations = ( 8514D4C820EE27080002378A /* Dev */, - 5B4DFF722191A0C100E89D17 /* Spotify */, - 85631BFC20EFC95E0002BE51 /* Translate */, - 85631BF620EFC7EA0002BE51 /* Channels */, + 4B31B847219DE58D00837B59 /* Spotify */, + 2611CEEC21807A6E00FFD4DD /* LoadDB */, 85631BF220EFC50F0002BE51 /* DevAutoTests */, 85631BF020EFC4FC0002BE51 /* Release */, + 4B31B845219DE57F00837B59 /* Release-Debug */, 85631BEE20EFC4F30002BE51 /* Prerelease */, ); defaultConfigurationIsVisible = 0; @@ -1052,11 +1056,11 @@ isa = XCConfigurationList; buildConfigurations = ( 8514D4CB20EE27080002378A /* Dev */, - 5B4DFF732191A0C100E89D17 /* Spotify */, - 85631BFD20EFC95E0002BE51 /* Translate */, - 85631BF720EFC7EA0002BE51 /* Channels */, + 4B31B848219DE58D00837B59 /* Spotify */, + 2611CEED21807A6E00FFD4DD /* LoadDB */, 85631BF320EFC50F0002BE51 /* DevAutoTests */, 85631BF120EFC4FC0002BE51 /* Release */, + 4B31B846219DE57F00837B59 /* Release-Debug */, 85631BEF20EFC4F30002BE51 /* Prerelease */, ); defaultConfigurationIsVisible = 0; diff --git a/Nynja-Share/Resources/Info.plist b/Nynja-Share/Resources/Info.plist index 7c5b3060fa8c4ee81507b16e568168c3874b0154..1cd90a1436e8978f550f23eb39e08405e9fbd3ef 100644 --- a/Nynja-Share/Resources/Info.plist +++ b/Nynja-Share/Resources/Info.plist @@ -21,7 +21,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 0.5.4.Dev + 0.5.4.Load Config $(Config) ModelsVersion diff --git a/Nynja-Share/UI/ForwardSelector/View/ForwardSelectorViewController.swift b/Nynja-Share/UI/ForwardSelector/View/ForwardSelectorViewController.swift index 19a61f0c3baf93177b8fc5fa5a88e5983fdd1b13..1575fcb7680f5d9e15adbd137ffc981e3ea2e21f 100644 --- a/Nynja-Share/UI/ForwardSelector/View/ForwardSelectorViewController.swift +++ b/Nynja-Share/UI/ForwardSelector/View/ForwardSelectorViewController.swift @@ -393,7 +393,7 @@ final class ForwardSelectorViewController: UIViewController, ForwardSelectorView } func closeShareExtension() { - MQTTService.sharedInstance.disconnect(shouldRemoveConnectionSubscriber: false) + MQTTService.sharedInstance.disconnect() if let context = self.extensionContext { context.completeRequest(returningItems: nil, completionHandler: nil) } diff --git a/Nynja.xcodeproj/project.pbxproj b/Nynja.xcodeproj/project.pbxproj index 6db4b540859e6cf7969385d28cccac069bc9c80d..0f0747971d09db74eb890e498054de5e8e406c4e 100644 --- a/Nynja.xcodeproj/project.pbxproj +++ b/Nynja.xcodeproj/project.pbxproj @@ -64,9 +64,7 @@ 00E98254205C2726008BF03D /* SessionFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E98253205C2726008BF03D /* SessionFooterView.swift */; }; 00E98256205C2740008BF03D /* SessionItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E98255205C2740008BF03D /* SessionItemCell.swift */; }; 00E9825A205FC324008BF03D /* SessionDescView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E98259205FC324008BF03D /* SessionDescView.swift */; }; - 00E9825C205FD351008BF03D /* AuthHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E9825B205FD351008BF03D /* AuthHandler.swift */; }; 00E9825E205FDB1A008BF03D /* AuthExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E9825D205FDB1A008BF03D /* AuthExtension.swift */; }; - 00E9825F205FE86E008BF03D /* AuthHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E9825B205FD351008BF03D /* AuthHandler.swift */; }; 00EB7872206E286A00E3FB03 /* WCReusableViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00EB7871206E286A00E3FB03 /* WCReusableViews.swift */; }; 00EDCA0D202B7243000928D4 /* TimeZoneSelectorDS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00EDCA0C202B7243000928D4 /* TimeZoneSelectorDS.swift */; }; 00F7B33E2029DD4B00E443E1 /* AudioItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F7B33D2029DD4B00E443E1 /* AudioItemView.swift */; }; @@ -143,10 +141,13 @@ 26053123212741C2002E1CF1 /* LogOutputWireFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26053122212741C2002E1CF1 /* LogOutputWireFrame.swift */; }; 2605312921298BEF002E1CF1 /* Logoutputcell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2605312821298BEF002E1CF1 /* Logoutputcell.swift */; }; 2605312B21299198002E1CF1 /* LogOutputDS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2605312A21299198002E1CF1 /* LogOutputDS.swift */; }; - 260552A61F9E1CD100D68DE6 /* SearchHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260552A51F9E1CD100D68DE6 /* SearchHandler.swift */; }; 260629712056EF2800CB8F65 /* LinksCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260629702056EF2800CB8F65 /* LinksCell.swift */; }; 2606F3BC20BFE20500CF7F15 /* MessageInteractor+Translation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2606F3BB20BFE20400CF7F15 /* MessageInteractor+Translation.swift */; }; 2610D4642076516900E6E2B2 /* Array+Feature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26DCB25320692237001EF0AB /* Array+Feature.swift */; }; + 2611CEF72182090900FFD4DD /* LogWriterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2611CEF62182090900FFD4DD /* LogWriterProtocol.swift */; }; + 2611CEF82182090900FFD4DD /* LogWriterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2611CEF62182090900FFD4DD /* LogWriterProtocol.swift */; }; + 2611CEF92182090900FFD4DD /* LogWriterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2611CEF62182090900FFD4DD /* LogWriterProtocol.swift */; }; + 2611CEFA2182090900FFD4DD /* LogWriterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2611CEF62182090900FFD4DD /* LogWriterProtocol.swift */; }; 26131E02210399BA00BE94F9 /* TranscribeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26131E01210399BA00BE94F9 /* TranscribeService.swift */; }; 26142B1120472ECD004E5FE4 /* MessageLinkTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26142B1020472ECD004E5FE4 /* MessageLinkTable.swift */; }; 26142B1320473BFD004E5FE4 /* DBMessageLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26142B1220473BFD004E5FE4 /* DBMessageLink.swift */; }; @@ -466,7 +467,7 @@ 359EB23D1F9A1BE600147437 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A8045D91F60E18E00AED866 /* Queue.swift */; }; 359EB2571F9A1E5000147437 /* BaseMQTTModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A62B7D71F4CB9D100F45B51 /* BaseMQTTModel.swift */; }; 359EB27B1F9A28C500147437 /* MessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 359EB27A1F9A28C500147437 /* MessageHandler.swift */; }; - 359EB27C1F9A2C8C00147437 /* MQTTServiceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A8045D71F60C98200AED866 /* MQTTServiceHelper.swift */; }; + 359EB27C1F9A2C8C00147437 /* MQTTService+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A8045D71F60C98200AED866 /* MQTTService+Helper.swift */; }; 359EB2831F9A2E6A00147437 /* ProfileHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 359EB2821F9A2E6A00147437 /* ProfileHandler.swift */; }; 359EB2841F9A6F2300147437 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6D36F8E41F0ADBD300FA1AC8 /* Localizable.strings */; }; 35B1AB821F9FB06500E65233 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35B1AB811F9FB06500E65233 /* Attachment.swift */; }; @@ -515,7 +516,7 @@ 3A8045D31F60C8E200AED866 /* MQTTServiceProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A8045CF1F60C8E200AED866 /* MQTTServiceProfile.swift */; }; 3A8045D41F60C8E200AED866 /* MQTTServiceAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A8045D01F60C8E200AED866 /* MQTTServiceAuth.swift */; }; 3A8045D61F60C93D00AED866 /* MQTTServiceChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A8045D51F60C93D00AED866 /* MQTTServiceChat.swift */; }; - 3A8045D81F60C98200AED866 /* MQTTServiceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A8045D71F60C98200AED866 /* MQTTServiceHelper.swift */; }; + 3A8045D81F60C98200AED866 /* MQTTService+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A8045D71F60C98200AED866 /* MQTTService+Helper.swift */; }; 3A8045DA1F60E18E00AED866 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A8045D91F60E18E00AED866 /* Queue.swift */; }; 3AA13C761F2252F900BE5D8F /* SearchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA13C751F2252F900BE5D8F /* SearchModel.swift */; }; 3AA4E6ACDBCB060172A7A279 /* FavoritesProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 462440AD41D807CE8957FDD9 /* FavoritesProtocols.swift */; }; @@ -538,8 +539,25 @@ 45F60C4B14438C65076457AB /* EditUsernameProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83894D517BFF22637F2878B7 /* EditUsernameProtocols.swift */; }; 4B02130220372C5700650298 /* OtherItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B02130120372C5700650298 /* OtherItemView.swift */; }; 4B0213042037331100650298 /* ScheduleMessageViewControllerConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0213032037331100650298 /* ScheduleMessageViewControllerConstants.swift */; }; + 4B030F2D2195BFF300F293B7 /* AuthHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B030F2B2195BFF300F293B7 /* AuthHandlerDelegate.swift */; }; + 4B030F2E2195BFF300F293B7 /* AuthHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B030F2B2195BFF300F293B7 /* AuthHandlerDelegate.swift */; }; + 4B030F2F2195BFF300F293B7 /* AuthHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B030F2C2195BFF300F293B7 /* AuthHandler.swift */; }; + 4B030F302195BFF300F293B7 /* AuthHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B030F2C2195BFF300F293B7 /* AuthHandler.swift */; }; + 4B030F322195CD4500F293B7 /* MQTTServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B030F312195CD4500F293B7 /* MQTTServiceDelegate.swift */; }; + 4B030F332195CD4500F293B7 /* MQTTServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B030F312195CD4500F293B7 /* MQTTServiceDelegate.swift */; }; + 4B030F362195CD9B00F293B7 /* MQTTService+QueuePool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B030F352195CD9B00F293B7 /* MQTTService+QueuePool.swift */; }; + 4B030F372195CD9B00F293B7 /* MQTTService+QueuePool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B030F352195CD9B00F293B7 /* MQTTService+QueuePool.swift */; }; + 4B030F3B2195CF8100F293B7 /* Host.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B030F3A2195CF8100F293B7 /* Host.swift */; }; + 4B030F3C2195CF8100F293B7 /* Host.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B030F3A2195CF8100F293B7 /* Host.swift */; }; + 4B030F3E2195D88100F293B7 /* UserInfoImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B030F3D2195D88100F293B7 /* UserInfoImpl.swift */; }; + 4B030F3F2195D88100F293B7 /* UserInfoImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B030F3D2195D88100F293B7 /* UserInfoImpl.swift */; }; 4B052CB0203614D400BC2A9B /* StringAtomExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B052CAF203614D400BC2A9B /* StringAtomExtension.swift */; }; 4B052CB12036193900BC2A9B /* StringAtomExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B052CAF203614D400BC2A9B /* StringAtomExtension.swift */; }; + 4B055C37219C313A001FE077 /* FileDownloaderFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B055C36219C313A001FE077 /* FileDownloaderFactory.swift */; }; + 4B055C39219C3146001FE077 /* FileDownloaderKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B055C38219C3146001FE077 /* FileDownloaderKind.swift */; }; + 4B055C3B219C4101001FE077 /* MQTTServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B055C3A219C4101001FE077 /* MQTTServiceProtocol.swift */; }; + 4B055C3C219C4119001FE077 /* MQTTServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B055C3A219C4101001FE077 /* MQTTServiceProtocol.swift */; }; + 4B055C3F219C61A2001FE077 /* ProfileHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B055C3E219C61A2001FE077 /* ProfileHandlerDelegate.swift */; }; 4B058F03204EA928004C7D9F /* DAOProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B058F02204EA928004C7D9F /* DAOProtocol.swift */; }; 4B058F0B204EAEBA004C7D9F /* RoomDAOProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B058F0A204EAEBA004C7D9F /* RoomDAOProtocol.swift */; }; 4B058F0D204EAEC3004C7D9F /* RoomDAO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B058F0C204EAEC3004C7D9F /* RoomDAO.swift */; }; @@ -556,6 +574,10 @@ 4B06D3202028A9B1003B275B /* P2pChatItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B06D31F2028A9B1003B275B /* P2pChatItemsFactory.swift */; }; 4B06D3222028A9C6003B275B /* GroupChatItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B06D3212028A9C6003B275B /* GroupChatItemsFactory.swift */; }; 4B06D3242028B209003B275B /* WCBaseItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B06D3232028B209003B275B /* WCBaseItemsFactory.swift */; }; + 4B0CC1FD2195B52000E0BA61 /* IoHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CC1FC2195B52000E0BA61 /* IoHandlerDelegate.swift */; }; + 4B0CC1FE2195B52000E0BA61 /* IoHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CC1FC2195B52000E0BA61 /* IoHandlerDelegate.swift */; }; + 4B0CC1FF2195B58000E0BA61 /* StaticDelegating.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B87717C2195AFF50014AD09 /* StaticDelegating.swift */; }; + 4B0CC2022195B69900E0BA61 /* LinkHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CC2012195B69900E0BA61 /* LinkHandlerDelegate.swift */; }; 4B1D7DFA2029BF3400703228 /* HistoryItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D7DF92029BF3400703228 /* HistoryItemsFactory.swift */; }; 4B1D7DFC2029C37900703228 /* FavoritesItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D7DFB2029C37900703228 /* FavoritesItemsFactory.swift */; }; 4B1D7DFE2029C41C00703228 /* AboutItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D7DFD2029C41C00703228 /* AboutItemsFactory.swift */; }; @@ -570,9 +592,9 @@ 4B1D7E112029FF5000703228 /* Array+WheelItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D7E102029FF5000703228 /* Array+WheelItemModel.swift */; }; 4B1D7E14202A0A0200703228 /* GroupMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D7E13202A0A0200703228 /* GroupMode.swift */; }; 4B1F1230203C8DDE00D61D21 /* JobTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1F122F203C8DDE00D61D21 /* JobTable.swift */; }; + 4B2B1558218F5423000E4916 /* SharedColumn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B1557218F5423000E4916 /* SharedColumn.swift */; }; 4B2D063A202DDA2000010A0C /* BackSwipable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2D0639202DDA2000010A0C /* BackSwipable.swift */; }; 4B2D063C202E1A1500010A0C /* ContactsExpandedItemsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2D063B202E1A1500010A0C /* ContactsExpandedItemsFactory.swift */; }; - 4B348FD62163668500CCB0E3 /* LogServiceStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF090B421635B2E00DCCA5C /* LogServiceStub.swift */; }; 4B348FD7216366A900CCB0E3 /* LogServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF090B821635B4700DCCA5C /* LogServiceProtocol.swift */; }; 4B348FD8216366AE00CCB0E3 /* LogServiceTopic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF090BA21635B6600DCCA5C /* LogServiceTopic.swift */; }; 4B348FF22163808300CCB0E3 /* DeletedIndexesCalculatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B348FF12163808300CCB0E3 /* DeletedIndexesCalculatorTests.swift */; }; @@ -643,6 +665,17 @@ 4B7E93382170D1BC001558CF /* RootNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7E93372170D1BC001558CF /* RootNavigationController.swift */; }; 4B7E933B2170D410001558CF /* ForwardSelectorWireFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7E933A2170D410001558CF /* ForwardSelectorWireFrame.swift */; }; 4B7E933E2170D4FF001558CF /* ServiceFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7E933D2170D4FF001558CF /* ServiceFactory.swift */; }; + 4B86C562219C12840006A192 /* DAO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B86C561219C12840006A192 /* DAO.swift */; }; + 4B87712F2192F6940014AD09 /* ColumnDefinitionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B87712E2192F6940014AD09 /* ColumnDefinitionExtension.swift */; }; + 4B877131219312570014AD09 /* ColumnExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B877130219312570014AD09 /* ColumnExtension.swift */; }; + 4B877133219314D50014AD09 /* TypedRequestExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B877132219314D50014AD09 /* TypedRequestExtension.swift */; }; + 4B8771352193154E0014AD09 /* DBExtendedModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8771342193154E0014AD09 /* DBExtendedModel.swift */; }; + 4B877137219315770014AD09 /* QueryInterfaceRequestExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B877136219315770014AD09 /* QueryInterfaceRequestExtension.swift */; }; + 4B877139219315AA0014AD09 /* SercerModelConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B877138219315AA0014AD09 /* SercerModelConvertible.swift */; }; + 4B87713B219328780014AD09 /* QueryArgs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B87713A219328780014AD09 /* QueryArgs.swift */; }; + 4B8771782195AC5B0014AD09 /* HistoryHandlerSubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8771772195AC5B0014AD09 /* HistoryHandlerSubscriber.swift */; }; + 4B87717B2195AF9D0014AD09 /* TypingHandlerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B87717A2195AF9D0014AD09 /* TypingHandlerDelegate.swift */; }; + 4B87717D2195AFF50014AD09 /* StaticDelegating.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B87717C2195AFF50014AD09 /* StaticDelegating.swift */; }; 4B8996C8204ECE9B00DCB183 /* ContactDAO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8996C7204ECE9B00DCB183 /* ContactDAO.swift */; }; 4B8996CA204ECEA700DCB183 /* ContactDAOProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8996C9204ECEA700DCB183 /* ContactDAOProtocol.swift */; }; 4B8996CD204ED33400DCB183 /* StarDAOProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8996CC204ED33400DCB183 /* StarDAOProtocol.swift */; }; @@ -678,9 +711,13 @@ 4BB0EFBB2151347900704136 /* AlertImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB0EFB82151347900704136 /* AlertImageViewController.swift */; }; 4BB0EFBC2151347900704136 /* AlertImageViewControllerConstraints.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB0EFB92151347900704136 /* AlertImageViewControllerConstraints.swift */; }; 4BB0EFBD2151347900704136 /* AlertManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB0EFBA2151347900704136 /* AlertManager.swift */; }; + 4BB35E23219AF46E0007C18E /* RosterRelatedQueryArgs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB35E22219AF46E0007C18E /* RosterRelatedQueryArgs.swift */; }; + 4BC8B38D2191AC360086DC6C /* ContactsProvidingFetchingArgs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC8B38C2191AC360086DC6C /* ContactsProvidingFetchingArgs.swift */; }; 4BD53BF4202C8BCA00569C1A /* AVURLAsset+Duration.swift in Sources */ = {isa = PBXBuildFile; fileRef = E77FBDDC1FFE828400BDB255 /* AVURLAsset+Duration.swift */; }; 4BDC7E61203492CA00BCD381 /* TopSwipable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDC7E60203492CA00BCD381 /* TopSwipable.swift */; }; 4BDC7E63203494C000BCD381 /* ScheduleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDC7E62203494C000BCD381 /* ScheduleButton.swift */; }; + 4BDDAAE9218CA28300F775A7 /* HomeDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDAAE8218CA28300F775A7 /* HomeDataProvider.swift */; }; + 4BDDAAEB218CA2C700F775A7 /* HomeDataProviderImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDAAEA218CA2C700F775A7 /* HomeDataProviderImpl.swift */; }; 4BE2C5D92142EAC500A73DD9 /* AudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BE2C5D02142EAC500A73DD9 /* AudioPlayer.swift */; }; 4BE2C5DA2142EAC500A73DD9 /* AudioSessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BE2C5D22142EAC500A73DD9 /* AudioSessionManager.swift */; }; 4BE2C5DB2142EAC500A73DD9 /* AudioRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BE2C5D42142EAC500A73DD9 /* AudioRecorder.swift */; }; @@ -692,7 +729,6 @@ 4BE2C5E72142EB5A00A73DD9 /* NynjaCommunicatorService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BE2C5E52142EB5A00A73DD9 /* NynjaCommunicatorService.swift */; }; 4BE2C5E82142EB5A00A73DD9 /* NynjaRingingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BE2C5E62142EB5A00A73DD9 /* NynjaRingingService.swift */; }; 4BEE89D69CACB85ABEE9046F /* QRCodeGeneratorPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFAB7D8D9024C26FA51BF783 /* QRCodeGeneratorPresenter.swift */; }; - 4BF090B621635B3000DCCA5C /* LogServiceStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF090B421635B2E00DCCA5C /* LogServiceStub.swift */; }; 4BF090B921635B4700DCCA5C /* LogServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF090B821635B4700DCCA5C /* LogServiceProtocol.swift */; }; 4BF090BB21635B6600DCCA5C /* LogServiceTopic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF090BA21635B6600DCCA5C /* LogServiceTopic.swift */; }; 4BF090BC21635B8000DCCA5C /* LogServiceTopic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF090BA21635B6600DCCA5C /* LogServiceTopic.swift */; }; @@ -715,6 +751,8 @@ 4BF2C3E42188BABC00E59F6C /* UIDeviceExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7A77FD71FACC360004AE609 /* UIDeviceExtension.swift */; }; 4BF2C3E92189B49500E59F6C /* Localizable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D485DE41F0AD96D00E12FB1 /* Localizable.swift */; }; 4BF2C3F02189F58F00E59F6C /* ServerSignal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF2C3EF2189F58F00E59F6C /* ServerSignal.swift */; }; + 4BF2C3FC218AFE9D00E59F6C /* FullNameRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF2C3FB218AFE9D00E59F6C /* FullNameRepresentable.swift */; }; + 4BF2C3FD218AFF6300E59F6C /* FullNameRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF2C3FB218AFE9D00E59F6C /* FullNameRepresentable.swift */; }; 4C5EEA13EBC6A8398F08DCD1 /* MainWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AAB8F770774CE3AE3FD6E1 /* MainWireframe.swift */; }; 4D53FE7454959323B1CCFD96 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D270F638DBB2D8FC1BDEB633 /* ProfileViewController.swift */; }; 4DAEBCF361B86B0AD3C98749 /* EditUsernameInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBE3BAC9B7EA418FB463EF04 /* EditUsernameInteractor.swift */; }; @@ -1474,8 +1512,6 @@ A4330A6F2109EBA70060BD93 /* CountriesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4330A6D2109EBA70060BD93 /* CountriesProvider.swift */; }; A4330A712109EBB30060BD93 /* CountriesProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4330A702109EBB30060BD93 /* CountriesProviding.swift */; }; A4330A722109EBB30060BD93 /* CountriesProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4330A702109EBB30060BD93 /* CountriesProviding.swift */; }; - A4330A742109F0D40060BD93 /* StorageService+UserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4330A732109F0D40060BD93 /* StorageService+UserInfo.swift */; }; - A4330A752109F0D40060BD93 /* StorageService+UserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4330A732109F0D40060BD93 /* StorageService+UserInfo.swift */; }; A433D9A120A5C18C00C946F9 /* ContactsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A433D9A020A5C18C00C946F9 /* ContactsProvider.swift */; }; A433D9A320A5C19600C946F9 /* ContactsProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = A433D9A220A5C19600C946F9 /* ContactsProviding.swift */; }; A438DB9220763AFB00AA86A2 /* Contact+Desc.swift in Sources */ = {isa = PBXBuildFile; fileRef = A438DB9120763AFB00AA86A2 /* Contact+Desc.swift */; }; @@ -1946,7 +1982,7 @@ E7598F6C1FA1D8B90082FBE7 /* ProfileContactCellLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7598F651FA1D8B80082FBE7 /* ProfileContactCellLayout.swift */; }; E761A0D91F8B8CF000C088E0 /* EditProfileViewControllerLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = E761A0D81F8B8CF000C088E0 /* EditProfileViewControllerLayout.swift */; }; E761A0DC1F8B8F3900C088E0 /* NynjaButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E761A0DB1F8B8F3900C088E0 /* NynjaButton.swift */; }; - E76462891FCD64790091FC2E /* DBModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E76462881FCD64790091FC2E /* DBModelProtocol.swift */; }; + E76462891FCD64790091FC2E /* DBModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E76462881FCD64790091FC2E /* DBModel.swift */; }; E764628C1FCD67AC0091FC2E /* DBModelConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = E764628B1FCD67AC0091FC2E /* DBModelConvertible.swift */; }; E76491961F7A529D001E741C /* WheelContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E76491951F7A529D001E741C /* WheelContainer.swift */; }; E764919B1F7A5485001E741C /* MainWheelContainerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E76491991F7A5485001E741C /* MainWheelContainerDelegate.swift */; }; @@ -2431,7 +2467,6 @@ 00E98253205C2726008BF03D /* SessionFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionFooterView.swift; sourceTree = ""; }; 00E98255205C2740008BF03D /* SessionItemCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionItemCell.swift; sourceTree = ""; }; 00E98259205FC324008BF03D /* SessionDescView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDescView.swift; sourceTree = ""; }; - 00E9825B205FD351008BF03D /* AuthHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthHandler.swift; sourceTree = ""; }; 00E9825D205FDB1A008BF03D /* AuthExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthExtension.swift; sourceTree = ""; }; 00EB7871206E286A00E3FB03 /* WCReusableViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WCReusableViews.swift; sourceTree = ""; }; 00EBF72D0964E3EC64F5B966 /* SplashProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SplashProtocols.swift; sourceTree = ""; }; @@ -2508,9 +2543,9 @@ 26053122212741C2002E1CF1 /* LogOutputWireFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogOutputWireFrame.swift; sourceTree = ""; }; 2605312821298BEF002E1CF1 /* Logoutputcell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logoutputcell.swift; sourceTree = ""; }; 2605312A21299198002E1CF1 /* LogOutputDS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogOutputDS.swift; sourceTree = ""; }; - 260552A51F9E1CD100D68DE6 /* SearchHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SearchHandler.swift; path = Services/HandleServices/SearchHandler.swift; sourceTree = ""; }; 260629702056EF2800CB8F65 /* LinksCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinksCell.swift; sourceTree = ""; }; 2606F3BB20BFE20400CF7F15 /* MessageInteractor+Translation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MessageInteractor+Translation.swift"; sourceTree = ""; }; + 2611CEF62182090900FFD4DD /* LogWriterProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogWriterProtocol.swift; sourceTree = ""; }; 26131E01210399BA00BE94F9 /* TranscribeService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranscribeService.swift; sourceTree = ""; }; 26142B1020472ECD004E5FE4 /* MessageLinkTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageLinkTable.swift; sourceTree = ""; }; 26142B1220473BFD004E5FE4 /* DBMessageLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBMessageLink.swift; sourceTree = ""; }; @@ -2525,6 +2560,7 @@ 2625DBF720EFC5DE00E01C05 /* FourCharCode+StringLiteralConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FourCharCode+StringLiteralConvertible.swift"; sourceTree = ""; }; 2625F29E212463E8007C42B5 /* ProgressIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressIdentifier.swift; sourceTree = ""; }; 262D43862033417F002F1E45 /* FriendExtansion+BERT.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FriendExtansion+BERT.swift"; sourceTree = ""; }; + 26305F702180765E00400DB0 /* LoadDBConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = LoadDBConfig.xcconfig; sourceTree = ""; }; 2632139020D797F500C31144 /* TranslationViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranslationViewProtocol.swift; sourceTree = ""; }; 2633EF6D205212F700DB3868 /* MemberDAOProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemberDAOProtocol.swift; sourceTree = ""; }; 2633EF6F2052130400DB3868 /* MemberDAO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemberDAO.swift; sourceTree = ""; }; @@ -2785,7 +2821,7 @@ 3A8045CF1F60C8E200AED866 /* MQTTServiceProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MQTTServiceProfile.swift; sourceTree = ""; }; 3A8045D01F60C8E200AED866 /* MQTTServiceAuth.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MQTTServiceAuth.swift; sourceTree = ""; }; 3A8045D51F60C93D00AED866 /* MQTTServiceChat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MQTTServiceChat.swift; sourceTree = ""; }; - 3A8045D71F60C98200AED866 /* MQTTServiceHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MQTTServiceHelper.swift; sourceTree = ""; }; + 3A8045D71F60C98200AED866 /* MQTTService+Helper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MQTTService+Helper.swift"; sourceTree = ""; }; 3A8045D91F60E18E00AED866 /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Queue.swift; path = Library/Queue.swift; sourceTree = ""; }; 3A8C12DDFD0D831F21959665 /* Pods-Nynja-Share.devautotests.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nynja-Share.devautotests.xcconfig"; path = "Pods/Target Support Files/Pods-Nynja-Share/Pods-Nynja-Share.devautotests.xcconfig"; sourceTree = ""; }; 3AA13C751F2252F900BE5D8F /* SearchModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchModel.swift; sourceTree = ""; }; @@ -2816,7 +2852,17 @@ 499373C9A81A05B77308A5F0 /* AddContactByUsernameProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddContactByUsernameProtocols.swift; sourceTree = ""; }; 4B02130120372C5700650298 /* OtherItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OtherItemView.swift; sourceTree = ""; }; 4B0213032037331100650298 /* ScheduleMessageViewControllerConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleMessageViewControllerConstants.swift; sourceTree = ""; }; + 4B030F2B2195BFF300F293B7 /* AuthHandlerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthHandlerDelegate.swift; sourceTree = ""; }; + 4B030F2C2195BFF300F293B7 /* AuthHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthHandler.swift; sourceTree = ""; }; + 4B030F312195CD4500F293B7 /* MQTTServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MQTTServiceDelegate.swift; sourceTree = ""; }; + 4B030F352195CD9B00F293B7 /* MQTTService+QueuePool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MQTTService+QueuePool.swift"; sourceTree = ""; }; + 4B030F3A2195CF8100F293B7 /* Host.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Host.swift; sourceTree = ""; }; + 4B030F3D2195D88100F293B7 /* UserInfoImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInfoImpl.swift; sourceTree = ""; }; 4B052CAF203614D400BC2A9B /* StringAtomExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringAtomExtension.swift; sourceTree = ""; }; + 4B055C36219C313A001FE077 /* FileDownloaderFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileDownloaderFactory.swift; sourceTree = ""; }; + 4B055C38219C3146001FE077 /* FileDownloaderKind.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileDownloaderKind.swift; sourceTree = ""; }; + 4B055C3A219C4101001FE077 /* MQTTServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MQTTServiceProtocol.swift; sourceTree = ""; }; + 4B055C3E219C61A2001FE077 /* ProfileHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileHandlerDelegate.swift; sourceTree = ""; }; 4B058EFD204EA751004C7D9F /* ProfileDAO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileDAO.swift; sourceTree = ""; }; 4B058F00204EA7F5004C7D9F /* DBManagerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBManagerProtocol.swift; sourceTree = ""; }; 4B058F02204EA928004C7D9F /* DAOProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DAOProtocol.swift; sourceTree = ""; }; @@ -2836,6 +2882,8 @@ 4B06D31F2028A9B1003B275B /* P2pChatItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = P2pChatItemsFactory.swift; sourceTree = ""; }; 4B06D3212028A9C6003B275B /* GroupChatItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupChatItemsFactory.swift; sourceTree = ""; }; 4B06D3232028B209003B275B /* WCBaseItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WCBaseItemsFactory.swift; sourceTree = ""; }; + 4B0CC1FC2195B52000E0BA61 /* IoHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IoHandlerDelegate.swift; sourceTree = ""; }; + 4B0CC2012195B69900E0BA61 /* LinkHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkHandlerDelegate.swift; sourceTree = ""; }; 4B15A544CC681BABD1A631AF /* QRCodeGeneratorInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = QRCodeGeneratorInteractor.swift; sourceTree = ""; }; 4B1D7DF92029BF3400703228 /* HistoryItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryItemsFactory.swift; sourceTree = ""; }; 4B1D7DFB2029C37900703228 /* FavoritesItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesItemsFactory.swift; sourceTree = ""; }; @@ -2851,6 +2899,7 @@ 4B1D7E102029FF5000703228 /* Array+WheelItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+WheelItemModel.swift"; sourceTree = ""; }; 4B1D7E13202A0A0200703228 /* GroupMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupMode.swift; sourceTree = ""; }; 4B1F122F203C8DDE00D61D21 /* JobTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JobTable.swift; sourceTree = ""; }; + 4B2B1557218F5423000E4916 /* SharedColumn.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedColumn.swift; sourceTree = ""; }; 4B2D0639202DDA2000010A0C /* BackSwipable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackSwipable.swift; sourceTree = ""; }; 4B2D063B202E1A1500010A0C /* ContactsExpandedItemsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsExpandedItemsFactory.swift; sourceTree = ""; }; 4B348FF12163808300CCB0E3 /* DeletedIndexesCalculatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedIndexesCalculatorTests.swift; sourceTree = ""; }; @@ -2909,6 +2958,17 @@ 4B7E93372170D1BC001558CF /* RootNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootNavigationController.swift; sourceTree = ""; }; 4B7E933A2170D410001558CF /* ForwardSelectorWireFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForwardSelectorWireFrame.swift; sourceTree = ""; }; 4B7E933D2170D4FF001558CF /* ServiceFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceFactory.swift; sourceTree = ""; }; + 4B86C561219C12840006A192 /* DAO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DAO.swift; sourceTree = ""; }; + 4B87712E2192F6940014AD09 /* ColumnDefinitionExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColumnDefinitionExtension.swift; sourceTree = ""; }; + 4B877130219312570014AD09 /* ColumnExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColumnExtension.swift; sourceTree = ""; }; + 4B877132219314D50014AD09 /* TypedRequestExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypedRequestExtension.swift; sourceTree = ""; }; + 4B8771342193154E0014AD09 /* DBExtendedModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBExtendedModel.swift; sourceTree = ""; }; + 4B877136219315770014AD09 /* QueryInterfaceRequestExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryInterfaceRequestExtension.swift; sourceTree = ""; }; + 4B877138219315AA0014AD09 /* SercerModelConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SercerModelConvertible.swift; sourceTree = ""; }; + 4B87713A219328780014AD09 /* QueryArgs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryArgs.swift; sourceTree = ""; }; + 4B8771772195AC5B0014AD09 /* HistoryHandlerSubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryHandlerSubscriber.swift; sourceTree = ""; }; + 4B87717A2195AF9D0014AD09 /* TypingHandlerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypingHandlerDelegate.swift; sourceTree = ""; }; + 4B87717C2195AFF50014AD09 /* StaticDelegating.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticDelegating.swift; sourceTree = ""; }; 4B8996C7204ECE9B00DCB183 /* ContactDAO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactDAO.swift; sourceTree = ""; }; 4B8996C9204ECEA700DCB183 /* ContactDAOProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactDAOProtocol.swift; sourceTree = ""; }; 4B8996CC204ED33400DCB183 /* StarDAOProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StarDAOProtocol.swift; sourceTree = ""; }; @@ -2938,8 +2998,12 @@ 4BB0EFB82151347900704136 /* AlertImageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertImageViewController.swift; sourceTree = ""; }; 4BB0EFB92151347900704136 /* AlertImageViewControllerConstraints.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertImageViewControllerConstraints.swift; sourceTree = ""; }; 4BB0EFBA2151347900704136 /* AlertManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertManager.swift; sourceTree = ""; }; + 4BB35E22219AF46E0007C18E /* RosterRelatedQueryArgs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RosterRelatedQueryArgs.swift; sourceTree = ""; }; + 4BC8B38C2191AC360086DC6C /* ContactsProvidingFetchingArgs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsProvidingFetchingArgs.swift; sourceTree = ""; }; 4BDC7E60203492CA00BCD381 /* TopSwipable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopSwipable.swift; sourceTree = ""; }; 4BDC7E62203494C000BCD381 /* ScheduleButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleButton.swift; sourceTree = ""; }; + 4BDDAAE8218CA28300F775A7 /* HomeDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeDataProvider.swift; sourceTree = ""; }; + 4BDDAAEA218CA2C700F775A7 /* HomeDataProviderImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeDataProviderImpl.swift; sourceTree = ""; }; 4BE2C5D02142EAC500A73DD9 /* AudioPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioPlayer.swift; sourceTree = ""; }; 4BE2C5D22142EAC500A73DD9 /* AudioSessionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioSessionManager.swift; sourceTree = ""; }; 4BE2C5D42142EAC500A73DD9 /* AudioRecorder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioRecorder.swift; sourceTree = ""; }; @@ -2950,7 +3014,6 @@ 4BE2C5E12142EB0E00A73DD9 /* AudioManagerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioManagerDelegate.swift; sourceTree = ""; }; 4BE2C5E52142EB5A00A73DD9 /* NynjaCommunicatorService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NynjaCommunicatorService.swift; sourceTree = ""; }; 4BE2C5E62142EB5A00A73DD9 /* NynjaRingingService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NynjaRingingService.swift; sourceTree = ""; }; - 4BF090B421635B2E00DCCA5C /* LogServiceStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogServiceStub.swift; sourceTree = ""; }; 4BF090B821635B4700DCCA5C /* LogServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogServiceProtocol.swift; sourceTree = ""; }; 4BF090BA21635B6600DCCA5C /* LogServiceTopic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogServiceTopic.swift; sourceTree = ""; }; 4BF090C421635E8600DCCA5C /* Message+LinkedId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Message+LinkedId.swift"; sourceTree = ""; }; @@ -2958,6 +3021,7 @@ 4BF090CB21635FDC00DCCA5C /* Message+Type.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Message+Type.swift"; sourceTree = ""; }; 4BF2C3DF2188B27500E59F6C /* Injectable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Injectable.swift; sourceTree = ""; }; 4BF2C3EF2189F58F00E59F6C /* ServerSignal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSignal.swift; sourceTree = ""; }; + 4BF2C3FB218AFE9D00E59F6C /* FullNameRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullNameRepresentable.swift; sourceTree = ""; }; 4CDA2BE900351F21464CE687 /* DateTimePickerInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DateTimePickerInteractor.swift; sourceTree = ""; }; 4D247CBC45C1C1267BBBB289 /* QRCodeReaderInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = QRCodeReaderInteractor.swift; sourceTree = ""; }; 4F7C039B61A0663D43BE5AE5 /* SelectCountryProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SelectCountryProtocols.swift; sourceTree = ""; }; @@ -3409,6 +3473,7 @@ 9BD8E41220F3A2E2001384EC /* CallInProgressInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallInProgressInteractor.swift; sourceTree = ""; }; 9BE521212189B2E10070C664 /* ThreeButtonHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreeButtonHeaderView.swift; sourceTree = ""; }; 9BFFE61A2178DCFF004FE2CA /* BannerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BannerView.swift; sourceTree = ""; }; + 9C2E07BBF40570582F4258A3 /* Pods-Nynja.release-debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nynja.release-debug.xcconfig"; path = "Pods/Target Support Files/Pods-Nynja/Pods-Nynja.release-debug.xcconfig"; sourceTree = ""; }; 9C4192D925259B75441492A9 /* FavoritesPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = FavoritesPresenter.swift; sourceTree = ""; }; 9DE44A136617140435B23343 /* GroupStorageWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GroupStorageWireframe.swift; sourceTree = ""; }; 9E82188EE0AC1D1C05470692 /* Pods-NynjaUnitTests.channels.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NynjaUnitTests.channels.xcconfig"; path = "Pods/Target Support Files/Pods-NynjaUnitTests/Pods-NynjaUnitTests.channels.xcconfig"; sourceTree = ""; }; @@ -3572,7 +3637,6 @@ A4330A692109EA850060BD93 /* DatabaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseManager.swift; sourceTree = ""; }; A4330A6D2109EBA70060BD93 /* CountriesProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountriesProvider.swift; sourceTree = ""; }; A4330A702109EBB30060BD93 /* CountriesProviding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountriesProviding.swift; sourceTree = ""; }; - A4330A732109F0D40060BD93 /* StorageService+UserInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StorageService+UserInfo.swift"; sourceTree = ""; }; A433D9A020A5C18C00C946F9 /* ContactsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsProvider.swift; sourceTree = ""; }; A433D9A220A5C19600C946F9 /* ContactsProviding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsProviding.swift; sourceTree = ""; }; A438DB9120763AFB00AA86A2 /* Contact+Desc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Contact+Desc.swift"; sourceTree = ""; }; @@ -3828,6 +3892,7 @@ B28D1FE755A0457DBEDAC068 /* MapSearchProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = MapSearchProtocols.swift; sourceTree = ""; }; B2B221F69CB3D5C6A1B12456 /* VideoPreviewInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = VideoPreviewInteractor.swift; sourceTree = ""; }; B2EF4EFCD3C9DE0B69FC40F9 /* TimeZoneSelectorWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TimeZoneSelectorWireframe.swift; sourceTree = ""; }; + B3D64F28A0B75CE6D74100B1 /* Pods-Nynja-Share.release-debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nynja-Share.release-debug.xcconfig"; path = "Pods/Target Support Files/Pods-Nynja-Share/Pods-Nynja-Share.release-debug.xcconfig"; sourceTree = ""; }; B4DC9EF9D78D3F5C48B00EF3 /* ScheduleMessageWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ScheduleMessageWireframe.swift; sourceTree = ""; }; B62646CA6345B6C5AD0C87A0 /* ScheduleMessagePresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ScheduleMessagePresenter.swift; sourceTree = ""; }; B7121EB7205045F300AABBE6 /* MediaDownloadManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaDownloadManager.swift; sourceTree = ""; }; @@ -3934,10 +3999,12 @@ CBE3BAC9B7EA418FB463EF04 /* EditUsernameInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = EditUsernameInteractor.swift; sourceTree = ""; }; CCA291E1CE928BC100DD6353 /* Pods-Nynja-Share.translate.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nynja-Share.translate.xcconfig"; path = "Pods/Target Support Files/Pods-Nynja-Share/Pods-Nynja-Share.translate.xcconfig"; sourceTree = ""; }; CDF62E1A004579220E231142 /* LoginProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LoginProtocols.swift; sourceTree = ""; }; + CEBE788F463F17C3CCD8302A /* Pods-NynjaUnitTests.release-debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NynjaUnitTests.release-debug.xcconfig"; path = "Pods/Target Support Files/Pods-NynjaUnitTests/Pods-NynjaUnitTests.release-debug.xcconfig"; sourceTree = ""; }; D1AE7296B9A53355289740D1 /* ProfilePresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ProfilePresenter.swift; sourceTree = ""; }; D1D5302025583482829BBF2E /* GroupStorageViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GroupStorageViewController.swift; sourceTree = ""; }; D270F638DBB2D8FC1BDEB633 /* ProfileViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = ""; }; D4B94201A3DDCB11C62D6A16 /* AddContactViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddContactViewController.swift; sourceTree = ""; }; + D55BAC5832CAFB50A4F64387 /* Pods-Nynja.loaddb.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nynja.loaddb.xcconfig"; path = "Pods/Target Support Files/Pods-Nynja/Pods-Nynja.loaddb.xcconfig"; sourceTree = ""; }; D7956526150F4211DE78173E /* AddContactViaPhoneInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddContactViaPhoneInteractor.swift; sourceTree = ""; }; D8AC83D4F29DA35FEFDFBC65 /* QRCodeGeneratorViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = QRCodeGeneratorViewController.swift; sourceTree = ""; }; D8F8C8AFEB81C734967FE902 /* EditGroupNameViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = EditGroupNameViewController.swift; sourceTree = ""; }; @@ -4005,7 +4072,7 @@ E7598F651FA1D8B80082FBE7 /* ProfileContactCellLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProfileContactCellLayout.swift; sourceTree = ""; }; E761A0D81F8B8CF000C088E0 /* EditProfileViewControllerLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileViewControllerLayout.swift; sourceTree = ""; }; E761A0DB1F8B8F3900C088E0 /* NynjaButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NynjaButton.swift; sourceTree = ""; }; - E76462881FCD64790091FC2E /* DBModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBModelProtocol.swift; sourceTree = ""; }; + E76462881FCD64790091FC2E /* DBModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBModel.swift; sourceTree = ""; }; E764628B1FCD67AC0091FC2E /* DBModelConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBModelConvertible.swift; sourceTree = ""; }; E76491951F7A529D001E741C /* WheelContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WheelContainer.swift; sourceTree = ""; }; E76491991F7A5485001E741C /* MainWheelContainerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWheelContainerDelegate.swift; sourceTree = ""; }; @@ -4086,6 +4153,7 @@ EE1EF22666DC92AE739E2DA5 /* Pods-Nynja.stickers.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nynja.stickers.xcconfig"; path = "Pods/Target Support Files/Pods-Nynja/Pods-Nynja.stickers.xcconfig"; sourceTree = ""; }; EE2260535ED2762F80FA7A38 /* MapProtocols.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = MapProtocols.swift; sourceTree = ""; }; EE63F9D9E7D7D9B4CAF2FE90 /* TopUpAccountPresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TopUpAccountPresenter.swift; sourceTree = ""; }; + EF57CF149EF8224DA702DEB9 /* Pods-Nynja-Share.loaddb.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nynja-Share.loaddb.xcconfig"; path = "Pods/Target Support Files/Pods-Nynja-Share/Pods-Nynja-Share.loaddb.xcconfig"; sourceTree = ""; }; EF74AC6D4879E4DA38B3C352 /* AddContactByUsernameWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddContactByUsernameWireframe.swift; sourceTree = ""; }; EFF6A30BDE89BF7887B67DA0 /* ProfileInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ProfileInteractor.swift; sourceTree = ""; }; F01F3338177726EB10E0D040 /* LoginViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; }; @@ -4196,6 +4264,7 @@ F1D4A49F20762A1D00F31089 /* Configurable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configurable.swift; sourceTree = ""; }; F1EED41420C57C30001060C4 /* PhotoPreviewSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoPreviewSource.swift; sourceTree = ""; }; F1F219FC7966064C555AC2A4 /* TopUpAccountViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TopUpAccountViewController.swift; sourceTree = ""; }; + F437803397DA4D6AA4459469 /* Pods-NynjaUnitTests.loaddb.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NynjaUnitTests.loaddb.xcconfig"; path = "Pods/Target Support Files/Pods-NynjaUnitTests/Pods-NynjaUnitTests.loaddb.xcconfig"; sourceTree = ""; }; F46A5D92A279FA0A509DA508 /* Pods-NynjaUnitTests.translate.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NynjaUnitTests.translate.xcconfig"; path = "Pods/Target Support Files/Pods-NynjaUnitTests/Pods-NynjaUnitTests.translate.xcconfig"; sourceTree = ""; }; F56141F2CF85255940EA304F /* EditPhotoWireframe.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = EditPhotoWireframe.swift; sourceTree = ""; }; F79C9355E1AA4B373567F765 /* LanguageSettingsInteractor.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LanguageSettingsInteractor.swift; sourceTree = ""; }; @@ -5908,21 +5977,18 @@ isa = PBXGroup; children = ( A497F56520EFA517005CC60F /* Base */, + 4B8771752195ABE80014AD09 /* MessageHandler */, + 4B8771762195AC3C0014AD09 /* HistoryHandler */, + 4B8771792195AF7E0014AD09 /* TypingHandler */, + 4B0CC2002195B67D00E0BA61 /* LinkHandler */, + 4B055C3D219C618B001FE077 /* ProfileHandler */, 265AEA141FE9AFA600AC4806 /* MemberHandler.swift */, 269666171FB57963009E41C1 /* RoomHandler.swift */, - 3A771CA91F191B38008D968A /* ProfileHandler.swift */, - 3A19FEAC1F3B7F1D00ACE750 /* MessageHandler.swift */, - 855C9FE52125B4C0000E3429 /* MessageHandlerSubscriber.swift */, 3A2374D81F262A1600701045 /* ContactHandler.swift */, 3A237BCC1F30E5D400C42B6E /* RosterHandler.swift */, - 3A1EB9A41F3A848A00658E93 /* HistoryHandler.swift */, - 260552A51F9E1CD100D68DE6 /* SearchHandler.swift */, - 263D66321FE8D95100A509F8 /* TypingHandler.swift */, 26FA420F201821B400E6F6EC /* StarHandler.swift */, 0008E91A20333A38003E316E /* JobHandler.swift */, 855EF424202CCADB00541BE3 /* ExtendedStarHandler.swift */, - 00E9825B205FD351008BF03D /* AuthHandler.swift */, - A48C154320EF7A15002DA994 /* LinkHandler.swift */, ); name = Handlers; sourceTree = ""; @@ -5961,6 +6027,7 @@ 266AE8C2203496B60096A12C /* AsyncOperation.swift */, F112B18F20E0FBE800B06E3E /* AsyncBlockOperation.swift */, 263C04E82132E2FF00B8F0BE /* WrappedTaskOperation.swift */, + 4BF2C3FB218AFE9D00E59F6C /* FullNameRepresentable.swift */, ); name = Library; sourceTree = ""; @@ -6037,16 +6104,12 @@ 3A8045CC1F60C8E200AED866 /* MQTT */ = { isa = PBXGroup; children = ( - FBCE83D420E52396003B7558 /* MQTTServiceWallet.swift */, + 4B030F392195CF7600F293B7 /* Entities */, + 4B030F342195CD4A00F293B7 /* API */, + 4B030F382195CDDA00F293B7 /* Extensions */, 3A8045CD1F60C8E200AED866 /* MQTTService.swift */, - 3A8045D71F60C98200AED866 /* MQTTServiceHelper.swift */, - 3A8045D51F60C93D00AED866 /* MQTTServiceChat.swift */, - 3A8045CE1F60C8E200AED866 /* MQTTServiceFriend.swift */, - 3A8045CF1F60C8E200AED866 /* MQTTServiceProfile.swift */, - 3A8045D01F60C8E200AED866 /* MQTTServiceAuth.swift */, - 0008E9122032D5AC003E316E /* MQTTServiceSchedule.swift */, - 855EF422202CC85300541BE3 /* MQTTServiceStars.swift */, - A48C153E20EF765E002DA994 /* MQTTServiceLink.swift */, + 4B030F312195CD4500F293B7 /* MQTTServiceDelegate.swift */, + 4B055C3A219C4101001FE077 /* MQTTServiceProtocol.swift */, ); name = MQTT; path = Services/MQTT; @@ -6181,6 +6244,7 @@ 3A22662B1EFEF31E00D6A867 /* Assets.xcassets */, 6D36F8E41F0ADBD300FA1AC8 /* Localizable.strings */, F1313AFE20888CAB00E04092 /* DevConfig.xcconfig */, + 26305F702180765E00400DB0 /* LoadDBConfig.xcconfig */, F10AFE9A20EF8B9A00C7CE83 /* DevAutoTests.xcconfig */, F1313AFF20888CB800E04092 /* PrereleaseConfig.xcconfig */, F1313B0020888CC400E04092 /* ReleaseConfig.xcconfig */, @@ -6432,6 +6496,65 @@ path = DateTime; sourceTree = ""; }; + 4B030F2A2195BFF300F293B7 /* AuthHandler */ = { + isa = PBXGroup; + children = ( + 4B030F2B2195BFF300F293B7 /* AuthHandlerDelegate.swift */, + 4B030F2C2195BFF300F293B7 /* AuthHandler.swift */, + ); + path = AuthHandler; + sourceTree = ""; + }; + 4B030F342195CD4A00F293B7 /* API */ = { + isa = PBXGroup; + children = ( + FBCE83D420E52396003B7558 /* MQTTServiceWallet.swift */, + 3A8045D51F60C93D00AED866 /* MQTTServiceChat.swift */, + 3A8045CE1F60C8E200AED866 /* MQTTServiceFriend.swift */, + 3A8045CF1F60C8E200AED866 /* MQTTServiceProfile.swift */, + 3A8045D01F60C8E200AED866 /* MQTTServiceAuth.swift */, + 0008E9122032D5AC003E316E /* MQTTServiceSchedule.swift */, + 855EF422202CC85300541BE3 /* MQTTServiceStars.swift */, + A48C153E20EF765E002DA994 /* MQTTServiceLink.swift */, + ); + path = API; + sourceTree = ""; + }; + 4B030F382195CDDA00F293B7 /* Extensions */ = { + isa = PBXGroup; + children = ( + 3A8045D71F60C98200AED866 /* MQTTService+Helper.swift */, + 4B030F352195CD9B00F293B7 /* MQTTService+QueuePool.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 4B030F392195CF7600F293B7 /* Entities */ = { + isa = PBXGroup; + children = ( + 4B030F3A2195CF8100F293B7 /* Host.swift */, + ); + path = Entities; + sourceTree = ""; + }; + 4B055C35219C30F5001FE077 /* FileDownloaderFactory */ = { + isa = PBXGroup; + children = ( + 4B055C36219C313A001FE077 /* FileDownloaderFactory.swift */, + 4B055C38219C3146001FE077 /* FileDownloaderKind.swift */, + ); + path = FileDownloaderFactory; + sourceTree = ""; + }; + 4B055C3D219C618B001FE077 /* ProfileHandler */ = { + isa = PBXGroup; + children = ( + 3A771CA91F191B38008D968A /* ProfileHandler.swift */, + 4B055C3E219C61A2001FE077 /* ProfileHandlerDelegate.swift */, + ); + name = ProfileHandler; + sourceTree = ""; + }; 4B058EF9204EA6EE004C7D9F /* DB */ = { isa = PBXGroup; children = ( @@ -6564,6 +6687,24 @@ name = Chat; sourceTree = ""; }; + 4B0CC1FB2195B3C200E0BA61 /* IoHandler */ = { + isa = PBXGroup; + children = ( + 3A1DC73E1EF15B65006A8E9F /* IoHandler.swift */, + 4B0CC1FC2195B52000E0BA61 /* IoHandlerDelegate.swift */, + ); + path = IoHandler; + sourceTree = ""; + }; + 4B0CC2002195B67D00E0BA61 /* LinkHandler */ = { + isa = PBXGroup; + children = ( + A48C154320EF7A15002DA994 /* LinkHandler.swift */, + 4B0CC2012195B69900E0BA61 /* LinkHandlerDelegate.swift */, + ); + name = LinkHandler; + sourceTree = ""; + }; 4B0DBA892137F6F800D79163 /* ChatService */ = { isa = PBXGroup; children = ( @@ -6944,6 +7085,33 @@ path = ServiceFactory; sourceTree = ""; }; + 4B8771752195ABE80014AD09 /* MessageHandler */ = { + isa = PBXGroup; + children = ( + 3A19FEAC1F3B7F1D00ACE750 /* MessageHandler.swift */, + 855C9FE52125B4C0000E3429 /* MessageHandlerSubscriber.swift */, + ); + name = MessageHandler; + sourceTree = ""; + }; + 4B8771762195AC3C0014AD09 /* HistoryHandler */ = { + isa = PBXGroup; + children = ( + 3A1EB9A41F3A848A00658E93 /* HistoryHandler.swift */, + 4B8771772195AC5B0014AD09 /* HistoryHandlerSubscriber.swift */, + ); + name = HistoryHandler; + sourceTree = ""; + }; + 4B8771792195AF7E0014AD09 /* TypingHandler */ = { + isa = PBXGroup; + children = ( + 263D66321FE8D95100A509F8 /* TypingHandler.swift */, + 4B87717A2195AF9D0014AD09 /* TypingHandlerDelegate.swift */, + ); + name = TypingHandler; + sourceTree = ""; + }; 4B8996C6204ECE8500DCB183 /* Contact */ = { isa = PBXGroup; children = ( @@ -7072,6 +7240,23 @@ path = AlertImageViewController; sourceTree = ""; }; + 4BC8B38B2191AC200086DC6C /* Entity */ = { + isa = PBXGroup; + children = ( + 4BC8B38C2191AC360086DC6C /* ContactsProvidingFetchingArgs.swift */, + ); + name = Entity; + sourceTree = ""; + }; + 4BDDAAE7218CA27200F775A7 /* HomeDataProvider */ = { + isa = PBXGroup; + children = ( + 4BDDAAE8218CA28300F775A7 /* HomeDataProvider.swift */, + 4BDDAAEA218CA2C700F775A7 /* HomeDataProviderImpl.swift */, + ); + path = HomeDataProvider; + sourceTree = ""; + }; 4BE2C5CE2142EAC500A73DD9 /* Audio */ = { isa = PBXGroup; children = ( @@ -7144,8 +7329,8 @@ isa = PBXGroup; children = ( 4B7C73EF215A5508007924DB /* LogService.swift */, - 4BF090B421635B2E00DCCA5C /* LogServiceStub.swift */, 4BF090B821635B4700DCCA5C /* LogServiceProtocol.swift */, + 2611CEF62182090900FFD4DD /* LogWriterProtocol.swift */, 4BF090BA21635B6600DCCA5C /* LogServiceTopic.swift */, ); path = LogService; @@ -7207,6 +7392,7 @@ 50D9FF79FEBFA4AFE9A2791A /* Interactor */ = { isa = PBXGroup; children = ( + 4BDDAAE7218CA27200F775A7 /* HomeDataProvider */, EFF6A30BDE89BF7887B67DA0 /* ProfileInteractor.swift */, ); path = Interactor; @@ -7538,9 +7724,15 @@ 61CB12AA514912C6B8E4F670 /* Pods-Nynja.devautotests.xcconfig */, 3A8C12DDFD0D831F21959665 /* Pods-Nynja-Share.devautotests.xcconfig */, CB70AD73977CD00AD11C287C /* Pods-NynjaUnitTests.devautotests.xcconfig */, + D55BAC5832CAFB50A4F64387 /* Pods-Nynja.loaddb.xcconfig */, + EF57CF149EF8224DA702DEB9 /* Pods-Nynja-Share.loaddb.xcconfig */, + F437803397DA4D6AA4459469 /* Pods-NynjaUnitTests.loaddb.xcconfig */, 7ADCB0C891B31AF691307B4F /* Pods-Nynja.spotify.xcconfig */, 7F7FC209C7703E3E7617D782 /* Pods-Nynja-Share.spotify.xcconfig */, A0A57BD401783039D49B7B75 /* Pods-NynjaUnitTests.spotify.xcconfig */, + 9C2E07BBF40570582F4258A3 /* Pods-Nynja.release-debug.xcconfig */, + B3D64F28A0B75CE6D74100B1 /* Pods-Nynja-Share.release-debug.xcconfig */, + CEBE788F463F17C3CCD8302A /* Pods-NynjaUnitTests.release-debug.xcconfig */, ); name = Pods; sourceTree = ""; @@ -9977,7 +10169,7 @@ isa = PBXGroup; children = ( A4330A642109DFA00060BD93 /* UserInfo.swift */, - A4330A732109F0D40060BD93 /* StorageService+UserInfo.swift */, + 4B030F3D2195D88100F293B7 /* UserInfoImpl.swift */, ); name = UserInfo; sourceTree = ""; @@ -9994,6 +10186,7 @@ A433D99F20A5C17600C946F9 /* ContactsProvider */ = { isa = PBXGroup; children = ( + 4BC8B38B2191AC200086DC6C /* Entity */, A433D9A220A5C19600C946F9 /* ContactsProviding.swift */, A433D9A020A5C18C00C946F9 /* ContactsProvider.swift */, ); @@ -10203,6 +10396,7 @@ A4166F5F205FEA120008F231 /* MessageLink.swift */, 26588E6620A20E49000D3E1A /* Customizable.swift */, 2651093E20ADB81100F1B38B /* NotificationSettingProtocol.swift */, + 4B877138219315AA0014AD09 /* SercerModelConvertible.swift */, ); path = ServerModel; sourceTree = ""; @@ -10815,6 +11009,7 @@ A49E1BCA20A9A6880074DFD3 /* Library */ = { isa = PBXGroup; children = ( + 4B87717C2195AFF50014AD09 /* StaticDelegating.swift */, A4C92FFC20B323B600D6FB0F /* Extensions */, A49E1BCB20A9A68D0074DFD3 /* Models */, ); @@ -10869,8 +11064,9 @@ isa = PBXGroup; children = ( A497F56C20EFA86F005CC60F /* Base */, + 4B0CC1FB2195B3C200E0BA61 /* IoHandler */, + 4B030F2A2195BFF300F293B7 /* AuthHandler */, A497F56D20EFA8B6005CC60F /* ErrorsHandler.swift */, - 3A1DC73E1EF15B65006A8E9F /* IoHandler.swift */, ); path = Handlers; sourceTree = ""; @@ -11920,10 +12116,13 @@ E7417E961FBED8FD00E5C124 /* DB */ = { isa = PBXGroup; children = ( + 4B86C561219C12840006A192 /* DAO.swift */, E764628D1FCD68020091FC2E /* Protocols */, E74FD69B1FC5D04A00656611 /* Extensions */, E73CEF0F1FC2F52200246066 /* Models */, E7417E971FBED90600E5C124 /* Tables */, + 4B87713A219328780014AD09 /* QueryArgs.swift */, + 4BB35E22219AF46E0007C18E /* RosterRelatedQueryArgs.swift */, ); path = DB; sourceTree = ""; @@ -12020,9 +12219,13 @@ children = ( E745A24A20061AD400D7EF42 /* DatabaseExtension.swift */, E70938401FBEE488006CCDC6 /* TableDefinitionExtension.swift */, + 4B87712E2192F6940014AD09 /* ColumnDefinitionExtension.swift */, 8509FC802158E11000734D93 /* TableAlternationExtension.swift */, E74FD69C1FC5D06200656611 /* TransactionObserverExtension.swift */, A48BF1C120A1CA390076D892 /* Array+Table.swift */, + 4B877130219312570014AD09 /* ColumnExtension.swift */, + 4B877132219314D50014AD09 /* TypedRequestExtension.swift */, + 4B877136219315770014AD09 /* QueryInterfaceRequestExtension.swift */, ); path = Extensions; sourceTree = ""; @@ -12110,7 +12313,8 @@ children = ( E7C9CEC91FCC27A30090C2E0 /* FeedProtocol.swift */, E7AE41651FCC498800C3ED5D /* FeedType.swift */, - E76462881FCD64790091FC2E /* DBModelProtocol.swift */, + E76462881FCD64790091FC2E /* DBModel.swift */, + 4B8771342193154E0014AD09 /* DBExtendedModel.swift */, ); path = Base; sourceTree = ""; @@ -12260,6 +12464,7 @@ E7417E981FBED91100E5C124 /* Table.swift */, 85B0013121270DEC000C89FE /* TableOrder.swift */, E709383E1FBEE41D006CCDC6 /* Describable.swift */, + 4B2B1557218F5423000E4916 /* SharedColumn.swift */, ); path = Base; sourceTree = ""; @@ -12844,6 +13049,7 @@ F11786EF20AC5474007A9A1B /* ServiceFactory */ = { isa = PBXGroup; children = ( + 4B055C35219C30F5001FE077 /* FileDownloaderFactory */, F11786F020AC5482007A9A1B /* ServiceFactory.swift */, ); name = ServiceFactory; @@ -14494,6 +14700,7 @@ A42CE60420692EDB000889CC /* serviceTask_Spec.swift in Sources */, 264FFA981FC5917D0028243D /* WheelItemModel.swift in Sources */, 8503B51C20503B4B006F0593 /* NynjaCellButton.swift in Sources */, + 4B0CC1FE2195B52000E0BA61 /* IoHandlerDelegate.swift in Sources */, A4F3DAB32084949F00FF71C7 /* MessageExtension+BERT.swift in Sources */, 26B32B941FE20B9700888A0A /* mucExtension+BERT.swift in Sources */, A42CE5B620692EDB000889CC /* Room_Spec.swift in Sources */, @@ -14505,6 +14712,7 @@ A4F3DAA42084935400FF71C7 /* Constants.swift in Sources */, E7ED35131FB33806008B5704 /* CellModel.swift in Sources */, A42CE55220692EDB000889CC /* act.swift in Sources */, + 4B055C3C219C4119001FE077 /* MQTTServiceProtocol.swift in Sources */, A42CE5DA20692EDB000889CC /* p2p_Spec.swift in Sources */, A4A2424F2060390000B0A804 /* Handlers.swift in Sources */, 2610D4642076516900E6E2B2 /* Array+Feature.swift in Sources */, @@ -14534,6 +14742,7 @@ A415132420DBD5C400C2C01F /* Link.swift in Sources */, 35B1AB8E1FA3470500E65233 /* NavigateProtocol.swift in Sources */, A42CE55C20692EDB000889CC /* error.swift in Sources */, + 4B030F3F2195D88100F293B7 /* UserInfoImpl.swift in Sources */, 267D465C20AB4CD500D42242 /* AuthExtension.swift in Sources */, A42CE5F420692EDB000889CC /* Loc_Spec.swift in Sources */, A42CE57A20692EDB000889CC /* serviceTask.swift in Sources */, @@ -14566,8 +14775,7 @@ 4BF090CD21635FED00DCCA5C /* Message+Type.swift in Sources */, 26A856222074C4F000C642EA /* ActionsView+Layout.swift in Sources */, 266AE8C42034971A0096A12C /* AsyncOperation.swift in Sources */, - 00E9825F205FE86E008BF03D /* AuthHandler.swift in Sources */, - 359EB27C1F9A2C8C00147437 /* MQTTServiceHelper.swift in Sources */, + 359EB27C1F9A2C8C00147437 /* MQTTService+Helper.swift in Sources */, 4B5A0B74216E3BDD002C4160 /* ForwardAvatarViewModel.swift in Sources */, A4B544F020EFB4EC00EB7B0F /* BertTupleExtension.swift in Sources */, 4BF090CF2163601400DCCA5C /* Message+LinkedId.swift in Sources */, @@ -14608,6 +14816,7 @@ A42CE5E820692EDB000889CC /* Auth_Spec.swift in Sources */, A415132320DBD5C100C2C01F /* Link_Spec.swift in Sources */, A42CE5C620692EDB000889CC /* reader_Spec.swift in Sources */, + 4B030F372195CD9B00F293B7 /* MQTTService+QueuePool.swift in Sources */, 359EB2831F9A2E6A00147437 /* ProfileHandler.swift in Sources */, A42CE5A820692EDB000889CC /* Star.swift in Sources */, A42CE5C020692EDB000889CC /* ok_Spec.swift in Sources */, @@ -14616,10 +14825,10 @@ A42CE59420692EDB000889CC /* Tag.swift in Sources */, A42CE5A020692EDB000889CC /* ok.swift in Sources */, 267D465720AB45E200D42242 /* Customizable.swift in Sources */, + 4B030F332195CD4500F293B7 /* MQTTServiceDelegate.swift in Sources */, A42CE60820692EDB000889CC /* task_Spec.swift in Sources */, A43B25B220AB1E2600FF8107 /* ImagePlaceholderField.swift in Sources */, A43B25B120AB1E1B00FF8107 /* NynjaSearchField.swift in Sources */, - A4330A752109F0D40060BD93 /* StorageService+UserInfo.swift in Sources */, 26C0C1CA2073C93400C530DA /* ForwardContent.swift in Sources */, 263529132075725200DC6FBD /* SendJob.swift in Sources */, 35B98F9B1F9B956F009B8DEC /* StringExtensions.swift in Sources */, @@ -14631,6 +14840,8 @@ A42CE5B020692EDB000889CC /* TypeSpec.swift in Sources */, 26B32B971FE20BB500888A0A /* DescExtension+BERT.swift in Sources */, A42CE54220692EDA000889CC /* muc.swift in Sources */, + 4B030F3C2195CF8100F293B7 /* Host.swift in Sources */, + 2611CEF82182090900FFD4DD /* LogWriterProtocol.swift in Sources */, A45F115620B4226600F45004 /* RawRepresentable+Localized.swift in Sources */, 4BD53BF4202C8BCA00569C1A /* AVURLAsset+Duration.swift in Sources */, B767F48D215D1C2500FA9B27 /* ComingSoonProtocol.swift in Sources */, @@ -14678,6 +14889,7 @@ 8551CF0F2170857300829CF1 /* Message+Construct.swift in Sources */, A42CE57620692EDB000889CC /* push.swift in Sources */, A42CE55020692EDB000889CC /* Feature.swift in Sources */, + 4B030F302195BFF300F293B7 /* AuthHandler.swift in Sources */, 35B1AB831F9FB0DD00E65233 /* AmazonManager.swift in Sources */, 26B32B911FE20B7A00888A0A /* p2pExtension+BERT.swift in Sources */, 262D438820335225002F1E45 /* FriendRequstModel.swift in Sources */, @@ -14700,6 +14912,7 @@ A497F56E20EFA8B6005CC60F /* ErrorsHandler.swift in Sources */, 263D66311FE8D30200A509F8 /* TypingExtension+BERT.swift in Sources */, A42CE56220692EDB000889CC /* cur.swift in Sources */, + 4B0CC1FF2195B58000E0BA61 /* StaticDelegating.swift in Sources */, E7A77FDB1FACC58A004AE609 /* KeychainService.swift in Sources */, 85D669E720BD959800FBD803 /* Int+AnyObject.swift in Sources */, A42CE58420692EDB000889CC /* Vox.swift in Sources */, @@ -14758,6 +14971,7 @@ A4330A6F2109EBA70060BD93 /* CountriesProvider.swift in Sources */, A42CE54820692EDB000889CC /* reader.swift in Sources */, A42CE60A20692EDB000889CC /* Index_Spec.swift in Sources */, + 4BF2C3FD218AFF6300E59F6C /* FullNameRepresentable.swift in Sources */, 26352916207572AA00DC6FBD /* JobExtension.swift in Sources */, 8566BB12215BC39D00320E15 /* FetchType.swift in Sources */, 8520040120D4672E007C0036 /* StickerPack.swift in Sources */, @@ -14800,6 +15014,7 @@ 4B8AB705215CE52300C69DE1 /* SequenceExtension.swift in Sources */, 26C0C1EF2073DE2600C530DA /* ForwardSelectorProtocols+ShareExt.swift in Sources */, 26A8563A20750B5D00C642EA /* ScheduleInfo.swift in Sources */, + 4B030F2E2195BFF300F293B7 /* AuthHandlerDelegate.swift in Sources */, A45F115420B4224100F45004 /* RoomExtension.swift in Sources */, 26C0C1DC2073D94E00C530DA /* P2pExtension.swift in Sources */, 4B5A0B73216E3BDD002C4160 /* ActionsView.swift in Sources */, @@ -14826,6 +15041,7 @@ 6D6234F61F1E150600EF375F /* HistoryTableDS.swift in Sources */, FBE3885D2118849000149721 /* AlertActionWrapper.swift in Sources */, F119E66E20D24BBF0043A532 /* MultiplePreviewWireframe.swift in Sources */, + 2611CEF72182090900FFD4DD /* LogWriterProtocol.swift in Sources */, 2648C40F2069B52100863614 /* ChangeNumberStep3Presenter.swift in Sources */, A42D51A0206A361400EEB952 /* reader.swift in Sources */, A42D52BB206A53AA00EEB952 /* Vox_Spec.swift in Sources */, @@ -14840,6 +15056,7 @@ 262D43872033417F002F1E45 /* FriendExtansion+BERT.swift in Sources */, 2600CCC1216D47DC00EDC9C3 /* OptionallyActionCellViewModel.swift in Sources */, FE58F9B1208F00FE004AFDD3 /* MessageEditActionTable.swift in Sources */, + 4B87713B219328780014AD09 /* QueryArgs.swift in Sources */, F105C6A0209F71BF0091786A /* CameraInteractor.swift in Sources */, 4B4266BA204D898900194BC1 /* ForwardSelectorDisplayMode.swift in Sources */, 8572C3B92092364C00E4840C /* StickerPackageDataSource.swift in Sources */, @@ -14854,7 +15071,7 @@ 4B1D7E112029FF5000703228 /* Array+WheelItemModel.swift in Sources */, A46C36342121999100172773 /* DDMechanism.swift in Sources */, 854A4B302080D6C400759152 /* CellWithImageTableViewCell.swift in Sources */, - 3A8045D81F60C98200AED866 /* MQTTServiceHelper.swift in Sources */, + 3A8045D81F60C98200AED866 /* MQTTService+Helper.swift in Sources */, 8E9601971FF2EC8100E0C21D /* GroupFilesListVC.swift in Sources */, 4B06D3222028A9C6003B275B /* GroupChatItemsFactory.swift in Sources */, A4F3DABE2084990C00FF71C7 /* Roster+DB.swift in Sources */, @@ -14897,6 +15114,7 @@ 3A1F74FA1F5ED344009A11E4 /* PushService.swift in Sources */, FEA656042167777F00B44029 /* WalletBalancesInteractor.swift in Sources */, 261F2E2E200EB0AD007D0813 /* RepliesVC+CellDelegate.swift in Sources */, + 4B0CC1FD2195B52000E0BA61 /* IoHandlerDelegate.swift in Sources */, A45F110620B4218D00F45004 /* MessageConfiguration.swift in Sources */, 26F5C8BE206BD49B003A7FF5 /* DefaultActionItemModel.swift in Sources */, 4B0213042037331100650298 /* ScheduleMessageViewControllerConstants.swift in Sources */, @@ -15169,6 +15387,7 @@ FEA6560E2167797E00B44029 /* WalletFundingNetworkRouter.swift in Sources */, 3AE0A84B1F20321A008A04F3 /* Wheel.swift in Sources */, 00E4A65F201A287100CEC61F /* MapSearchDS.swift in Sources */, + 4B055C3F219C61A2001FE077 /* ProfileHandlerDelegate.swift in Sources */, 2603139D20A0A4BA009AC66D /* ChatLanguageSettingsProtocols.swift in Sources */, 26142B1120472ECD004E5FE4 /* MessageLinkTable.swift in Sources */, A4679BAC20B2DD100021FE9C /* SubscribersSelectorViewController.swift in Sources */, @@ -15205,6 +15424,7 @@ 267BE2AF1FE13AB600C47E18 /* ParticipantsPresenter.swift in Sources */, 2648C4112069B52100863614 /* ChangeNumberCodeView.swift in Sources */, 35F2DA611F73CAD400777920 /* NotificationManager.swift in Sources */, + 4B86C562219C12840006A192 /* DAO.swift in Sources */, 26C1A3ED2031D3030009F7F0 /* OtherUserContainerViewController.swift in Sources */, FB16E79920EFAFA8009FA203 /* Money.swift in Sources */, BA982E458F95A7A5AB4A8A73 /* TutorialViewController.swift in Sources */, @@ -15220,6 +15440,7 @@ 2C95C06FF47CF32C3B35FF4F /* TutorialInteractor.swift in Sources */, 8EBFF1692004033F00CC4C25 /* GroupAudiosCell.swift in Sources */, 855AC540208E45AA00DC2335 /* StickerCellModel.swift in Sources */, + 4B877131219312570014AD09 /* ColumnExtension.swift in Sources */, 3A2A99831EFAD2FB002749B3 /* PageControl.swift in Sources */, E784388156A5228026955A54 /* TutorialWireframe.swift in Sources */, E73315F21FB0BB0300C273FF /* Array+Contact.swift in Sources */, @@ -15252,6 +15473,7 @@ FEA656052167777F00B44029 /* WalletBalancesWallet.swift in Sources */, A45F112E20B4218D00F45004 /* MessageContentProtocol.swift in Sources */, 0008E9132032D5AC003E316E /* MQTTServiceSchedule.swift in Sources */, + 4B8771782195AC5B0014AD09 /* HistoryHandlerSubscriber.swift in Sources */, A432CF1A20B4347D00993AFB /* MaterialTextInput.swift in Sources */, B77C11E62109254800CCB42E /* InterpretationTypePresenter.swift in Sources */, B750EF062046D7C700A99F9C /* SpeedStringRepresentable.swift in Sources */, @@ -15308,7 +15530,6 @@ 8502DB512061030100613C8C /* WheelPositionPickerProtocols.swift in Sources */, 00F7B3402029DD6200E443E1 /* TextItemView.swift in Sources */, E77764BE1FBDA9B60042541D /* ImageWheelItemView.swift in Sources */, - A4330A742109F0D40060BD93 /* StorageService+UserInfo.swift in Sources */, FBCE841520E525A6003B7558 /* URLRequestConvertible.swift in Sources */, 264638271FFFE7F9002590E6 /* RepliesPresenter.swift in Sources */, E7C36C391FC46A9E00740630 /* ServiceExtension.swift in Sources */, @@ -15464,6 +15685,7 @@ 2605311B212740FD002E1CF1 /* LogOutputProtocols.swift in Sources */, FBCE841420E525A6003B7558 /* NetworkService.swift in Sources */, A409B1CF2108D48E0051C20B /* QueryFactory.swift in Sources */, + 4B87717D2195AFF50014AD09 /* StaticDelegating.swift in Sources */, A42D52B7206A53AA00EEB952 /* reader_Spec.swift in Sources */, F119E66A20D24B960043A532 /* MultiplePreviewProtocols.swift in Sources */, 850FC611203312FA00832D87 /* ForwardSelectorViewControllerLayout.swift in Sources */, @@ -15552,7 +15774,6 @@ 8584C90F20920F3C001A0BBB /* StickerGridCellModel.swift in Sources */, A42D51A4206A361400EEB952 /* Feature.swift in Sources */, 26D6D227212EDA6600EA2419 /* ConvertMessageDAOProtocol.swift in Sources */, - 260552A61F9E1CD100D68DE6 /* SearchHandler.swift in Sources */, F1607B1F20B21A9D00BDF60A /* CameraViewController.swift in Sources */, 853E595B20D71E6C007799B9 /* StickerPack+DB.swift in Sources */, A4CE80C720C95EE000400713 /* CollectionState.swift in Sources */, @@ -15578,7 +15799,7 @@ 8ED0F3D01FBC5CF2004916AB /* GroupsListTableDS.swift in Sources */, 260225DD20F379EF004FC238 /* MessageConvertionView.swift in Sources */, A45F111F20B4218D00F45004 /* InfoView.swift in Sources */, - E76462891FCD64790091FC2E /* DBModelProtocol.swift in Sources */, + E76462891FCD64790091FC2E /* DBModel.swift in Sources */, A418DA3120ED092F00FE780B /* InfoChannelView.swift in Sources */, E71AB31E1F70188C00A0CF5A /* WheelItemViewFactory.swift in Sources */, 8580BACE20BD98CF00239D9D /* UpdateResult.swift in Sources */, @@ -15587,6 +15808,7 @@ 26DCB2412064B9B1001EF0AB /* InviteFriendHeaderViewLayout.swift in Sources */, F105C6BB20A1347E0091786A /* PhotoPreviewPresenter.swift in Sources */, FEA655FA2167777F00B44029 /* TransferDetailsPresenter.swift in Sources */, + 4B87717B2195AF9D0014AD09 /* TypingHandlerDelegate.swift in Sources */, 8ED0F3CE1FBC5CF2004916AB /* GroupsListInteractor.swift in Sources */, 267BE2851FDE983400C47E18 /* SettingsGroupVC.swift in Sources */, 8580BAC720BD983400239D9D /* MentionFetchProtocols.swift in Sources */, @@ -15651,6 +15873,7 @@ A4688DFC20652DE30013660D /* StorageChange.swift in Sources */, 5683555B8382F7F37FEE1AF5 /* ProfileWireframe.swift in Sources */, A42D51AC206A361400EEB952 /* Auth.swift in Sources */, + 4B877139219315AA0014AD09 /* SercerModelConvertible.swift in Sources */, A42D52CD206A53AB00EEB952 /* chain_Spec.swift in Sources */, 8580BACA20BD983400239D9D /* MentionTransitionProtocol.swift in Sources */, A44B4D5B20CE9BDF00CA700A /* ImageCell.swift in Sources */, @@ -15666,6 +15889,7 @@ 8572C3BB2092366100E4840C /* StickerCollectionDataSource.swift in Sources */, E735853D1F6C2705003354B5 /* Geometry.swift in Sources */, 85B0013421272694000C89FE /* MessageInteractor+History.swift in Sources */, + 4B030F2F2195BFF300F293B7 /* AuthHandler.swift in Sources */, F119E67720D27E990043A532 /* ImagePreviewCVCell.swift in Sources */, 260313A720A0A4BA009AC66D /* ChatLanguageSettingsTableDataSource.swift in Sources */, 260313A520A0A4BA009AC66D /* BaseCell.swift in Sources */, @@ -15702,16 +15926,20 @@ A45F112220B4218D00F45004 /* MessageVideoView.swift in Sources */, A42D51A8206A361400EEB952 /* Index.swift in Sources */, 859B862D204820DC003272B2 /* ThemePickerViewController.swift in Sources */, + 4BDDAAE9218CA28300F775A7 /* HomeDataProvider.swift in Sources */, 855AC533208E441500DC2335 /* StickersInputViewController.swift in Sources */, 00F7B34A202B350A00E443E1 /* TimeZoneManager.swift in Sources */, A94B03A70E016BDA759B0703 /* EditProfileViewController.swift in Sources */, 8562853920D166E5000C9739 /* CollectionPreviewState.swift in Sources */, 4B7C73F2215A5509007924DB /* MotionManager.swift in Sources */, + 4B030F322195CD4500F293B7 /* MQTTServiceDelegate.swift in Sources */, F11786BB20A8A63F007A9A1B /* CoordinatorProtocol.swift in Sources */, F105C6BE20A1347E0091786A /* PhotoPreviewInteractor.swift in Sources */, F18AEAFD20C15792004FE01C /* SelectAvatarCoordinator.swift in Sources */, 85D66A1220BD965300FBD803 /* UserMentionTableViewCell.swift in Sources */, + 4BB35E23219AF46E0007C18E /* RosterRelatedQueryArgs.swift in Sources */, 2657BE51201233E300F21935 /* ImageFilledItemModel.swift in Sources */, + 4B030F3E2195D88100F293B7 /* UserInfoImpl.swift in Sources */, 85BA176120BEA7BD001EF8AC /* StickerPreviewContainerView.swift in Sources */, 85CB25DF20D7325500D5E565 /* StickerPackExtension.swift in Sources */, 00E8513B2021E96E007DC792 /* GApiResponse.swift in Sources */, @@ -15743,6 +15971,7 @@ 85458CED212D74B400BA8814 /* P2P+Opponent.swift in Sources */, E79061B41FBF10AA009FD83A /* MessageTable.swift in Sources */, 00EB7872206E286A00E3FB03 /* WCReusableViews.swift in Sources */, + 4BC8B38D2191AC360086DC6C /* ContactsProvidingFetchingArgs.swift in Sources */, 8ED0F3C11FBC5CB1004916AB /* Contact+DialogCellModel.swift in Sources */, 8EE9BC1A1FFE79FF00ECBBC7 /* GroupStorageCell.swift in Sources */, FEA655E02167777E00B44029 /* SeedBackupWalletPresenter.swift in Sources */, @@ -15793,6 +16022,7 @@ A4679B8920B2DA550021FE9C /* Array+ChannelSubscriber.swift in Sources */, A4ED79AC20C7056C00A41F67 /* AllChannelsItemsFactory.swift in Sources */, E707C4AF1FA0F6E700B86137 /* ProfileActionCell.swift in Sources */, + 4B877137219315770014AD09 /* QueryInterfaceRequestExtension.swift in Sources */, 2648C4172069B52100863614 /* ChangeNumberStep2Wireframe.swift in Sources */, A45F111A20B4218D00F45004 /* MessageImageView.swift in Sources */, 8520040420D4F311007C0036 /* StickerPreviewAnimator.swift in Sources */, @@ -15934,6 +16164,7 @@ 00102F42202C914400A877A9 /* NynjaCalendarCell.swift in Sources */, 853FB0662049B193000996C5 /* SupportPresenter.swift in Sources */, 267BE2BA1FE13AB600C47E18 /* ParticipantsWireframe.swift in Sources */, + 4B8771352193154E0014AD09 /* DBExtendedModel.swift in Sources */, F11786CC20A8E4FD007A9A1B /* CameraVideoPreviewViewController.swift in Sources */, A408A0BA20C174040029F54B /* ChannelsListPresenter.swift in Sources */, 850FC60F203310D200832D87 /* SelectionAvatarView.swift in Sources */, @@ -16027,6 +16258,7 @@ 8524C4D6217772C8003BF374 /* Member+Construct.swift in Sources */, A44B4D5A20CE9BDF00CA700A /* SwitchCell.swift in Sources */, F1607B3020B2FD5A00BDF60A /* QRNotificationVIew.swift in Sources */, + 4BF2C3FC218AFE9D00E59F6C /* FullNameRepresentable.swift in Sources */, FEA655CB2167777E00B44029 /* SeedVerificationWalletPresenter.swift in Sources */, 260313A420A0A4BA009AC66D /* DirectableActionCellViewModel.swift in Sources */, 859773232087965700B03B4A /* NynjaControlContainerView.swift in Sources */, @@ -16037,6 +16269,7 @@ FEA655DD2167777E00B44029 /* WalletDetailsProtocols.swift in Sources */, A43B259820AB1DFA00FF8107 /* TextInputContent.swift in Sources */, E723FD221F9E59A600E0B602 /* ProfileSection.swift in Sources */, + 4B030F2D2195BFF300F293B7 /* AuthHandlerDelegate.swift in Sources */, 4B752B652163A64300E852B9 /* Desc+Place.swift in Sources */, 8524C4D22177713C003BF374 /* Member+Status.swift in Sources */, E77D58A01F98C38100FBE926 /* ProfileSectionHeaderView.swift in Sources */, @@ -16069,6 +16302,7 @@ 5BC1D38120D3B54B002A44B3 /* CallInfoView.swift in Sources */, 264638291FFFE835002590E6 /* RepliesInteractor.swift in Sources */, 85D66A2220BD970400FBD803 /* BBCodeElement.swift in Sources */, + 4B87712F2192F6940014AD09 /* ColumnDefinitionExtension.swift in Sources */, B723C634204DA54200884FFD /* SettingsDataAndStorageTableDataSource.swift in Sources */, FEA655E72167777E00B44029 /* SeedBackupWalletOutputParams.swift in Sources */, 851EBD7F20B418890065C644 /* StickersInputView.swift in Sources */, @@ -16195,6 +16429,7 @@ A43B25D720AB1EE400FF8107 /* NewChannelWireFrame.swift in Sources */, A4166F5C205FE3670008F231 /* JobService.swift in Sources */, 26F03C0D20698B0000712CB0 /* ChatWheelItemModel.swift in Sources */, + 4B0CC2022195B69900E0BA61 /* LinkHandlerDelegate.swift in Sources */, E4F62F7771D4BB7FE3B48FA2 /* VideoPreviewProtocols.swift in Sources */, FBD8857A2147F9640099B8C3 /* AssetsConstants.swift in Sources */, 8580BAEC20BD9A7100239D9D /* LinkRecognizable.swift in Sources */, @@ -16246,14 +16481,18 @@ 4B1D7E092029D86600703228 /* CreateGroupItemsFactory.swift in Sources */, 85052E5720D1A90D00BCC386 /* StickerImagePreviewView.swift in Sources */, 8580BACC20BD984500239D9D /* MessageEditInfo.swift in Sources */, + 4B877133219314D50014AD09 /* TypedRequestExtension.swift in Sources */, + 4B2B1558218F5423000E4916 /* SharedColumn.swift in Sources */, 850FC5FA2032F64100832D87 /* ForwardSelectorWireFrame.swift in Sources */, 3AA4E6ACDBCB060172A7A279 /* FavoritesProtocols.swift in Sources */, 9B81AD92215A5EEA00993A8C /* ActiveSpeakerView.swift in Sources */, C99D6CD77325A09D045DB760 /* FavoritesViewController.swift in Sources */, 00E98256205C2740008BF03D /* SessionItemCell.swift in Sources */, 85D77807211D9B980044E72F /* ScrollPosition.swift in Sources */, + 4B030F362195CD9B00F293B7 /* MQTTService+QueuePool.swift in Sources */, FEA655F22167777E00B44029 /* PaymentWireFrame.swift in Sources */, DF55CCC682DAB5392F2A763D /* FavoritesPresenter.swift in Sources */, + 4BDDAAEB218CA2C700F775A7 /* HomeDataProviderImpl.swift in Sources */, 002FFCE1202DE8BE003CCB26 /* Date+Extension.swift in Sources */, B723C626204D86AF00884FFD /* SettingsDataAndStorageViewController.swift in Sources */, A4494C7E2080F26600223B06 /* ChannelsItemsFactory.swift in Sources */, @@ -16402,6 +16641,7 @@ 855EF423202CC85300541BE3 /* MQTTServiceStars.swift in Sources */, 3362A56D731AC1411C02D037 /* MyGroupAliasWireframe.swift in Sources */, 260313A020A0A4BA009AC66D /* SwitchableActionCell.swift in Sources */, + 4B055C37219C313A001FE077 /* FileDownloaderFactory.swift in Sources */, 8ED0F3D11FBC5CF2004916AB /* GroupsListViewController.swift in Sources */, BBF46945EB64E07C58817ACA /* EditGroupNameProtocols.swift in Sources */, 8520040020D466CE007C0036 /* StickerPack_Spec.swift in Sources */, @@ -16485,7 +16725,6 @@ C02DD71CA3832908D422B83C /* CreateGroupWireframe.swift in Sources */, A45F110E20B4218D00F45004 /* PositionType.swift in Sources */, 26EEA5472091F84E0066D3B0 /* CollectionsExtensions.swift in Sources */, - 00E9825C205FD351008BF03D /* AuthHandler.swift in Sources */, 1CCEA1165C5D016C6768E5DC /* GroupRulesProtocols.swift in Sources */, 6B13E514036AA364CE2AC038 /* GroupRulesViewController.swift in Sources */, A4330A712109EBB30060BD93 /* CountriesProviding.swift in Sources */, @@ -16495,6 +16734,7 @@ 0D520AAAA7FD1F0464C8174F /* GroupRulesInteractor.swift in Sources */, A45F115E20B422AF00F45004 /* Contact+DB.swift in Sources */, 8505445720627C7C00E0F2B3 /* HistoryCellModel.swift in Sources */, + 4B055C3B219C4101001FE077 /* MQTTServiceProtocol.swift in Sources */, 2F2A5C12A7202E7834F923DC /* GroupRulesWireframe.swift in Sources */, 2625DBF820EFC5DE00E01C05 /* FourCharCode+StringLiteralConvertible.swift in Sources */, D3A30AF05BD7C46A9A8C1FC1 /* GroupStorageProtocols.swift in Sources */, @@ -16505,6 +16745,7 @@ 26534B25210B4BE70003B9BC /* DBMessage+Extension.swift in Sources */, 2F7C7F7837BDE6F5767A3A8C /* GroupStorageViewController.swift in Sources */, 4B1D7E012029C4BE00703228 /* OptionsItemsFactory.swift in Sources */, + 4B055C39219C3146001FE077 /* FileDownloaderKind.swift in Sources */, 4B348FFE2163850100CCB0E3 /* DeletedIndexesCalculator.swift in Sources */, C921738220BADAFC00519A2D /* TextInputValidationService.swift in Sources */, 9763CCDFE5AF7B58C21CDED9 /* GroupStoragePresenter.swift in Sources */, @@ -16519,6 +16760,7 @@ 260313AB20A0A4BA009AC66D /* ChatLanguageSettingsInteractor.swift in Sources */, 7C51CDC1260CE191C07EE46C /* SelectCountryViewController.swift in Sources */, 8596CEF22048A763006FC65D /* ThemeCellModel.swift in Sources */, + 4B030F3B2195CF8100F293B7 /* Host.swift in Sources */, 8566772020C1924500DD4204 /* MessageInteractor+MessageHandlerSubscriber.swift in Sources */, A1AD6864F4F49D9FC8997D59 /* SelectCountryPresenter.swift in Sources */, 32E5A25AD25BF752EB3864AB /* SelectCountryInteractor.swift in Sources */, @@ -16654,7 +16896,6 @@ FB816EF020B5B36D00093DCD /* HistoryRequestModelTests.swift in Sources */, 85458CEA212D742300BA8814 /* muc.swift in Sources */, A4CB153B21039C1100C3B68B /* JailbreakDetectorProtocol.swift in Sources */, - 4BF090B621635B3000DCCA5C /* LogServiceStub.swift in Sources */, 4B8C05952164A9D60034D8F3 /* ChatCellModelMock.swift in Sources */, 85458D01212D7C1A00BA8814 /* StringAtomExtension.swift in Sources */, A4AB8E522105EC46005F9B0C /* TextField.swift in Sources */, @@ -16675,6 +16916,7 @@ 4BF090CE21635FEE00DCCA5C /* Message+Type.swift in Sources */, A49EE6D7210B110800B700B1 /* Link.swift in Sources */, 4B8FC31C2163CD8C00602D6B /* ChatCellModel.swift in Sources */, + 2611CEF92182090900FFD4DD /* LogWriterProtocol.swift in Sources */, 85458CE4212D731300BA8814 /* MessageIdentifiers.swift in Sources */, FB61D651214FEC8200CB2A1F /* LocalizableConstants.swift in Sources */, A4330A562109D60D0060BD93 /* QueryFactoryProtocol.swift in Sources */, @@ -16721,12 +16963,12 @@ buildActionMask = 2147483647; files = ( 4B348FD8216366AE00CCB0E3 /* LogServiceTopic.swift in Sources */, + 2611CEFA2182090900FFD4DD /* LogWriterProtocol.swift in Sources */, FE21ACB92113AB3C006010A0 /* KeychainServiceTest.swift in Sources */, FE21ACBC2113AB92006010A0 /* QueryFactoryProtocol.swift in Sources */, 4B348FD7216366A900CCB0E3 /* LogServiceProtocol.swift in Sources */, FE21ACBD2113ABBF006010A0 /* DictionaryExtension.swift in Sources */, FE21ACBA2113AB4B006010A0 /* KeychainService.swift in Sources */, - 4B348FD62163668500CCB0E3 /* LogServiceStub.swift in Sources */, 4BF2C3E92189B49500E59F6C /* Localizable.swift in Sources */, 85458CDC212D6FFF00BA8814 /* String+Split.swift in Sources */, FB61D652214FEC8300CB2A1F /* LocalizableConstants.swift in Sources */, @@ -16809,6 +17051,267 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 26305F6B2180763600400DB0 /* LoadDB */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 26305F702180765E00400DB0 /* LoadDBConfig.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = LoadDB; + }; + 26305F6C2180763600400DB0 /* LoadDB */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D55BAC5832CAFB50A4F64387 /* Pods-Nynja.loaddb.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIconSpotify; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Nynja/Resources/Nynja.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 9GKQ5AMF2B; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = "$(SRCROOT)/Nynja/Resources/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_SWIFT_FLAGS = "$(inherited) -D SQLITE_HAS_CODEC -D GRDBCIPHER $(inherited) \"-D\" \"COCOAPODS\" -Xfrontend -debug-time-function-bodies -Xfrontend -warn-long-expression-type-checking=20 -Xfrontend -warn-long-function-bodies=20"; + PRODUCT_BUNDLE_IDENTIFIER = "$(BundleIdentifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = "84763be4-0d2a-4728-9e67-b65eb99614ad"; + PROVISIONING_PROFILE_SPECIFIER = DevBundle_Dev; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OBJC_BRIDGING_HEADER = "Nynja-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; + SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; + }; + name = LoadDB; + }; + 26305F6D2180763600400DB0 /* LoadDB */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = EF57CF149EF8224DA702DEB9 /* Pods-Nynja-Share.loaddb.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "Nynja-Share/Resources/Nynja-Share.entitlements"; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 9GKQ5AMF2B; + ENABLE_BITCODE = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = "Nynja-Share/Resources/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" -DSHARE_EXTENSION -Xfrontend -warn-long-expression-type-checking=20 -Xfrontend -warn-long-function-bodies=20"; + PRODUCT_BUNDLE_IDENTIFIER = "$(ExtensionBundleIdentifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = "ee586ed5-2bed-44bc-ab45-42f41bfa7316"; + PROVISIONING_PROFILE_SPECIFIER = DevBundle_DevExt; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = LoadDB; + }; + 26305F6E2180763600400DB0 /* LoadDB */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F437803397DA4D6AA4459469 /* Pods-NynjaUnitTests.loaddb.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 9GKQ5AMF2B; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = NynjaUnitTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.nynja.mobile.communicator.NynjaUnitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = LoadDB; + }; + 26305F6F2180763600400DB0 /* LoadDB */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 9GKQ5AMF2B; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = NynjaIntegrationTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.nynja.dev.mobile.communicator.NynjaIntegrationTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Nynja.app/Nynja"; + }; + name = LoadDB; + }; 357809AE1F9765CF00C9680C /* Dev */ = { isa = XCBuildConfiguration; baseConfigurationReference = A169D8E4AB2003F96040DD7A /* Pods-Nynja-Share.dev.xcconfig */; @@ -16846,7 +17349,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = "Nynja-Share/Resources/Nynja-Share.entitlements"; - CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; @@ -16862,6 +17365,7 @@ PROVISIONING_PROFILE = "2a318f9e-d0ab-41dc-968a-e1cb13de4de5"; PROVISIONING_PROFILE_SPECIFIER = ProductionBundle_AppstoreExt; SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = RELEASE; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; @@ -17025,7 +17529,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Nynja/Resources/Nynja.entitlements; - CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; @@ -17049,6 +17553,177 @@ }; name = Release; }; + 4B31B83F219DE52000837B59 /* Release-Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F1313B0020888CC400E04092 /* ReleaseConfig.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = "Release-Debug"; + }; + 4B31B840219DE52000837B59 /* Release-Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9C2E07BBF40570582F4258A3 /* Pods-Nynja.release-debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Nynja/Resources/Nynja.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 9GKQ5AMF2B; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = "$(SRCROOT)/Nynja/Resources/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_SWIFT_FLAGS = "$(inherited) -D SQLITE_HAS_CODEC -D GRDBCIPHER $(inherited) \"-D\" \"COCOAPODS\" -Xfrontend -debug-time-function-bodies -Xfrontend -warn-long-expression-type-checking=20 -Xfrontend -warn-long-function-bodies=20"; + PRODUCT_BUNDLE_IDENTIFIER = "$(BundleIdentifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = "e08a86e7-d3c6-4351-bfa9-7e563b2b7cba"; + PROVISIONING_PROFILE_SPECIFIER = ProductionBundle_Dev; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OBJC_BRIDGING_HEADER = "Nynja-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 4.0; + SWIFT_WHOLE_MODULE_OPTIMIZATION = YES; + }; + name = "Release-Debug"; + }; + 4B31B841219DE52000837B59 /* Release-Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B3D64F28A0B75CE6D74100B1 /* Pods-Nynja-Share.release-debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "Nynja-Share/Resources/Nynja-Share.entitlements"; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = 9GKQ5AMF2B; + ENABLE_BITCODE = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = "Nynja-Share/Resources/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" -DSHARE_EXTENSION"; + PRODUCT_BUNDLE_IDENTIFIER = "$(ExtensionBundleIdentifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = "3e08568d-8d8f-426c-bf4c-0f12f19252e5"; + PROVISIONING_PROFILE_SPECIFIER = ProductionBundle_DevExt; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Release-Debug"; + }; + 4B31B842219DE52000837B59 /* Release-Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = CEBE788F463F17C3CCD8302A /* Pods-NynjaUnitTests.release-debug.xcconfig */; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 9GKQ5AMF2B; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = NynjaUnitTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.nynja.mobile.communicator.NynjaUnitTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Release-Debug"; + }; + 4B31B843219DE52000837B59 /* Release-Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 9GKQ5AMF2B; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = NynjaIntegrationTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.nynja.dev.mobile.communicator.NynjaIntegrationTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Nynja.app/Nynja"; + }; + name = "Release-Debug"; + }; 5B4DFF6D2191A08E00E89D17 /* Spotify */ = { isa = XCBuildConfiguration; baseConfigurationReference = 5B4DFF6C21919FA000E89D17 /* SpotifyConfig.xcconfig */; @@ -17898,10 +18573,12 @@ isa = XCConfigurationList; buildConfigurations = ( 357809AE1F9765CF00C9680C /* Dev */, + 26305F6D2180763600400DB0 /* LoadDB */, 5B4DFF6F2191A08E00E89D17 /* Spotify */, F10AFE9E20EF8BBE00C7CE83 /* DevAutoTests */, F1313AFD20888BD300E04092 /* Prerelease */, 357809AF1F9765CF00C9680C /* Release */, + 4B31B841219DE52000837B59 /* Release-Debug */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -17910,10 +18587,12 @@ isa = XCConfigurationList; buildConfigurations = ( 3ABCE8FD1EC9330D00A80B15 /* Dev */, + 26305F6B2180763600400DB0 /* LoadDB */, 5B4DFF6D2191A08E00E89D17 /* Spotify */, F10AFE9C20EF8BBE00C7CE83 /* DevAutoTests */, F1313AFB20888BD300E04092 /* Prerelease */, 3ABCE8FE1EC9330D00A80B15 /* Release */, + 4B31B83F219DE52000837B59 /* Release-Debug */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -17922,10 +18601,12 @@ isa = XCConfigurationList; buildConfigurations = ( 3ABCE9001EC9330D00A80B15 /* Dev */, + 26305F6C2180763600400DB0 /* LoadDB */, 5B4DFF6E2191A08E00E89D17 /* Spotify */, F10AFE9D20EF8BBE00C7CE83 /* DevAutoTests */, F1313AFC20888BD300E04092 /* Prerelease */, 3ABCE9011EC9330D00A80B15 /* Release */, + 4B31B840219DE52000837B59 /* Release-Debug */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -17934,10 +18615,12 @@ isa = XCConfigurationList; buildConfigurations = ( F1C37AB0209A1BF4005EA197 /* Dev */, + 26305F6E2180763600400DB0 /* LoadDB */, 5B4DFF702191A08E00E89D17 /* Spotify */, F10AFE9F20EF8BBE00C7CE83 /* DevAutoTests */, F1C37AB1209A1BF4005EA197 /* Prerelease */, F1C37AB2209A1BF4005EA197 /* Release */, + 4B31B842219DE52000837B59 /* Release-Debug */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -17946,10 +18629,12 @@ isa = XCConfigurationList; buildConfigurations = ( FE21ACAE2113AA7F006010A0 /* Dev */, + 26305F6F2180763600400DB0 /* LoadDB */, 5B4DFF712191A08E00E89D17 /* Spotify */, FE21ACB02113AA7F006010A0 /* DevAutoTests */, FE21ACB32113AA7F006010A0 /* Prerelease */, FE21ACB42113AA7F006010A0 /* Release */, + 4B31B843219DE52000837B59 /* Release-Debug */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/Nynja.xcodeproj/xcshareddata/xcschemes/LoadDB.xcscheme b/Nynja.xcodeproj/xcshareddata/xcschemes/LoadDB.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..2243a6445c6258a41d59591dbd0b1595a2ed693a --- /dev/null +++ b/Nynja.xcodeproj/xcshareddata/xcschemes/LoadDB.xcscheme @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Nynja/AppDelegate.swift b/Nynja/AppDelegate.swift index 332ecf388e8093ea60d5c0bc18587aecc72f37e8..7ce25040daffd7af49c913415f1a448bce452024 100644 --- a/Nynja/AppDelegate.swift +++ b/Nynja/AppDelegate.swift @@ -125,8 +125,6 @@ private extension AppDelegate { } private func wipeStorage() { - MQTTService.sharedInstance.wasRunApp = storageService.wasRun - if !storageService.wasRun { LogService.log(topic: .db) { return "Clear storage: AppDelegate - if it is first runs" } storageService.clearStorage() diff --git a/Nynja/BadgeNumberService.swift b/Nynja/BadgeNumberService.swift index 1d913c53c4775fa2209fb742ebfbe89a30a09769..f86d18afc3f0d43a54293c74e215acc32d4c5280 100644 --- a/Nynja/BadgeNumberService.swift +++ b/Nynja/BadgeNumberService.swift @@ -6,44 +6,42 @@ // Copyright © 2018 TecSynt Solutions. All rights reserved. // -class BadgeNumberService: BadgeNumberServiceProtocol, StorageSubscriber { +final class BadgeNumberService: BadgeNumberServiceProtocol, StorageSubscriber { typealias BadgeHandler = (_ badgeNumber: Int64) -> Void + // MARK: - Singleton + static let shared = BadgeNumberService() + // MARK: - Properties - private(set) var badgeNumber: Int64 = 0 + + private var badgeNumber: Int64 = 0 private var counters: [String: Int64] = [:] private var subscribers: [AnyWeakSubscriber] = [] - private let conversationsProvider = ConversationsProvider() + private let conversationsProvider = ServiceFactory().makeConversationsProvider() + // MARK: - Init & deinit + private init() { subscribe() } func initCounters() { - var badgeNumber: Int64 = 0 - - conversationsProvider - .fetchAllConversations() - .forEach { chat in - guard let id = chat.id else { return } - counters[id] = chat.unreadCount - badgeNumber += chat.unreadCount - } - - self.badgeNumber = badgeNumber + self.badgeNumber = conversationsProvider.fetchUnreadMessagesCount() } deinit { unsubscribe() } + // MARK: - Subscription + private func subscribe() { subscribeTypes.forEach { DBObserver.default.register(subscriber: self, type: $0) @@ -54,8 +52,10 @@ class BadgeNumberService: BadgeNumberServiceProtocol, StorageSubscriber { DBObserver.default.unregister(subscriber: self) } + // MARK: - Register subscribers // TODO: sync + func observeBadgeNumber(_ object: AnyObject, notifyImmediately: Bool = true, handler: @escaping BadgeHandler) { let subscriber = AnyWeakSubscriber(object: object, handler: handler) subscribers.append(subscriber) @@ -71,6 +71,7 @@ class BadgeNumberService: BadgeNumberServiceProtocol, StorageSubscriber { func clean() { badgeNumber = 0 + counters = [:] subscribers.forEach { $0.handler(badgeNumber) } } @@ -116,5 +117,4 @@ class BadgeNumberService: BadgeNumberServiceProtocol, StorageSubscriber { subscribers.forEach { $0.handler(badgeNumber) } } - } diff --git a/Nynja/BadgeNumberServiceProtocol.swift b/Nynja/BadgeNumberServiceProtocol.swift index 83255461a28ee10db99c1089ddcedd5e4e4041bf..f7021e3e7c16351117574f5b2eb6a7ab14428396 100644 --- a/Nynja/BadgeNumberServiceProtocol.swift +++ b/Nynja/BadgeNumberServiceProtocol.swift @@ -12,9 +12,6 @@ typealias BadgeHandler = (_ badgeNumber: Int64) -> Void /// Service which provide access to badge number and capability to observe changes protocol BadgeNumberServiceProtocol { - /// Represents badge number which should show in the app icon - var badgeNumber: Int64 { get } - // MARK: - Register subscribers /// Add subscriber which observe changes of 'badgeNumber' property func observeBadgeNumber(_ object: AnyObject, notifyImmediately: Bool, handler: @escaping BadgeHandler) diff --git a/Nynja/ChatService/ChatService.swift b/Nynja/ChatService/ChatService.swift index 622c682f04b9a2ba04e6e7539a7ad9328715f293..2cfad147cc51bc7e760440d2f50fb1c32cbd3523 100644 --- a/Nynja/ChatService/ChatService.swift +++ b/Nynja/ChatService/ChatService.swift @@ -10,7 +10,9 @@ final class ChatService { // MARK: - Dependencies - private static let storageService = StorageService.sharedInstance + private static var storageService: StorageService { + return .sharedInstance + } private static let messageParser = MessageParser(dependencies: .init(storageService: storageService)) diff --git a/Nynja/ContactDAO.swift b/Nynja/ContactDAO.swift index d7ce86b923554462c8de74ea1d36e22b3e4b7cab..27b611e7731476f6b63779d0c77fa3c2f12ecada 100644 --- a/Nynja/ContactDAO.swift +++ b/Nynja/ContactDAO.swift @@ -15,6 +15,7 @@ import GRDBCipher class ContactDAO: ContactDAOProtocol { + // MARK: - Fetch // MARK: -- Contact static var currentContact: Contact? { @@ -34,120 +35,108 @@ class ContactDAO: ContactDAOProtocol { } } - static func findContactBy(phone: String) -> Contact? { - let phoneIdColumn = Column(ContactTable.Column.phoneId.title) - - guard let contact = dbManager.fetch({ db in - return try DBContact.filter(phoneIdColumn.like("\(phone)%")).fetchOne(db) - }) else { - return nil - } - - return Contact(contact: contact) + static func findPlainContactBy(phone: String) -> Contact? { + return dbManager.fetch { db in + return try DBContact + .filter(Column.phoneId.like("\(phone)%")) + .fetchOne(db) + }?.serverModel } static func findContactBy(phoneId: String) -> Contact? { - guard let dbContact = fetchContact(by: phoneId) else { return nil } - return Contact(contact: dbContact) + return fetchContact(by: phoneId)?.serverModel } - static func findContactBy(username: String) -> Contact? { - let nickColumn = Column(ContactTable.Column.nick.title) - - guard let contact = dbManager.fetch({ db in - return try DBContact.filter(nickColumn == username).fetchOne(db) - }) else { - return nil - } - - return Contact(contact: contact) + static func findPlainContactBy(username: String) -> Contact? { + return dbManager.fetch { db in + return try DBContact + .filter(Column.nick == username) + .fetchOne(db) + }?.serverModel } - static func findContactBy(voxId: String) -> Contact? { - let serviceIdColumn = Column(ServiceTable.Column.id.title) - let serviceTypeColumn = Column(ServiceTable.Column.type.title) - let serviceTargetTypeColumn = Column(ServiceTable.Column.targetType.title) - let contactIdColumn = Column(ContactTable.Column.phoneId.title) - let voxType = "vox" - - let predicate = serviceIdColumn == voxId && - serviceTypeColumn == voxType && - serviceTargetTypeColumn == DBService.TargetType.contact.rawValue - - guard - let service = dbManager.fetch({ db in - return try DBService.filter(predicate).fetchOne(db) - }), let contact = dbManager.fetch({ (db) in - return try DBContact.filter(contactIdColumn == service.targetId).fetchOne(db) - }) else { - return nil - } - - return Contact(contact: contact) - } // MARK: -- Contacts - static func fetchContacts() -> [Contact] { - guard let rosterId = StorageService.sharedInstance.rosterId else { return [] } - - let contacts = dbManager.fetch { db -> [DBContact] in - return try DBContact.contacts(from: db, rosterId: rosterId) + + static func fetchPlainContacts() -> [Contact] { + guard let rosterId = StorageService.sharedInstance.rosterId else { + return [] } - return contacts.map { Contact(contact: $0) } + return dbManager.fetch { db -> [DBContact] in + return try DBContact + .filter(Column.rosterId == rosterId) + .fetchAll(db) + }.serverModels } - static func fetchContactWithoutSelf() -> [Contact] { + static func fetchPlainContactsWithoutSelf() -> [Contact] { var contacts: [Contact] = [] if let phoneId = StorageService.sharedInstance.phoneId { - contacts = fetchContacts(with: [phoneId], isExcluded: true) + contacts = fetchPlainContacts(with: [phoneId], isExcluded: true) } else { - contacts = fetchContacts() + contacts = fetchPlainContacts() } return contacts } - static func fetchContacts(with phoneIds: [String], isExcluded: Bool = false) -> [Contact] { - let contacts = dbManager.fetch { db -> [DBContact] in - let phoneIdColumn = Column(ContactTable.Column.phoneId.title) - - var predicate = phoneIds.contains(phoneIdColumn) + static func fetchPlainContacts(with phoneIds: [String], isExcluded: Bool = false) -> [Contact] { + return dbManager.fetch { db -> [DBContact] in + var predicate = phoneIds.contains(Column.phoneId) if isExcluded { predicate = !predicate } - // TODO: need to think - let contacts = try DBContact.filter(predicate).fetchAll(db) - try contacts.forEach { try $0.construct(db) } - return contacts - } - - return contacts.map { Contact(contact: $0) } + return try DBContact.filter(predicate).fetchAll(db) + }.serverModels } - static func fetchContacts(with statuses: [Contact.Status]) -> [Contact] { + static func fetchPlainContacts(with statuses: [Contact.Status]) -> [Contact] { let statuses = statuses.map { $0.rawValue } - let statusColumn = Column(ContactTable.Column.status.title) - - let contacts = dbManager.fetch { db -> [DBContact] in - // TODO: need to think - let contacts = try DBContact.filter(statuses.contains(statusColumn)).fetchAll(db) - try contacts.forEach { try $0.construct(db) } - return contacts - } + return dbManager.fetch { db -> [DBContact] in + return try DBContact + .filter(statuses.contains(Column.status)) + .fetchAll(db) + }.serverModels + } + + static func fetchContacts(with args: RosterRelatedQueryArgs) -> [Contact] { + return dbManager.fetch { db in + return try args.makeTypedRequest().fetchAllConstructed(db) + }.serverModels + } + + static func fetchPlainContacts( + with args: RosterRelatedQueryArgs, + excludingIds ids: [String], + excludingStatuses statuses: [Contact.Status]?) -> [Contact] { - return contacts.map { Contact(contact: $0) } + return dbManager.fetch { db in + return try args + .makeTypedRequest() + .filter(!ids.contains(Column.phoneId)) + .performIfValueExists(statuses) { $0.filter(!$1.strings.contains(Column.status)) } + .fetchAll(db) + }.serverModels + } + + static func fetchChats(with args: DBContact.RequestArgs) -> [Contact] { + return dbManager.fetch { db in + return try DBContact + .request(with: args) + .fetchAllConstructed(db) + }.serverModels } // MARK: -- Reader static func fetchReader(for phoneId: String, kind: ReaderKind) -> Int64? { let readersString = dbManager.fetch{ db in return try DBContact - .filter(Column(ContactTable.Column.phoneId.title) == phoneId) - .select([Column(ContactTable.Column.reader.title)]) + .filter(Column.phoneId == phoneId) + .select([Column.reader]) .asRequest(of: String.self) .fetchOne(db) } @@ -183,5 +172,4 @@ class ContactDAO: ContactDAOProtocol { try? dbManager.perform(action: .updateColumns([ContactTable.Column.reader.title]), with: contact) } - } diff --git a/Nynja/ContactDAOProtocol.swift b/Nynja/ContactDAOProtocol.swift index f97c190fe12ccf4db05e5a77a3e3a5de6ac89ca7..4543120ad3e3d14144237b091c9480e995f3aa02 100644 --- a/Nynja/ContactDAOProtocol.swift +++ b/Nynja/ContactDAOProtocol.swift @@ -10,28 +10,43 @@ protocol ContactDAOProtocol: DAOProtocol { // MARK: - Fetch // MARK: -- Contact + static var currentContact: Contact? { get } static func fetchContact(by id: String) -> DBContact? static func fetchContact(by rowId: Int64) -> DBContact? + static func findPlainContactBy(phone: String) -> Contact? static func findContactBy(phoneId: String) -> Contact? - static func findContactBy(username: String) -> Contact? - static func findContactBy(voxId: String) -> Contact? + static func findPlainContactBy(username: String) -> Contact? + // MARK: -- Contacts - static func fetchContacts() -> [Contact] - static func fetchContactWithoutSelf() -> [Contact] - static func fetchContacts(with phoneIds: [String], isExcluded: Bool) -> [Contact] - static func fetchContacts(with statuses: [Contact.Status]) -> [Contact] + + static func fetchPlainContacts() -> [Contact] + static func fetchPlainContactsWithoutSelf() -> [Contact] + static func fetchPlainContacts(with phoneIds: [String], isExcluded: Bool) -> [Contact] + static func fetchPlainContacts(with statuses: [Contact.Status]) -> [Contact] + + static func fetchContacts(with args: RosterRelatedQueryArgs) -> [Contact] + static func fetchPlainContacts(with args: RosterRelatedQueryArgs, + excludingIds ids: [String], + excludingStatuses statuses: [Contact.Status]?) -> [Contact] + + static func fetchChats(with args: DBContact.RequestArgs) -> [Contact] + // MARK: -- Reader + static func fetchReader(for phoneId: String, kind: ReaderKind) -> Int64? + // MARK: - Update + static func updateColumns(_ columns: Set, contact: Contact) + // MARK: -- Fields - static func updateReader(_ reader: Int64, phoneId: String, kind: ReaderKind) + static func updateReader(_ reader: Int64, phoneId: String, kind: ReaderKind) } diff --git a/Nynja/ContactsProvider.swift b/Nynja/ContactsProvider.swift index 84ffbbafda5cbd134ccb5b1bd8aedf47e1177b9c..16c6c5f0ea59ef17eb27500a3410eeeb65ad209a 100644 --- a/Nynja/ContactsProvider.swift +++ b/Nynja/ContactsProvider.swift @@ -6,36 +6,36 @@ // Copyright © 2018 TecSynt Solutions. All rights reserved. // -class ContactsProvider: ContactsProviding { +import GRDBCipher + +final class ContactsProvider: ContactsProviding { + typealias FetchingArgs = ContactsProvidingFetchingArgs - func fetchHistory() -> [Contact] { - return ContactDAO - .fetchContactWithoutSelf() - .sorted { - $0.create > $1.create - } + private let orderColumns = [Column.created.desc] + + func fetchHistory(with args: FetchingArgs) -> [Contact] { + let daoArgs = makeRosterRelatedQueryArgs(from: args) + return ContactDAO.fetchPlainContacts(with: daoArgs, + excludingIds: [args.phoneId], + excludingStatuses: args.statuses) } - func fetchHistory(without statuses: [Contact.Status]) -> [Contact] { - return fetchHistory().filter { contact in - if let status = contact.originalStatus { - return !statuses.contains(status) - } - - return true - } + private func makeRosterRelatedQueryArgs(from args: FetchingArgs) -> RosterRelatedQueryArgs { + return RosterRelatedQueryArgs( + rosterId: args.rosterId, + args: .init(limit: args.limit, + orderingTerms: orderColumns)) } func fetchFriends() -> [Contact] { return ContactDAO - .fetchContacts(with: [.friend, .banned, .ban]) + .fetchPlainContacts(with: [.friend, .banned, .ban]) .withoutSelf } func fetchFriendsWithoutBlocked() -> [Contact] { return ContactDAO - .fetchContacts(with: [.friend]) + .fetchPlainContacts(with: [.friend]) .withoutSelf } - } diff --git a/Nynja/ContactsProviding.swift b/Nynja/ContactsProviding.swift index 5fedf5cc2bb2434417d8b0abc7b140bee1eec18e..2913cd0f61158ee4a20017dab51e9a79ee48240a 100644 --- a/Nynja/ContactsProviding.swift +++ b/Nynja/ContactsProviding.swift @@ -8,10 +8,8 @@ protocol ContactsProviding { - func fetchHistory() -> [Contact] - func fetchHistory(without statuses: [Contact.Status]) -> [Contact] + func fetchHistory(with args: ContactsProvidingFetchingArgs) -> [Contact] func fetchFriends() -> [Contact] func fetchFriendsWithoutBlocked() -> [Contact] - } diff --git a/Nynja/ContactsProvidingFetchingArgs.swift b/Nynja/ContactsProvidingFetchingArgs.swift new file mode 100644 index 0000000000000000000000000000000000000000..3e49f1db59cdd0f33a9b83bc6d336b0f17dba536 --- /dev/null +++ b/Nynja/ContactsProvidingFetchingArgs.swift @@ -0,0 +1,25 @@ +// +// ContactsProvidingFetchingArgs.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/6/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + + +struct ContactsProvidingFetchingArgs { + let rosterId: Int64 + let phoneId: String + let statuses: [Contact.Status]? + let limit: Int? + + init(rosterId: Int64, + phoneId: String, + statuses: [Contact.Status]? = nil, + limit: Int? = nil) { + self.rosterId = rosterId + self.phoneId = phoneId + self.statuses = statuses + self.limit = limit + } +} diff --git a/Nynja/ConversationsProvider.swift b/Nynja/ConversationsProvider.swift index c7b497524e1a497f29ca4a9fb27e7d2265199551..f5d011edfcf0a3f7988d9c86873ac737e5206ba7 100644 --- a/Nynja/ConversationsProvider.swift +++ b/Nynja/ConversationsProvider.swift @@ -6,67 +6,103 @@ // Copyright © 2018 TecSynt Solutions. All rights reserved. // -class ConversationsProvider: ConversationsProviding { +import GRDBCipher + +class ConversationsProvider: ConversationsProviding, InitializeInjectable { + + private let dbManager: DBManagerProtocol + + required init(dependencies: Dependencies) { + self.dbManager = dependencies.dbManager + } // MARK: - Chats - func fetchChats() -> [Contact] { - return ContactDAO - .fetchContacts(with: [.friend, .banned, .ban]) - .sorted(by: comparator) + func fetchChats(with args: FetchingArgs) -> [Contact] { + return ContactDAO.fetchChats(with: args) } // MARK: - Groups - func fetchGroups() -> [Room] { - return RoomDAO - .fetchRooms(kind: .group) - .sorted(by: comparator) + func fetchGroups(with args: FetchingArgs) -> [Room] { + let groupRequestArgs = makeGroupsRequestArgs(from: args, kind: .group) + return RoomDAO.fetchPlainRooms(with: groupRequestArgs) } // MARK: - Channels - func fetchChannels() -> [Room] { - return RoomDAO - .fetchRooms(kind: .channel) - .sorted(by: comparator) + func fetchChannels(with args: ConversationsProviding.FetchingArgs) -> [Room] { + let channelRequestArgs = makeGroupsRequestArgs(from: args, kind: .channel) + return RoomDAO.fetchPlainRooms(with: channelRequestArgs) } - func fetchMyChannels() -> [Room] { - guard let phoneId = StorageService.sharedInstance.phoneId else { - return [] - } - - return RoomDAO - .fetchUserRooms(with: phoneId, isAdmin: true, kind: .channel) - .sorted(by: comparator) + func fetchMyChannels(with args: ConversationsProviding.FetchingArgs, ownerId: String) -> [Room] { + let channelRequestArgs = makeGroupsRequestArgs(from: args, kind: .channel, ownerId: ownerId) + return RoomDAO.fetchPlainRooms(with: channelRequestArgs) + } + + private func makeGroupsRequestArgs(from args: FetchingArgs, kind: Room.Kind, ownerId: String? = nil) -> DBRoom.RequestArgs { + return .init( + rosterId: args.rosterId, + type: kind.rawValue, + onlyUnread: args.onlyUnread, + ownerId: ownerId, + limit: args.limit, + offset: args.offset) + } + + func comparator(lhs: ChatModel, rhs: ChatModel) -> Bool { + let created1 = lhs.last_msg?.created ?? 0 + let created2 = rhs.last_msg?.created ?? 0 + return created1 > created2 } // MARK: - All - func fetchAllConversations() -> [ChatModel] { - var chatModels: [ChatModel] = [] + func fetchUnreadMessagesCount() -> Int64 { + return dbManager.fetch { db in + return try makeUnreadCountRequest().fetchOne(db) + } ?? 0 + } + + private func makeUnreadCountRequest() -> AnyTypedRequest { + let unreadAlias = "unread" - let contacts = ContactDAO.fetchContactWithoutSelf() as [ChatModel] - chatModels.append(contentsOf: contacts) + let contactSql = makeUnreadCountSQL(for: ContactTable.name, unreadAlias: unreadAlias) + let roomSql = makeUnreadCountSQL(for: RoomTable.name, unreadAlias: unreadAlias) - let rooms = RoomDAO.fetchRooms() as [ChatModel] - chatModels.append(contentsOf: rooms) + let sql = """ + select sum(\(unreadAlias)) + from + (\(contactSql) + union + \(roomSql)) + """ - return chatModels + return SQLRequest(sql).asRequest(of: Int64.self) } - - // MARK: - Comparator - - func comparator(lhs: ChatModel, rhs: ChatModel) -> Bool { - let created1 = lhs.last_msg?.created ?? 0 - let created2 = rhs.last_msg?.created ?? 0 - return created1 > created2 + private func makeUnreadCountSQL(for tableName: String, unreadAlias: String) -> String { + let unreadColumn = "\(tableName).\(Column.unread)" + + return """ + select sum(\(unreadColumn)) as '\(unreadAlias)' + from \(tableName) + where \(unreadColumn) > 0 + """ } +} + + +// MARK: - InitInitializable + +extension ConversationsProvider { + struct Dependencies { + let dbManager: DBManagerProtocol + } } diff --git a/Nynja/ConversationsProviding.swift b/Nynja/ConversationsProviding.swift index a4f130a74206ec55c8fc50c2a4ef08dedc938c4d..10bf1bb955f721ec0ae074d7df4fb179e8d90357 100644 --- a/Nynja/ConversationsProviding.swift +++ b/Nynja/ConversationsProviding.swift @@ -6,15 +6,18 @@ // Copyright © 2018 TecSynt Solutions. All rights reserved. // + protocol ConversationsProviding { + typealias FetchingArgs = DBContact.RequestArgs + + func fetchChats(with args: FetchingArgs) -> [Contact] - func fetchChats() -> [Contact] - func fetchGroups() -> [Room] - - func fetchChannels() -> [Room] - func fetchMyChannels() -> [Room] + func fetchGroups(with args: FetchingArgs) -> [Room] - func fetchAllConversations() -> [ChatModel] + func fetchChannels(with args: FetchingArgs) -> [Room] + func fetchMyChannels(with args: FetchingArgs, ownerId: String) -> [Room] func comparator(lhs: ChatModel, rhs: ChatModel) -> Bool + + func fetchUnreadMessagesCount() -> Int64 } diff --git a/Nynja/DB/DAO.swift b/Nynja/DB/DAO.swift new file mode 100644 index 0000000000000000000000000000000000000000..d9b7fe91c02a8a6f018262f7a7b6bdee4fb6d75a --- /dev/null +++ b/Nynja/DB/DAO.swift @@ -0,0 +1,18 @@ +// +// DAO.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/13/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + + +class DAO { + let dbManager = StorageService.sharedInstance + + func fetch(with maker: U, kind: DBModelFetchingKind = .constructed) -> [T] where U.Model == T { + return dbManager.fetch { db in + return try maker.makeTypedRequest().fetchAll(db, kind: kind) + } + } +} diff --git a/Nynja/DB/Extensions/Array+Table.swift b/Nynja/DB/Extensions/Array+Table.swift index c85191d9d0d10aabcfffc3da4fac7a539755637d..1cd7aa1ad1e9f3583ef47c2a6a385a6e745300de 100644 --- a/Nynja/DB/Extensions/Array+Table.swift +++ b/Nynja/DB/Extensions/Array+Table.swift @@ -11,5 +11,4 @@ extension Array where Element == Table.Type { var names: [String] { return self.map { $0.name } } - } diff --git a/Nynja/DB/Extensions/ColumnDefinitionExtension.swift b/Nynja/DB/Extensions/ColumnDefinitionExtension.swift new file mode 100644 index 0000000000000000000000000000000000000000..6d6ce23996f2ea4230659d8235afef9445a2142f --- /dev/null +++ b/Nynja/DB/Extensions/ColumnDefinitionExtension.swift @@ -0,0 +1,22 @@ +// +// ColumnDefinitionExtension.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/7/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import GRDBCipher + +extension ColumnDefinition { + + @discardableResult + func references(_ table: String, column: Column, onDelete deleteAction: Database.ForeignKeyAction? = nil, onUpdate updateAction: Database.ForeignKeyAction? = nil, deferred: Bool = false) -> ColumnDefinition { + return self.references( + table, + column: column.name, + onDelete: deleteAction, + onUpdate: updateAction, + deferred: deferred) + } +} diff --git a/Nynja/DB/Extensions/ColumnExtension.swift b/Nynja/DB/Extensions/ColumnExtension.swift new file mode 100644 index 0000000000000000000000000000000000000000..298d228552adb4ea0cf61ba321bcc44585682df9 --- /dev/null +++ b/Nynja/DB/Extensions/ColumnExtension.swift @@ -0,0 +1,16 @@ +// +// ColumnExtension.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/7/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import GRDBCipher + +extension Column: CustomStringConvertible { + + public var description: String { + return name + } +} diff --git a/Nynja/DB/Extensions/QueryInterfaceRequestExtension.swift b/Nynja/DB/Extensions/QueryInterfaceRequestExtension.swift new file mode 100644 index 0000000000000000000000000000000000000000..65f82c70765092fbb6dd4dd9128aee3a1a33aeb0 --- /dev/null +++ b/Nynja/DB/Extensions/QueryInterfaceRequestExtension.swift @@ -0,0 +1,23 @@ +// +// QueryInterfaceRequestExtension.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/7/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import GRDBCipher + +extension QueryInterfaceRequest { + + func performIfValueExists( + _ value: V?, + closure: (QueryInterfaceRequest, V) -> QueryInterfaceRequest) -> QueryInterfaceRequest { + + guard let value = value else { + return self + } + + return closure(self, value) + } +} diff --git a/Nynja/DB/Extensions/TableDefinitionExtension.swift b/Nynja/DB/Extensions/TableDefinitionExtension.swift index 7a82d62550fff1c9465cfab317917eec6cad35dd..9fc91a0bae67a02847c64ef7c7f266a99dfdd882 100644 --- a/Nynja/DB/Extensions/TableDefinitionExtension.swift +++ b/Nynja/DB/Extensions/TableDefinitionExtension.swift @@ -10,8 +10,14 @@ import GRDBCipher extension TableDefinition { + @available(*, deprecated, message: "Use `func column(_ column: Column, _ type: Database.ColumnType? = nil) -> ColumnDefinition` instead") @discardableResult func column(_ desc: Describable, _ type: Database.ColumnType? = nil) -> ColumnDefinition { return column(desc.title, type) } + + @discardableResult + func column(_ column: Column, _ type: Database.ColumnType? = nil) -> ColumnDefinition { + return self.column(column.name, type) + } } diff --git a/Nynja/DB/Extensions/TransactionObserverExtension.swift b/Nynja/DB/Extensions/TransactionObserverExtension.swift index 56355cff3a48d5371afb9e40fef091bb426e5211..8fb5bf20d5198b0b93c00f0d9e497388ca22a001 100644 --- a/Nynja/DB/Extensions/TransactionObserverExtension.swift +++ b/Nynja/DB/Extensions/TransactionObserverExtension.swift @@ -13,5 +13,4 @@ extension TransactionObserver { func databaseWillCommit() throws {} func databaseDidCommit(_ db: Database) {} func databaseDidRollback(_ db: Database) {} - } diff --git a/Nynja/DB/Extensions/TypedRequestExtension.swift b/Nynja/DB/Extensions/TypedRequestExtension.swift new file mode 100644 index 0000000000000000000000000000000000000000..8f527dc788b4173df4f0fda09c68cadc59308cc9 --- /dev/null +++ b/Nynja/DB/Extensions/TypedRequestExtension.swift @@ -0,0 +1,41 @@ +// +// TypedRequestExtension.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/7/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import GRDBCipher + +enum DBModelFetchingKind { + case plain + case constructed +} + +extension TypedRequest where RowDecoder: DBModel { + + func fetchAllConstructed(_ db: Database) throws -> [RowDecoder] { + return try fetchAll(db, kind: .constructed) + } + + func fetchOneConstructed(_ db: Database) throws -> RowDecoder? { + return try fetchOne(db, kind: .constructed) + } + + func fetchAll(_ db: Database, kind: DBModelFetchingKind) throws -> [RowDecoder] { + let models = try self.fetchAll(db) + if kind == .constructed { + try models.construct(db) + } + return models + } + + func fetchOne(_ db: Database, kind: DBModelFetchingKind) throws -> RowDecoder? { + let model = try self.fetchOne(db) + if kind == .constructed { + try model?.construct(db) + } + return model + } +} diff --git a/Nynja/DB/Models/Base/DBExtendedModel.swift b/Nynja/DB/Models/Base/DBExtendedModel.swift new file mode 100644 index 0000000000000000000000000000000000000000..a07fd4c4872a4c2456d59f720cd0c14aba8ddb1b --- /dev/null +++ b/Nynja/DB/Models/Base/DBExtendedModel.swift @@ -0,0 +1,11 @@ +// +// DBExtendedModel.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/7/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +protocol DBExtendedModel: DBModel, ServerModelConvertible { + +} diff --git a/Nynja/DB/Models/Base/DBModelProtocol.swift b/Nynja/DB/Models/Base/DBModel.swift similarity index 60% rename from Nynja/DB/Models/Base/DBModelProtocol.swift rename to Nynja/DB/Models/Base/DBModel.swift index 10f019eb5fd79cc45bd5f3cb55740eb051727edf..c6dabaf5939f79ba77e108008031b367d747d6aa 100644 --- a/Nynja/DB/Models/Base/DBModelProtocol.swift +++ b/Nynja/DB/Models/Base/DBModel.swift @@ -1,5 +1,5 @@ // -// DBModelProtocol.swift +// DBModel.swift // Nynja // // Created by Volodymyr Hryhoriev on 11/28/17. @@ -8,15 +8,17 @@ import GRDBCipher -protocol DBModelProtocol: Persistable, RowConvertible { +protocol DBModel: Persistable, RowConvertible { func saveAggregate(_ db: Database) throws @discardableResult func deleteAggregate(_ db: Database) throws -> Bool + + func construct(_ db: Database) throws } -extension DBModelProtocol { +extension DBModel { func saveAggregate(_ db: Database) throws { try save(db) @@ -27,4 +29,13 @@ extension DBModelProtocol { try delete(db) return false } + + func construct(_ db: Database) throws {} +} + +extension Array where Element: DBModel { + + func construct(_ db: Database) throws { + try forEach { try $0.construct(db) } + } } diff --git a/Nynja/DB/Models/Base/FeedProtocol.swift b/Nynja/DB/Models/Base/FeedProtocol.swift index fd843bbac83292306201f969618c2663eaed4219..bd113fc650aa75eae59d22982af4106b5638534e 100644 --- a/Nynja/DB/Models/Base/FeedProtocol.swift +++ b/Nynja/DB/Models/Base/FeedProtocol.swift @@ -8,7 +8,7 @@ import GRDBCipher -protocol FeedProtocol: DBModelProtocol { +protocol FeedProtocol: DBModel { var id: Int64? { get set } var type: FeedType { get } diff --git a/Nynja/DB/Models/DBChatCheckpoint.swift b/Nynja/DB/Models/DBChatCheckpoint.swift index a43295c0477f4e1a1f1e42b4c202b26896bb92a6..33086746192fc7ca253810ea24959c1833c743f7 100644 --- a/Nynja/DB/Models/DBChatCheckpoint.swift +++ b/Nynja/DB/Models/DBChatCheckpoint.swift @@ -8,7 +8,7 @@ import GRDBCipher -class DBChatCheckpoint : Record, DBModelProtocol { +final class DBChatCheckpoint : Record, DBModel { var feedId: Int64 var feedType: Int @@ -49,7 +49,7 @@ class DBChatCheckpoint : Record, DBModelProtocol { container[ChatCheckpointTable.Column.topOffset.title] = topOffset } - // MARK: - DBModelProtocol + // MARK: - DBModel func saveAggregate(_ db: Database) throws { try self.save(db) } diff --git a/Nynja/DB/Models/DBContact.swift b/Nynja/DB/Models/DBContact.swift index f58a43005a16b7f6e8d3100fc83cd460a3bf0be5..9dc20c5a2d0c6ada403712993870d98a55d54439 100644 --- a/Nynja/DB/Models/DBContact.swift +++ b/Nynja/DB/Models/DBContact.swift @@ -8,7 +8,7 @@ import GRDBCipher -class DBContact: Record, DBModelProtocol { +final class DBContact: Record, DBExtendedModel { var phoneId: String var avatar: String? @@ -25,10 +25,18 @@ class DBContact: Record, DBModelProtocol { var messageId: String? var rosterId: Int64? - var message: DBMessage? + var message: DBMessage? { + didSet { + messageId = message?.localId + } + } var features: [DBFeature] = [] var services: [DBService] = [] + var serverModel: Contact { + return Contact(contact: self) + } + init?(contact: Contact, rosterId: Int64?) { guard let phoneId = contact.phone_id else { return nil } @@ -46,7 +54,7 @@ class DBContact: Record, DBModelProtocol { if let message = contact.message { self.message = DBMessage(message: message) - self.messageId = message.msg_id + self.messageId = self.message?.localId } else { self.messageId = contact.lastMessageId } @@ -130,11 +138,6 @@ class DBContact: Record, DBModelProtocol { // MARK: - Modification func saveAggregate(_ db: Database) throws { try message?.saveAggregate(db) - - if message != nil || messageId == nil { - messageId = message?.localId - } - try save(db) try features.forEach { try $0.save(db) } @@ -143,13 +146,13 @@ class DBContact: Record, DBModelProtocol { @discardableResult func deleteAggregate(_ db: Database) throws -> Bool { - try DBContact.request(targetId: self.phoneId).deleteAll(db) - - try DBService.request(targetId: self.phoneId, targetType: .contact).deleteAll(db) + try DBFeature.deleteAll(db, targetId: phoneId, targetType: .contact) + try DBService.deleteAll(db, targetId: phoneId, targetType: .contact) if let phoneId = StorageService.sharedInstance.phoneId { - try DBP2p.delete(db, firstId: self.phoneId, secondId: phoneId) + try DBP2p.delete(db, firstId: phoneId, secondId: phoneId) } + return try self.delete(db) } @@ -179,11 +182,6 @@ class DBContact: Record, DBModelProtocol { return try contacts(db, predicate: predicate) } - static func contacts(_ db: Database, ids: [String]) throws -> [DBContact] { - let phoneIdColumn = Column(ContactTable.Column.phoneId.title) - return try contacts(db, predicate: ids.contains(phoneIdColumn)) - } - private static func contacts(_ db: Database, predicate: SQLExpressible) throws -> [DBContact] { let contacts = try DBContact.filter(predicate).fetchAll(db) try contacts.forEach { try $0.construct(db) } @@ -195,17 +193,70 @@ class DBContact: Record, DBModelProtocol { message = try DBMessage.message(db, localId: messageId) } - features = (try? DBContact.request(targetId: self.phoneId).fetchAll(db)) ?? [] - services = try DBService.request(targetId: self.phoneId, targetType: .contact).fetchAll(db) + features = (try? DBFeature.request(targetId: phoneId, targetType: .contact).fetchAll(db)) ?? [] + services = try DBService.request(targetId: phoneId, targetType: .contact).fetchAll(db) } // MARK: - Request - static private func request(targetId: String) -> QueryInterfaceRequest { - return DBFeature.request(targetId: targetId, targetType: DBFeature.TargetType.contact) - } static func request(phoneId: String) -> QueryInterfaceRequest { return DBContact.filter(Column(ContactTable.Column.phoneId.title) == phoneId) } + + static func request(with args: RequestArgs) -> AnyTypedRequest { + let contactTable = ContactTable.name + let messageTable = MessageTable.name + + let messageIdColumn = "\(contactTable).\(Column.messageId)" + let localIdColumn = "\(messageTable).\(Column.localId)" + + let rosterIdColumn = "\(contactTable).\(Column.rosterId)" + let unreadColumn = "\(contactTable).\(Column.unread)" + let statusColumn = "\(contactTable).\(Column.status)" + + let createdColumn = "\(messageTable).\(Column.created)" + + let unreadCondition = args.onlyUnread ? "and \(unreadColumn) > 0" : "" + + var sql = """ + select \(contactTable).* + from \(contactTable) left join \(messageTable) + on \(messageIdColumn) == \(localIdColumn) + where \(rosterIdColumn) == \(args.rosterId) + \(unreadCondition) + and \(statusColumn) in ('friend', 'ban', 'banned') + order by \(createdColumn) desc + """ + + if let limit = args.limit { + sql.append(" limit \(limit)") + } + + if let offset = args.offset { + sql.append(" offset \(offset)") + } + + return SQLRequest(sql).asRequest(of: DBContact.self) + } } + +extension DBContact { + + struct RequestArgs { + let rosterId: Int64 + let onlyUnread: Bool + let limit: Int? + let offset: Int? + + public init(rosterId: Int64, + onlyUnread: Bool = false, + limit: Int? = nil, + offset: Int? = nil) { + self.rosterId = rosterId + self.onlyUnread = onlyUnread + self.limit = limit + self.offset = offset + } + } +} diff --git a/Nynja/DB/Models/DBConvertMessage.swift b/Nynja/DB/Models/DBConvertMessage.swift index e64ac6c909a0ef1e57d7802b8463e120a443d45b..b68e1d05fe86d90cb57b6813989d7af128cde81e 100644 --- a/Nynja/DB/Models/DBConvertMessage.swift +++ b/Nynja/DB/Models/DBConvertMessage.swift @@ -8,7 +8,7 @@ import GRDBCipher -class DBConvertMessage: Record, DBModelProtocol { +final class DBConvertMessage: Record, DBModel { var messageId: String var type: Int @@ -75,7 +75,7 @@ class DBConvertMessage: Record, DBModelProtocol { } - //MARK: - DBModelProtocol + //MARK: - DBModel func saveAggregate(_ db: Database) throws { try self.save(db) } diff --git a/Nynja/DB/Models/DBDesc.swift b/Nynja/DB/Models/DBDesc.swift index 3871fba2bb6630150ce1481e3c5d142912c7028b..eb003f7b14363a5e2f3f456dae651d07d0694b23 100644 --- a/Nynja/DB/Models/DBDesc.swift +++ b/Nynja/DB/Models/DBDesc.swift @@ -8,7 +8,7 @@ import GRDBCipher -final class DBDesc: Record, DBModelProtocol { +final class DBDesc: Record, DBModel { let serverId: String let mime: String @@ -106,7 +106,7 @@ final class DBDesc: Record, DBModelProtocol { private func deleteFeatures(_ db: Database) throws { let featureTarget = DBDesc.featureTarget(for: targetType) - try DBFeature.request(targetId: serverId, targetType: featureTarget).deleteAll(db) + try DBFeature.deleteAll(db, targetId: serverId, targetType: featureTarget) } static func deleteAll(_ db: Database, targetId: String, targetType: TargetType) throws { diff --git a/Nynja/DB/Models/DBFeature.swift b/Nynja/DB/Models/DBFeature.swift index 90116c1c8852a17904badb7ed25c927641475320..260150295d568d339661debc276d77b59d42689f 100644 --- a/Nynja/DB/Models/DBFeature.swift +++ b/Nynja/DB/Models/DBFeature.swift @@ -30,10 +30,7 @@ final class DBFeature: Codable { } static func deleteAll(_ db: Database, targetId: String, targetType: TargetType) throws { - let targetIdColumn = Column(FeatureTable.Column.targetId.title) - let targetTypeColumn = Column(FeatureTable.Column.targetType.title) - - try DBFeature.filter(targetIdColumn == targetId && targetTypeColumn == targetType.rawValue).deleteAll(db) + try request(targetId: targetId, targetType: targetType).deleteAll(db) } } diff --git a/Nynja/DB/Models/DBJob.swift b/Nynja/DB/Models/DBJob.swift index 80586f4a47a6f4b7acfcbed228a12d5188745186..199ef98d4ab1cad76c1cf888ea602c454175c688 100644 --- a/Nynja/DB/Models/DBJob.swift +++ b/Nynja/DB/Models/DBJob.swift @@ -9,7 +9,7 @@ import Foundation import GRDBCipher -class DBJob: Record, DBModelProtocol { +final class DBJob: Record, DBExtendedModel { enum JobType: Int { case schedule = 0 @@ -28,8 +28,13 @@ class DBJob: Record, DBModelProtocol { var features : [DBFeature] = [] var type: JobType = .schedule + var serverModel: Job { + return Job(job: self) + } + // MARK: - Mapping + init?(job: Job) { super.init() @@ -47,15 +52,15 @@ class DBJob: Record, DBModelProtocol { } required init(row: Row) { - self.id = row[JobTable.Column.id.title] - self.serverId = row[JobTable.Column.serverId.title] - self.container = row[JobTable.Column.container.title] - self.feedId = row[JobTable.Column.feedId.title] - self.prev = row[JobTable.Column.prev.title] - self.next = row[JobTable.Column.next.title] - self.time = row[JobTable.Column.time.title] - self.status = row[JobTable.Column.status.title] - let jobType: Int? = row[JobTable.Column.type.title] + self.id = row[Column.id] + self.serverId = row[Column.serverId] + self.container = row[Column.container] + self.feedId = row[Column.feedId] + self.prev = row[Column.prev] + self.next = row[Column.next] + self.time = row[Column.time] + self.status = row[Column.status] + let jobType: Int? = row[Column.type] if let jobType = jobType, let type = JobType(rawValue: jobType) { self.type = type } @@ -68,15 +73,15 @@ class DBJob: Record, DBModelProtocol { } override func encode(to container: inout PersistenceContainer) { - container[JobTable.Column.id.title] = self.id - container[JobTable.Column.serverId.title] = self.serverId - container[JobTable.Column.container.title] = self.container - container[JobTable.Column.feedId.title] = self.feedId - container[JobTable.Column.prev.title] = self.prev - container[JobTable.Column.next.title] = self.next - container[JobTable.Column.time.title] = self.time - container[JobTable.Column.status.title] = self.status - container[JobTable.Column.type.title] = self.type.rawValue + container[Column.id] = self.id + container[Column.serverId] = self.serverId + container[Column.container] = self.container + container[Column.feedId] = self.feedId + container[Column.prev] = self.prev + container[Column.next] = self.next + container[Column.time] = self.time + container[Column.status] = self.status + container[Column.type] = self.type.rawValue } override func didInsert(with rowID: Int64, for column: String?) { @@ -85,26 +90,21 @@ class DBJob: Record, DBModelProtocol { // MARK: - Fetch static func job(from db: Database, rowId: Int64) throws -> DBJob? { - let job = try DBJob.filter(Column.rowID == rowId).fetchOne(db) - try job?.construct(db) - return job + return try DBJob.filter(Column.rowID == rowId).fetchOneConstructed(db) } static func job(_ db: Database, serverId: Int64) throws -> DBJob? { - let serverIdColumn = Column(JobTable.Column.serverId.title) - let job = try DBJob.filter(serverIdColumn == serverId).fetchOne(db) - try job?.construct(db) - return job + return try DBJob.filter(Column.serverId == serverId).fetchOneConstructed(db) } static func jobs(_ db: Database, filter: SQLExpressible? = nil) throws -> [DBJob] { - let jobs = try (filter == nil ? DBJob.fetchAll(db) : DBJob.filter(filter!).fetchAll(db)) - try jobs.forEach { try $0.construct(db) } - return jobs + return try (filter == nil ? + DBJob.all().fetchAllConstructed(db) : // TODO: need to think + DBJob.filter(filter!).fetchAllConstructed(db)) } static func jobs(_ db: Database, type: JobType) throws -> [DBJob] { - return try jobs(db, filter: Column(JobTable.Column.type.title) == type.rawValue) + return try jobs(db, filter: Column.type == type.rawValue) } static func jobs(_ db: Database, messageLocalId: String) throws -> [DBJob] { @@ -112,10 +112,10 @@ class DBJob: Record, DBModelProtocol { return try SQLRequest(sql).asRequest(of: DBJob.self).fetchAll(db) } - private func construct(_ db: Database) throws { + func construct(_ db: Database) throws { if let jobId = self.id { self.messages = try DBJobMessage.messages(db, jobId: jobId) - self.features = try DBJob.requestFeature(jobId: String(describing: jobId)).fetchAll(db) + self.features = try DBFeature.request(targetId: "\(jobId)", targetType: .job).fetchAll(db) } } @@ -152,7 +152,7 @@ class DBJob: Record, DBModelProtocol { guard let jobId = tempId else { return false } self.id = jobId - try DBJob.requestFeature(jobId: String(describing: jobId)).deleteAll(db) + try DBFeature.deleteAll(db, targetId: "\(jobId)", targetType: .job) try deleteMessages(db, jobId: jobId) return try self.delete(db) @@ -160,11 +160,12 @@ class DBJob: Record, DBModelProtocol { // MARK: - Private private func fetchId(_ db: Database) throws -> Int64? { - let idColumn = Column(JobTable.Column.id.title) - let serverIdColumn = Column(JobTable.Column.serverId.title) - - let predicate = (serverId != nil && serverIdColumn == self.serverId) - return try DBJob.filter(predicate).select(idColumn).asRequest(of: Int64.self).fetchOne(db) + let predicate = (serverId != nil && Column.serverId == serverId) + return try DBJob + .filter(predicate) + .select(Column.id) + .asRequest(of: Int64.self) + .fetchOne(db) } private func deleteMessages(_ db: Database, jobId: Int64) throws { @@ -173,22 +174,19 @@ class DBJob: Record, DBModelProtocol { } // MARK: - Make Requests - static func requestFeature(jobId: String) -> QueryInterfaceRequest { - return DBFeature.request(targetId: jobId, targetType: .job) - } static func request(jobId: Int64) -> QueryInterfaceRequest { - return DBJob.filter(Column(JobTable.Column.id.title) == jobId) + return DBJob.filter(Column.id == jobId) } private static func sqlJobs(for messageLocalId: String, type: JobType = .schedule) -> String { let jobTable = JobTable.name - let jobType = "\(jobTable).\(JobTable.Column.type.title)" - let jobId = "\(jobTable).\(JobTable.Column.id.title)" + let jobType = "\(jobTable).\(Column.type)" + let jobId = "\(jobTable).\(Column.id)" let jobMessageTable = JobMessageTable.name - let jodMessageJobId = "\(jobMessageTable).\(JobMessageTable.Column.jobId.title)" - let jobMessageLocalId = "\(jobMessageTable).\(JobMessageTable.Column.localId.title)" + let jodMessageJobId = "\(jobMessageTable).\(Column.jobId)" + let jobMessageLocalId = "\(jobMessageTable).\(Column.localId)" let sql = """ select \(jobTable).* @@ -199,5 +197,19 @@ class DBJob: Record, DBModelProtocol { return sql } +} + +extension DBJob { + + struct QueryArgs: CompositeQueryArgs { + let type: DBJob.JobType + let args: AnyQueryArgs + + func makeTypedRequest() -> QueryInterfaceRequest { + return AnyQueryArgs(args: args) + .filter { $0.filter(Column.type == self.type.rawValue) } + .makeTypedRequest() + } + } } diff --git a/Nynja/DB/Models/DBJobMessage.swift b/Nynja/DB/Models/DBJobMessage.swift index 705505d3130f0225fd88605808f4516fb366435f..fd146f3c074c922db3ad2a2018f5698a2e19eafb 100644 --- a/Nynja/DB/Models/DBJobMessage.swift +++ b/Nynja/DB/Models/DBJobMessage.swift @@ -9,7 +9,7 @@ import Foundation import GRDBCipher -final class DBJobMessage: Record, DBModelProtocol { +final class DBJobMessage: Record, DBModel { var id: Int64? var container: String? @@ -119,7 +119,7 @@ final class DBJobMessage: Record, DBModelProtocol { return messages } - private func construct(_ db: Database) throws { + func construct(_ db: Database) throws { if let id = self.id, case let targetId = String(id) { self.files = try DBDesc.descs(targetId: targetId, targetType: .job, db: db) } diff --git a/Nynja/DB/Models/DBLink.swift b/Nynja/DB/Models/DBLink.swift index a10c2009f1baf9e1685eaf6e711819d4f8aae331..2df604df64a5c34389f911f68f891aeba4c24d75 100644 --- a/Nynja/DB/Models/DBLink.swift +++ b/Nynja/DB/Models/DBLink.swift @@ -52,7 +52,7 @@ final class DBLink: Codable { } -extension DBLink: DBModelProtocol { +extension DBLink: DBModel { static var databaseTableName: String { return LinkTable.name diff --git a/Nynja/DB/Models/DBMember.swift b/Nynja/DB/Models/DBMember.swift index 2b456cbcb55bab7d3f707ed44e840a73af12b98c..abbba718122601b1a10ed3753d33c4c18f719c9f 100644 --- a/Nynja/DB/Models/DBMember.swift +++ b/Nynja/DB/Models/DBMember.swift @@ -8,7 +8,7 @@ import GRDBCipher -class DBMember: Record, DBModelProtocol { +final class DBMember: Record, DBModel { var id: Int64 var container: String? @@ -145,15 +145,6 @@ class DBMember: Record, DBModelProtocol { try member.construct(db) return member } - - static func all(from db: Database) throws -> [DBMember] { - let members = try DBMember.fetchAll(db) - try members.forEach { - try $0.construct(db) - } - - return members - } static func member(from db: Database, id: Int64) throws -> DBMember? { guard let member = try DBMember.fetchOne(db, key: id) else { @@ -164,21 +155,21 @@ class DBMember: Record, DBModelProtocol { return member } + static func member(from db: Database, roomId: String, phoneId: String) throws -> DBMember? { + return try requestMember(roomId: roomId, phoneIds: [phoneId]).fetchOneConstructed(db) + } + static func members(from db: Database, roomId: String, isAdmin: Bool = false) throws -> [DBMember] { - let members = try requestMember(roomId: roomId, isAdmin: isAdmin).fetchAll(db) - try members.forEach { try $0.construct(db) } - return members + return try requestMember(roomId: roomId, isAdmin: isAdmin).fetchAllConstructed(db) } - static func member(from db: Database, roomId: String, phoneId: String) throws -> DBMember? { - let member = try requestMember(roomId: roomId, phoneId: phoneId).fetchOne(db) - try member?.construct(db) - return member + static func members(from db: Database, roomId: String, phoneIds: [String]) throws -> [DBMember] { + return try requestMember(roomId: roomId, phoneIds: phoneIds).fetchAllConstructed(db) } - private func construct(_ db: Database) throws { + func construct(_ db: Database) throws { let memberId = "\(self.id)" - self.features = (try? DBMember.requestFeature(targetId: memberId).fetchAll(db)) ?? [] + self.features = (try? DBFeature.request(targetId: memberId, targetType: .member).fetchAll(db)) ?? [] self.services = (try? DBService.request(targetId: memberId, targetType: .member).fetchAll(db)) ?? [] if let feedId = self.feedId, let feedType = self.feedType { @@ -194,29 +185,29 @@ class DBMember: Record, DBModelProtocol { @discardableResult func deleteAggregate(_ db: Database) throws -> Bool { let memberId = "\(self.id)" - try DBMember.requestFeature(targetId: memberId).deleteAll(db) - - try? DBService.request(targetId: memberId, targetType: .member).deleteAll(db) + try DBFeature.deleteAll(db, targetId: memberId, targetType: .member) + try? DBService.deleteAll(db, targetId: memberId, targetType: .member) return try self.delete(db) } // MARK: - Requests - static private func requestFeature(targetId: String) -> QueryInterfaceRequest { - return DBFeature.request(targetId: targetId, targetType: DBFeature.TargetType.member) - } - static private func requestMember(roomId: String, phoneId: String) -> AnyTypedRequest { + static private func requestMember(roomId: String, phoneIds: [String]) -> AnyTypedRequest { let memberTable = MemberTable.name let roomMemberTable = RoomMemberTable.name + let phoneIds = phoneIds + .map { "'\($0)'" } + .joinedByComma() + let sql = """ SELECT \(memberTable).* FROM \(roomMemberTable) LEFT JOIN \(memberTable) ON \(roomMemberTable).\(RoomMemberTable.Column.memberId.title) = \(memberTable).\(MemberTable.Column.id.title) WHERE \(RoomMemberTable.Column.roomId.title) = '\(roomId)' - AND \(MemberTable.Column.phoneId.title) = '\(phoneId)' + AND \(MemberTable.Column.phoneId.title) in (\(phoneIds)) """ return SQLRequest(sql).asRequest(of: DBMember.self) diff --git a/Nynja/DB/Models/DBMessage.swift b/Nynja/DB/Models/DBMessage.swift index b4074042489fa6662da89fe7c5ce706a9ab986bd..9c0ec7a6b3cbbf84cbc3ced38dfe7e7c8579632e 100644 --- a/Nynja/DB/Models/DBMessage.swift +++ b/Nynja/DB/Models/DBMessage.swift @@ -10,7 +10,7 @@ import GRDBCipher private let tableName = MessageTable.name -final class DBMessage: Record, DBModelProtocol { +final class DBMessage: Record, DBModel { var container: String? var feedId: Int64? diff --git a/Nynja/DB/Models/DBMessageAction.swift b/Nynja/DB/Models/DBMessageAction.swift index fa37566035fd3dac8b0a2df559002ddf4a2cfdcc..099a5176bbfd25d711f5c81a2274cbdbe2837c6e 100644 --- a/Nynja/DB/Models/DBMessageAction.swift +++ b/Nynja/DB/Models/DBMessageAction.swift @@ -8,7 +8,7 @@ import GRDBCipher -final class DBMessageAction: Record, DBModelProtocol { +final class DBMessageAction: Record, DBModel { enum Action: String { case delete = "delete" @@ -51,7 +51,7 @@ final class DBMessageAction: Record, DBModelProtocol { } - // MARK: - DBModelProtocol + // MARK: - DBModel func saveAggregate(_ db: Database) throws { try self.save(db) diff --git a/Nynja/DB/Models/DBMessageEditAction.swift b/Nynja/DB/Models/DBMessageEditAction.swift index ee2ff754df4f58b1ea4ee0b031cffa03ecd2efb3..0e1bb65e2942a805ececa76c23bc35c4bba171df 100644 --- a/Nynja/DB/Models/DBMessageEditAction.swift +++ b/Nynja/DB/Models/DBMessageEditAction.swift @@ -8,7 +8,7 @@ import GRDBCipher -final class DBMessageEditAction: Record, DBModelProtocol { +final class DBMessageEditAction: Record, DBModel { var messageId: MessageServerId var payload: String @@ -45,7 +45,7 @@ final class DBMessageEditAction: Record, DBModelProtocol { } - // MARK: - DBModelProtocol + // MARK: - DBModel func saveAggregate(_ db: Database) throws { try save(db) diff --git a/Nynja/DB/Models/DBMessageLink.swift b/Nynja/DB/Models/DBMessageLink.swift index 307000ddd7060e03fde196b12f66bde75a10667e..12f3236b1aa9aefd65ef27289e04bd33f1af57a2 100644 --- a/Nynja/DB/Models/DBMessageLink.swift +++ b/Nynja/DB/Models/DBMessageLink.swift @@ -8,7 +8,7 @@ import GRDBCipher -class DBMessageLink: Record, DBModelProtocol { +final class DBMessageLink: Record, DBModel { var value: String? var feedId: String? @@ -56,7 +56,7 @@ class DBMessageLink: Record, DBModelProtocol { container[MessageLinkTable.Column.feedType.title] = feedType } - // MARK: - DBModelProtocol + // MARK: - DBModel func saveAggregate(_ db: Database) throws { try self.save(db) } diff --git a/Nynja/DB/Models/DBMuc.swift b/Nynja/DB/Models/DBMuc.swift index a2be04a71780e969f304295d444fe9521dc30399..3d35b1dd4577fe63e3012cb85fe2b2e77af457c7 100644 --- a/Nynja/DB/Models/DBMuc.swift +++ b/Nynja/DB/Models/DBMuc.swift @@ -8,7 +8,7 @@ import GRDBCipher -class DBMuc: Record, FeedProtocol { +final class DBMuc: Record, FeedProtocol { var id: Int64? var name: String @@ -49,7 +49,7 @@ class DBMuc: Record, FeedProtocol { container[MucTable.Column.name.title] = name } - // MARK: - DBModelProtocol + // MARK: - DBModel func saveAggregate(_ db: Database) throws { let nameColumn = Column(MucTable.Column.name.title) diff --git a/Nynja/DB/Models/DBP2p.swift b/Nynja/DB/Models/DBP2p.swift index b57d36ea600db77e7d1e892813d2d4b5065b2dc6..1742c46caaec95c900d088d285f1fdf39f3896ea 100644 --- a/Nynja/DB/Models/DBP2p.swift +++ b/Nynja/DB/Models/DBP2p.swift @@ -8,7 +8,7 @@ import GRDBCipher -class DBP2p: Record, FeedProtocol { +final class DBP2p: Record, FeedProtocol { var id: Int64? var from: String @@ -53,7 +53,7 @@ class DBP2p: Record, FeedProtocol { container[P2pTable.Column.to.title] = to } - // MARK: - DBModelProtocol + // MARK: - DBModel func saveAggregate(_ db: Database) throws { let fromColumn = Column(P2pTable.Column.from.title) let toColumn = Column(P2pTable.Column.to.title) diff --git a/Nynja/DB/Models/DBProfile.swift b/Nynja/DB/Models/DBProfile.swift index 7c7ef4ff5c345fbb61268b396e2a76aa9ca30d33..5c89cf9ffb064066b87d090ffc1b3e0d8d55043f 100644 --- a/Nynja/DB/Models/DBProfile.swift +++ b/Nynja/DB/Models/DBProfile.swift @@ -8,7 +8,7 @@ import GRDBCipher -class DBProfile: Record, DBModelProtocol { +final class DBProfile: Record, DBModel { var phone: String var update: Int64 @@ -80,23 +80,17 @@ class DBProfile: Record, DBModelProtocol { dbProfile.services = try DBService.request(targetId: dbProfile.phone, targetType: .profile).fetchAll(db) dbProfile.rosters = try DBRoster.rosters(from: db, profileId: dbProfile.phone) - dbProfile.features = try request(targetId: dbProfile.phone).fetchAll(db) + dbProfile.features = try DBFeature.request(targetId: dbProfile.phone, targetType: .profile).fetchAll(db) return dbProfile } @discardableResult func deleteAggregate(_ db: Database) throws -> Bool { - try DBProfile.request(targetId: self.phone).deleteAll(db) + try DBFeature.deleteAll(db, targetId: phone, targetType: .profile) + try DBService.deleteAll(db, targetId: phone, targetType: .profile) - try DBService.request(targetId: self.phone, targetType: .profile).deleteAll(db) - - return try self.delete(db) - } - - static private func request(targetId: String) -> QueryInterfaceRequest { - return DBFeature.request(targetId: targetId, targetType: DBFeature.TargetType.profile) + return try delete(db) } - } diff --git a/Nynja/DB/Models/DBRecentSticker.swift b/Nynja/DB/Models/DBRecentSticker.swift index 8c1f0315b4fbff862251c30afd690c63c3c0b296..07fa83dc4df7217c2735400cfebf4aa737863fcc 100644 --- a/Nynja/DB/Models/DBRecentSticker.swift +++ b/Nynja/DB/Models/DBRecentSticker.swift @@ -8,7 +8,7 @@ import GRDBCipher -final class DBRecentSticker: Codable, DBModelProtocol { +final class DBRecentSticker: Codable, DBModel { var id: Int64? var stickerId: String diff --git a/Nynja/DB/Models/DBRoom.swift b/Nynja/DB/Models/DBRoom.swift index a41b3696d4b7431d3b790531f0bf788f39a76f88..1424c99d2c253b312ead585e94a423b58bca8c92 100644 --- a/Nynja/DB/Models/DBRoom.swift +++ b/Nynja/DB/Models/DBRoom.swift @@ -8,7 +8,7 @@ import GRDBCipher -class DBRoom: Record, DBModelProtocol { +final class DBRoom: Record, DBExtendedModel { var id: String var name: String @@ -32,7 +32,11 @@ class DBRoom: Record, DBModelProtocol { var rosterId: Int64? var messageId: String? - var message: DBMessage? + var message: DBMessage? { + didSet { + messageId = message?.localId + } + } var members: [DBMember] = [] var admins: [DBMember] = [] @@ -40,6 +44,10 @@ class DBRoom: Record, DBModelProtocol { var files: [DBDesc] = [] var links: [DBLink] = [] + var serverModel: Room { + return Room(room: self) + } + init?(room: Room, rosterId: Int64?) { guard let id = room.id else { return nil } @@ -63,7 +71,7 @@ class DBRoom: Record, DBModelProtocol { // Message if let message = room.message { self.message = DBMessage(message: message) - self.messageId = room.message?.msg_id + self.messageId = self.message?.localId } else { self.messageId = room.lastMessageId } @@ -129,7 +137,7 @@ class DBRoom: Record, DBModelProtocol { // MARK: - Modification func saveAggregate(_ db: Database) throws { - try saveMessage(db) + try message?.saveAggregate(db) reader = calculateReader() try save(db) @@ -153,14 +161,6 @@ class DBRoom: Record, DBModelProtocol { return readers.max(except: selfReader) } - private func saveMessage(_ db: Database) throws { - try message?.saveAggregate(db) - - if message != nil || messageId == nil { - messageId = message?.localId - } - } - private func saveMembers(_ db: Database) throws { try admins.forEach { try $0.saveAggregate(db) @@ -177,17 +177,15 @@ class DBRoom: Record, DBModelProtocol { @discardableResult func deleteAggregate(_ db: Database) throws -> Bool { - try DBRoom.requestFeature(targetId: self.id).deleteAll(db) - try DBDesc.deleteAll(db, targetId: self.id, targetType: .room) + try DBFeature.deleteAll(db, targetId: id, targetType: .room) + try DBDesc.deleteAll(db, targetId: id, targetType: .room) try DBMuc.filter(MucTable.Column.name.title == self.name).deleteAll(db) return try self.delete(db) } // MARK: - Fetch static func room(from db: Database, rowId: Int64) throws -> DBRoom? { - let room = try DBRoom.filter(Column.rowID == rowId).fetchOne(db) - try room?.construct(db) - return room + return try DBRoom.filter(Column.rowID == rowId).fetchOneConstructed(db) } static func room(from db: Database, id: String, fullModel: Bool = true) throws -> DBRoom? { @@ -199,13 +197,6 @@ class DBRoom: Record, DBModelProtocol { return room } - static func rooms(_ db: Database, contactId: String, isAdmin: Bool, type: String?) throws -> [DBRoom] { - let sql = sqlContactRooms(contactId, isAdmin: isAdmin, type: type) - let rooms = try SQLRequest(sql).asRequest(of: DBRoom.self).fetchAll(db) - try rooms.forEach { try $0.construct(db) } - return rooms - } - static func rooms(_ db: Database, rosterId: Int64, type: String?) throws -> [DBRoom] { let rosterIdColumn = Column(RoomTable.Column.rosterId.title) return try rooms(db, type: type, predicate: rosterIdColumn == rosterId) @@ -213,10 +204,14 @@ class DBRoom: Record, DBModelProtocol { static func rooms(_ db: Database, ids: [String], type: String?) throws -> [DBRoom] { let idColumn = Column(RoomTable.Column.id.title) - return try rooms(db, type: type, predicate: ids.contains(idColumn)) + return try rooms( + db, + type: type, + predicate: ids.contains(idColumn), + onlySenderAndSelf: true) } - private static func rooms(_ db: Database, type: String?, predicate: SQLExpressible) throws -> [DBRoom] { + private static func rooms(_ db: Database, type: String?, predicate: SQLExpressible, onlySenderAndSelf: Bool = false) throws -> [DBRoom] { let typeColumn = Column(RoomTable.Column.type.title) let statusColumn = Column(RoomTable.Column.status.title) @@ -226,67 +221,191 @@ class DBRoom: Record, DBModelProtocol { } let rooms = try DBRoom.filter(predicate).fetchAll(db) - try rooms.forEach { try $0.construct(db) } + try rooms.forEach { try $0.construct(db, onlySenderAndSelf: onlySenderAndSelf) } return rooms } - private func construct(_ db: Database) throws { + static func rooms(_ db: Database, args: RequestArgs) throws -> [DBRoom] { + let rooms = try request(with: args).fetchAll(db) + try rooms.forEach { try $0.construct(db, onlySenderAndSelf: true) } + return rooms + } + + func construct(_ db: Database) throws { + try construct(db, onlySenderAndSelf: false) + } + + private func construct(_ db: Database, onlySenderAndSelf: Bool) throws { if let messageId = self.messageId { self.message = try DBMessage.message(db, localId: messageId) } - self.admins = try DBMember.members(from: db, roomId: id, isAdmin: true) - self.members = try DBMember.members(from: db, roomId: id) + var phoneIds: [String] = [] + if onlySenderAndSelf { + message?.from.map { phoneIds.append($0) } + StorageService.sharedInstance.phoneId.map { phoneIds.append($0) } + } + + try constructMembers(db, phoneIds: phoneIds) - self.features = (try? DBRoom.requestFeature(targetId: id).fetchAll(db)) ?? [] + self.features = (try? DBFeature.request(targetId: id, targetType: .room).fetchAll(db)) ?? [] self.files = DBRoom.fetchDescs(db, roomId: id) self.links = (try? DBLink.links(db, roomId: id)) ?? [] } + private func constructMembers(_ db: Database, phoneIds: [String]) throws { + guard !phoneIds.isEmpty else { + self.admins = try DBMember.members(from: db, roomId: id, isAdmin: true) + self.members = try DBMember.members(from: db, roomId: id) + return + } + + let members = try DBMember.members(from: db, roomId: id, phoneIds: phoneIds) + + let statuses: [MemberStatus] = [.admin, .owner] + let statusStrings = statuses.map { $0.rawValue } + + self.admins = [] + self.members = [] + + members.forEach { member in + if let status = member.status, statusStrings.contains(status) { + self.admins.append(member) + } else { + self.members.append(member) + } + } + } + static func fetchDescs(_ db: Database, roomId: String) -> [DBDesc] { return (try? descs(targetId: roomId, db: db)) ?? [] } - private static func sqlContactRooms(_ contactId: String, isAdmin: Bool, type: String?) -> String { + + // MARK: - Make Requests + + static private func descs(targetId: String, db: Database) throws -> [DBDesc] { + return try DBDesc.descs(targetId: targetId, targetType: .room, db: db) + } + + private static func request(with args: RequestArgs) -> AnyTypedRequest { let roomTable = RoomTable.name - let roomMemberTable = RoomMemberTable.name + let messageTable = MessageTable.name let memberTable = MemberTable.name + var sql = """ + select \(roomTable).* + from \(roomTable) + \(messageJoin(messageTable: messageTable, roomTable: roomTable)) + \(memberJoin(ownerId: args.ownerId, memberTable: memberTable, roomTable: roomTable)) + where + \(condition(rosterId: args.rosterId, roomTable: roomTable)) + and \(condition(type: args.type, roomTable: roomTable)) + \(condition(onlyUnread: args.onlyUnread, roomTable: roomTable)) + \(condition(ownerId: args.ownerId)) + \(orderBy(messageTable: messageTable)) + """ + + if let limit = args.limit { + sql.append(" limit \(limit)") + } + + if let offset = args.offset { + sql.append(" offset \(offset)") + } + + return SQLRequest(sql).asRequest(of: DBRoom.self) + } + + private static func messageJoin(messageTable: String, roomTable: String) -> String { + let messageIdColumn = "\(roomTable).\(Column.messageId)" + let localIdColumn = "\(messageTable).\(Column.localId)" + + return "left join \(messageTable) on \(messageIdColumn) == \(localIdColumn)" + } + + private static func memberJoin(ownerId: String?, memberTable: String, roomTable: String) -> String { + guard ownerId != nil else { + return "" + } + + let roomMemberTable = RoomMemberTable.name + let roomIdColumn = "\(roomTable).\(RoomTable.Column.id.title)" let roomMemberRoomIdColumn = "\(roomMemberTable).\(RoomMemberTable.Column.roomId.title)" let memberIdColumn = "\(memberTable).\(MemberTable.Column.id.title)" let rommMemberMemberIdColumn = "\(roomMemberTable).\(RoomMemberTable.Column.memberId.title)" + - let memberPhoneIdColumn = "\(memberTable).\(MemberTable.Column.phoneId.title)" - - var sql = """ - select \(roomTable).* - from \(roomTable) - left join \(roomMemberTable) on \(roomIdColumn) == \(roomMemberRoomIdColumn) - left join \(memberTable) on \(memberIdColumn) == \(rommMemberMemberIdColumn) - where \(memberPhoneIdColumn) == '\(contactId)' + return """ + left join \(roomMemberTable) on \(roomIdColumn) == \(roomMemberRoomIdColumn) + left join \(memberTable) on \(memberIdColumn) == \(rommMemberMemberIdColumn) """ + } + + private static func condition(rosterId: Int64, roomTable: String) -> String { + let rosterIdColumn = "\(roomTable).\(Column.rosterId)" + return "\(rosterIdColumn) == \(rosterId)" + } + + private static func condition(type: String, roomTable: String) -> String { + let typeColumn = "\(roomTable).\(Column.type)" + return "\(typeColumn) == '\(type)'" + } + + private static func condition(onlyUnread: Bool, roomTable: String) -> String { + let unreadColumn = "\(roomTable).\(Column.unread)" + return onlyUnread ? "and \(unreadColumn) > 0" : "" + } + + private static func condition(ownerId: String?) -> String { + guard let ownerId = ownerId else { + return "" + } + + let memberTable = MemberTable.name + let memberPhoneIdColumn = "\(memberTable).\(MemberTable.Column.phoneId.title)" let memberStatusColumn = "\(memberTable).\(MemberTable.Column.status.title)" - if isAdmin { - sql.append(contentsOf: "and \(memberStatusColumn) in ('admin', 'owner')") - } - let roomTypeColumn = "\(roomTable).\(RoomTable.Column.type.title)" - if let type = type { - sql.append(contentsOf: "and \(roomTypeColumn) == '\(type)'") - } - - return sql + return """ + and \(memberPhoneIdColumn) == '\(ownerId)'" + and \(memberStatusColumn) in ('admin', 'owner') + """ } - // MARK: - Make Requests - static private func requestFeature(targetId: String) -> QueryInterfaceRequest { - return DBFeature.request(targetId: targetId, targetType: .room) + private static func orderBy(messageTable: String) -> String { + let createdColumn = "\(messageTable).\(Column.created)" + return "order by \(createdColumn) desc" } +} + + +// MARK: - + +extension DBRoom { - static private func descs(targetId: String, db: Database) throws -> [DBDesc] { - return try DBDesc.descs(targetId: targetId, targetType: .room, db: db) + struct RequestArgs { + let rosterId: Int64 + let type: String + let onlyUnread: Bool + let ownerId: String? + let limit: Int? + let offset: Int? + + init(rosterId: Int64, + type: String, + onlyUnread: Bool = false, + ownerId: String? = nil, + limit: Int? = nil, + offset: Int? = nil) { + self.rosterId = rosterId + self.type = type + self.onlyUnread = onlyUnread + self.ownerId = ownerId + self.limit = limit + self.offset = offset + } } } diff --git a/Nynja/DB/Models/DBRoomMember.swift b/Nynja/DB/Models/DBRoomMember.swift index 95d1502219621c8da8741fac3a4281d9b4b251df..064920885dd954bf29bc58f5974af0b19a7ed571 100644 --- a/Nynja/DB/Models/DBRoomMember.swift +++ b/Nynja/DB/Models/DBRoomMember.swift @@ -8,7 +8,7 @@ import GRDBCipher -class DBRoomMember: Record { +final class DBRoomMember: Record { var roomId: String var memberId: Int64 diff --git a/Nynja/DB/Models/DBRoster.swift b/Nynja/DB/Models/DBRoster.swift index c76950802b688ef068fcd5c9ec8cdeeda8f3ef88..ecf212027055a321e221e5f9457258725fced6a9 100644 --- a/Nynja/DB/Models/DBRoster.swift +++ b/Nynja/DB/Models/DBRoster.swift @@ -8,7 +8,7 @@ import GRDBCipher -class DBRoster: Record, DBModelProtocol { +final class DBRoster: Record, DBModel { var id: Int64 var names: String @@ -24,9 +24,7 @@ class DBRoster: Record, DBModelProtocol { var contacts: [DBContact] = [] var rooms: [DBRoom] = [] - // TODO: Implement tags var stars: [DBStar] = [] -// var tag: [DBTag] = [] init?(roster: Roster, profileId: String) { guard let id = roster.id else { @@ -124,8 +122,7 @@ class DBRoster: Record, DBModelProtocol { } @discardableResult - func deleteAggragete(_ db: Database) throws -> Bool { + func deleteAggregate(_ db: Database) throws -> Bool { return try self.delete(db) - } - + } } diff --git a/Nynja/DB/Models/DBService.swift b/Nynja/DB/Models/DBService.swift index 0a6eafa869abb8cff4776374286460d406dedd29..849aca8ae0338c53b174c1a19b094338e2ca5a86 100644 --- a/Nynja/DB/Models/DBService.swift +++ b/Nynja/DB/Models/DBService.swift @@ -8,7 +8,7 @@ import GRDBCipher -class DBService: Codable { +final class DBService: Codable { var id: String var type: String @@ -58,6 +58,9 @@ class DBService: Codable { self.targetType = targetType.rawValue } + static func deleteAll(_ db: Database, targetId: String, targetType: TargetType) throws { + try request(targetId: targetId, targetType: targetType).deleteAll(db) + } } extension DBService: RowConvertible, Persistable { diff --git a/Nynja/DB/Models/DBStar.swift b/Nynja/DB/Models/DBStar.swift index 6f21437a7ccd263468e0092d5b3d7937908f5fbb..3551483edfdd5cc198fa54a11c64cfdcbf168b6f 100644 --- a/Nynja/DB/Models/DBStar.swift +++ b/Nynja/DB/Models/DBStar.swift @@ -9,16 +9,26 @@ import Foundation import GRDBCipher -final class DBStar: Record, DBModelProtocol { +final class DBStar: Record, DBExtendedModel { var id: Int64? var clientId: String? var rosterId: Int64? - var message: DBStarMessage? + var message: DBStarMessage? { + didSet { + messageID = message?.localId + } + } var messageID: String? var status: String? + var serverModel: Star { + return Star(star: self) + } + + // MARK: - Mapping + init?(star: Star) { self.id = star.id self.clientId = star.client_id ?? IdBuilder(format: .starClientId) @@ -28,6 +38,7 @@ final class DBStar: Record, DBModelProtocol { self.status = StringAtom.string(star.status) if let msg = star.message { self.message = DBStarMessage(message: msg) + self.messageID = message?.localId } super.init() } @@ -44,71 +55,39 @@ final class DBStar: Record, DBModelProtocol { } } + // MARK: - Record + override static var databaseTableName: String { return StarTable.name } required init(row: Row) { - id = row[StarTable.Column.id.title] - clientId = row[StarTable.Column.clientId.title] - rosterId = row[StarTable.Column.rosterId.title] - status = row[StarTable.Column.status.title] - messageID = row[StarTable.Column.messageId.title] + id = row[Column.id] + clientId = row[Column.clientId] + rosterId = row[Column.rosterId] + status = row[Column.status] + messageID = row[Column.messageId] super.init() } override func encode(to container: inout PersistenceContainer) { - container[StarTable.Column.id.title] = id - container[StarTable.Column.clientId.title] = clientId - container[StarTable.Column.rosterId.title] = rosterId - container[StarTable.Column.status.title] = status - container[StarTable.Column.messageId.title] = messageID + container[Column.id] = id + container[Column.clientId] = clientId + container[Column.rosterId] = rosterId + container[Column.status] = status + container[Column.messageId] = messageID } - // MARK: - DBModelProtocol + + // MARK: - DBModel + func saveAggregate(_ db: Database) throws { try message?.saveAggregate(db) - messageID = message?.localId try self.save(db) } - static func star(_ db: Database, rowId: Int64) throws -> DBStar? { - let star = try DBStar.filter(Column.rowID == rowId).fetchOne(db) - try star?.construct(db) - return star - } - - static func star(_ db: Database, clientId: String) throws -> DBStar? { - let clientIdColumn = Column(StarTable.Column.clientId.title) - let star = try DBStar.filter(clientIdColumn == clientId).fetchOne(db) - try star?.construct(db) - return star - } - - static func stars(from db: Database, rosterId: Int64) throws -> [DBStar] { - let rosterIdColumn = Column(StarTable.Column.rosterId.title) - let statusColumn = Column(StarTable.Column.status.title) - - let filter: SQLExpressible = rosterIdColumn == rosterId && statusColumn !== Star.Status.remove.rawValue - let stars = try DBStar.filter(filter).fetchAll(db) - - try stars.forEach { try $0.construct(db) } - return stars - } - - static func undeliveredStars(from db: Database, rosterId: Int64) throws -> [DBStar] { - let rosterIdColumn = Column(StarTable.Column.rosterId.title) - let starIdColumn = Column(StarTable.Column.id.title) - - let filter: SQLExpressible = rosterIdColumn == rosterId && starIdColumn == nil - let stars = try DBStar.filter(filter).fetchAll(db) - - try stars.forEach { try $0.construct(db) } - return stars - } - - private func construct(_ db: Database) throws { + func construct(_ db: Database) throws { if let messageId = messageID { message = try DBStarMessage.message(db, localId: messageId) } @@ -119,4 +98,15 @@ final class DBStar: Record, DBModelProtocol { try self.message?.deleteAggregate(db) return try self.delete(db) } + + + // MARK: - Fetching + + static func stars(from db: Database, rosterId: Int64) throws -> [DBStar] { + return try DBStar + .filter(Column.rosterId == rosterId) + .filter(Column.status !== Star.Status.remove.rawValue) + .fetchAllConstructed(db) + } + } diff --git a/Nynja/DB/Models/DBStarAction.swift b/Nynja/DB/Models/DBStarAction.swift index 9e669edbb0614bb06bcdde660eebea9a7bb35e07..7a65d5b46e8078a9b362c38b49110850e8baed99 100644 --- a/Nynja/DB/Models/DBStarAction.swift +++ b/Nynja/DB/Models/DBStarAction.swift @@ -8,7 +8,7 @@ import GRDBCipher -final class DBStarAction: Record, DBModelProtocol { +final class DBStarAction: Record, DBModel { enum Action: String { case delete = "delete" } @@ -23,14 +23,14 @@ final class DBStarAction: Record, DBModelProtocol { } required init(row: Row) { - starLocalId = row[StarActionTable.Column.starId.title] - action = Action(rawValue: row[StarActionTable.Column.action.title])! + starLocalId = row[Column.starId] + action = Action(rawValue: row[Column.action])! super.init() } override func encode(to container: inout PersistenceContainer) { - container[StarActionTable.Column.starId.title] = starLocalId - container[StarActionTable.Column.action.title] = action.rawValue + container[Column.starId] = starLocalId + container[Column.action] = action.rawValue } override static var databaseTableName: String { @@ -38,8 +38,8 @@ final class DBStarAction: Record, DBModelProtocol { } static func starAction(_ db: Database, starLocalId: String) throws -> DBStarAction? { - let starIdColumn = Column(StarActionTable.Column.starId.title) - let filter: SQLExpressible = starIdColumn == starLocalId - return try DBStarAction.filter(filter).fetchOne(db) + return try DBStarAction + .filter(Column.starId == starLocalId) + .fetchOne(db) } } diff --git a/Nynja/DB/Models/DBStarMessage.swift b/Nynja/DB/Models/DBStarMessage.swift index 00e7da110ef0f2c0866f2b921f35f6a4b3ecefaf..e75a2c273dbc54d08c7eaf6b37b32d28ebb96ea8 100644 --- a/Nynja/DB/Models/DBStarMessage.swift +++ b/Nynja/DB/Models/DBStarMessage.swift @@ -9,7 +9,7 @@ import Foundation import GRDBCipher -final class DBStarMessage: Record, DBModelProtocol { +final class DBStarMessage: Record, DBModel { var container: String? var feedId: Int64? @@ -68,7 +68,6 @@ final class DBStarMessage: Record, DBModelProtocol { self.status = message.statusString self.files = (message.files ?? []).compactMap { DBDesc(desc: $0, targetId: nil, targetType: .star) } -// self.repliedBy = message.repliedby?.myJoined(separator: DBMessage.separator) self.mentioned = message.mentioned?.joinedByComma() super.init() @@ -235,7 +234,7 @@ final class DBStarMessage: Record, DBModelProtocol { return nil } - private func construct(_ db: Database) throws { + func construct(_ db: Database) throws { files = try DBDesc.descs(targetId: localId, targetType: .star, db: db) guard let feedId = feedId, let feedType = feedType, let type = FeedType(rawValue: feedType) else { @@ -264,7 +263,9 @@ final class DBStarMessage: Record, DBModelProtocol { } } + // MARK: - Delete + @discardableResult func deleteAggregate(_ db: Database) throws -> Bool { try DBDesc.deleteAll(db, targetId: localId, targetType: .star) diff --git a/Nynja/DB/Models/DBStickerPack.swift b/Nynja/DB/Models/DBStickerPack.swift index a0818e949b6e8c8106346c10f7c408197f4ece7b..05d4e4c360cf13e341d6b21341dc82a586bf7319 100644 --- a/Nynja/DB/Models/DBStickerPack.swift +++ b/Nynja/DB/Models/DBStickerPack.swift @@ -8,7 +8,7 @@ import GRDBCipher -final class DBStickerPack: Record, DBModelProtocol { +final class DBStickerPack: Record, DBModel { // MARK: - Fields @@ -74,7 +74,7 @@ final class DBStickerPack: Record, DBModelProtocol { } - // MARK: - DBModelProtocol + // MARK: - DBModel func saveAggregate(_ db: Database) throws { try save(db) @@ -124,7 +124,7 @@ final class DBStickerPack: Record, DBModelProtocol { // MARK: Foreign Constraints - private func construct(_ db: Database) throws { + func construct(_ db: Database) throws { stickers = try DBStickerPack.stickers(from: db, targetId: String(id)) } diff --git a/Nynja/DB/Models/DBSyncFile.swift b/Nynja/DB/Models/DBSyncFile.swift index 943111b1faf71d435edf353602ff35955f3c864c..bd291d4c0eb9c4af6986186ac42a88000e3d8012 100644 --- a/Nynja/DB/Models/DBSyncFile.swift +++ b/Nynja/DB/Models/DBSyncFile.swift @@ -8,7 +8,7 @@ import GRDBCipher -class DBSyncFile: Record { +final class DBSyncFile: Record { var id: Int? var serverLink: String? diff --git a/Nynja/DB/Protocols/DBModelConvertible.swift b/Nynja/DB/Protocols/DBModelConvertible.swift index 658c1536253bcf87c68b48bbf0369063ea0db87a..b2e844f407bb46d39465c4bbb78b6aaceeee0ebd 100644 --- a/Nynja/DB/Protocols/DBModelConvertible.swift +++ b/Nynja/DB/Protocols/DBModelConvertible.swift @@ -8,6 +8,6 @@ protocol DBModelConvertible { - var databaseModel: DBModelProtocol? { get } + var databaseModel: DBModel? { get } } diff --git a/Nynja/DB/QueryArgs.swift b/Nynja/DB/QueryArgs.swift new file mode 100644 index 0000000000000000000000000000000000000000..31a59b4da8146363aa2efc36c9c214a765b2a34a --- /dev/null +++ b/Nynja/DB/QueryArgs.swift @@ -0,0 +1,78 @@ +// +// QueryArgs.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/7/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import GRDBCipher + +protocol QueryRequestMakeable { + func makeRequest() -> Request +} + +protocol QueryArgs: QueryRequestMakeable { + var limit: Int? { get } + var offset: Int? { get } + var orderingTerms: [SQLOrderingTerm]? { get } +} + +protocol TypedQueryRequestMakeable { + associatedtype Model: DBModel + func makeTypedRequest() -> QueryInterfaceRequest +} + +protocol CompositeQueryArgs: TypedQueryRequestMakeable { + // TODO: add filter func... + var args: AnyQueryArgs { get } +} + +typealias QueryFilter = (QueryInterfaceRequest) -> QueryInterfaceRequest + +struct AnyQueryArgs: QueryArgs, TypedQueryRequestMakeable { + let limit: Int? + let offset: Int? + let orderingTerms: [SQLOrderingTerm]? + + private(set) var filters: [QueryFilter] = [] + + init(limit: Int? = nil, + offset: Int? = nil, + orderingTerms: [SQLOrderingTerm]? = nil, + filters: [QueryFilter] = []) { + self.limit = limit + self.offset = offset + self.orderingTerms = orderingTerms + self.filters = filters + } + + init(args: QueryArgs, filters: [QueryFilter] = []) { + self.init( + limit: args.limit, + offset: args.offset, + orderingTerms: args.orderingTerms) + } + + func filter(_ filter: @escaping QueryFilter) -> AnyQueryArgs { + var filters = self.filters + filters.append(filter) + return AnyQueryArgs(args: self, filters: filters) + } + + func makeTypedRequest() -> QueryInterfaceRequest { + var query = T.all() + .performIfValueExists(limit) { $0.limit($1, offset: offset) } + .performIfValueExists(orderingTerms) { $0.order($1) } + + filters.forEach { filter in + query = filter(query) + } + + return query + } + + func makeRequest() -> Request { + return makeTypedRequest() + } +} diff --git a/Nynja/DB/RosterRelatedQueryArgs.swift b/Nynja/DB/RosterRelatedQueryArgs.swift new file mode 100644 index 0000000000000000000000000000000000000000..c7127806656048568430e0fee93775968396f454 --- /dev/null +++ b/Nynja/DB/RosterRelatedQueryArgs.swift @@ -0,0 +1,27 @@ +// +// RosterRelatedQueryArgs.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/13/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import GRDBCipher + +struct RosterRelatedQueryArgs: CompositeQueryArgs { + let rosterId: Int64 + let args: AnyQueryArgs + + init(rosterId: Int64) { + self.init(rosterId: rosterId, args: AnyQueryArgs()) + } + + init(rosterId: Int64, args: AnyQueryArgs) { + self.rosterId = rosterId + self.args = args + } + + func makeTypedRequest() -> QueryInterfaceRequest { + return args.makeTypedRequest().filter(Column.rosterId == rosterId) + } +} diff --git a/Nynja/DB/Tables/Base/SharedColumn.swift b/Nynja/DB/Tables/Base/SharedColumn.swift new file mode 100644 index 0000000000000000000000000000000000000000..45050efac3c963990dadc773848876d79e512e3d --- /dev/null +++ b/Nynja/DB/Tables/Base/SharedColumn.swift @@ -0,0 +1,33 @@ +// +// Column.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/4/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import GRDBCipher + +extension Column { + static let id = Column("id") + static let phoneId = Column("phoneId") + static let rosterId = Column("rosterId") + static let messageId = Column("messageId") + static let serverId = Column("serverId") + static let localId = Column("localId") + static let starId = Column("starId") + static let feedId = Column("feedId") + + static let container = Column("container") + static let action = Column("action") + static let reader = Column("reader") + static let unread = Column("unread") + static let created = Column("created") + static let time = Column("time") + + static let status = Column("status") + static let type = Column("type") + + static let next = Column("next") + static let prev = Column("prev") +} diff --git a/Nynja/DB/Tables/ContactTable.swift b/Nynja/DB/Tables/ContactTable.swift index 3ac2717c618af8e4e9806f0140df56999c3350e7..f8a4e45605706e731c6ed827fa786f2ea37d6ce6 100644 --- a/Nynja/DB/Tables/ContactTable.swift +++ b/Nynja/DB/Tables/ContactTable.swift @@ -52,3 +52,8 @@ extension ContactTable { case rosterId } } + + +extension Column { + static let nick = Column("nick") +} diff --git a/Nynja/DB/Tables/JobMessageTable.swift b/Nynja/DB/Tables/JobMessageTable.swift index cbd6bdb48ec7eaaf51ff48425756455ad92e0e55..5f02501d10233c2dbb4475806b60b10a56aa05c5 100644 --- a/Nynja/DB/Tables/JobMessageTable.swift +++ b/Nynja/DB/Tables/JobMessageTable.swift @@ -60,3 +60,8 @@ extension JobMessageTable { case jobId } } + + +extension Column { + static let jobId = Column("jobId") +} diff --git a/Nynja/DB/Tables/JobTable.swift b/Nynja/DB/Tables/JobTable.swift index ce966f7f0a6ea5459a72940778315745f266c502..4182a226449796be93502649ecdbe30c11ee235c 100644 --- a/Nynja/DB/Tables/JobTable.swift +++ b/Nynja/DB/Tables/JobTable.swift @@ -16,31 +16,15 @@ final class JobTable: Table { static func create(in db: Database) throws { try db.create(self) { t in - t.column(JobTable.Column.id, .integer).primaryKey(onConflict: nil, autoincrement: true) - t.column(JobTable.Column.serverId, .integer) - t.column(JobTable.Column.container, .text) - t.column(JobTable.Column.feedId, .text) - t.column(JobTable.Column.prev, .integer) - t.column(JobTable.Column.next, .integer) - t.column(JobTable.Column.time, .integer) - t.column(JobTable.Column.status, .text) - t.column(JobTable.Column.type, .integer).notNull().defaults(to: DBJob.JobType.schedule.rawValue) + t.column(Column.id, .integer).primaryKey(onConflict: nil, autoincrement: true) + t.column(Column.serverId, .integer) + t.column(Column.container, .text) + t.column(Column.feedId, .text) + t.column(Column.prev, .integer) + t.column(Column.next, .integer) + t.column(Column.time, .integer) + t.column(Column.status, .text) + t.column(Column.type, .integer).notNull().defaults(to: DBJob.JobType.schedule.rawValue) } } } - -// MARK: - Column -extension JobTable { - - enum Column: Int, Describable { - case id - case serverId - case container - case feedId - case prev - case next - case time - case status - case type - } -} diff --git a/Nynja/DB/Tables/StarActionTable.swift b/Nynja/DB/Tables/StarActionTable.swift index e906491682e8fe2b7de48a131bbeffe554351100..183fe3a8fd638ca29e8b295918d7a703f6c3b10f 100644 --- a/Nynja/DB/Tables/StarActionTable.swift +++ b/Nynja/DB/Tables/StarActionTable.swift @@ -19,18 +19,9 @@ final class StarActionTable: Table { t.column(Column.starId, .text) .notNull() .primaryKey(onConflict: .replace, autoincrement: false) - .references(StarTable.name, column: StarTable.Column.clientId.title, onDelete: .cascade) + .references(StarTable.name, column: Column.clientId, onDelete: .cascade) t.column(Column.action, .text).notNull() } } } - -// MARK: Column -extension StarActionTable { - - enum Column: Int, Describable { - case starId - case action - } -} diff --git a/Nynja/DB/Tables/StarTable.swift b/Nynja/DB/Tables/StarTable.swift index 1768cacb632d68bd0f55c75b39b3b062a1eb05d6..6676cd1755ab0357918697e6e3259d0747c4ec0c 100644 --- a/Nynja/DB/Tables/StarTable.swift +++ b/Nynja/DB/Tables/StarTable.swift @@ -25,14 +25,9 @@ final class StarTable: Table { } } -// MARK: - Column -extension StarTable { - - enum Column: Int, Describable { - case id - case clientId - case status - case rosterId - case messageId - } + +extension Column { + static let clientId = Column("clientId") } + + diff --git a/Nynja/DBManagerProtocol.swift b/Nynja/DBManagerProtocol.swift index cce9695e9b8e52362c7713ef9466d583a7563f97..07c5c2434b27e1adc4ccc88534176739cf4c810c 100644 --- a/Nynja/DBManagerProtocol.swift +++ b/Nynja/DBManagerProtocol.swift @@ -27,9 +27,9 @@ protocol DBManagerProtocol { // MARK: - Perform Actions func perform(action: DatabaseAction, with model: DBModelConvertible) throws - func perform(action: DatabaseAction, with model: DBModelProtocol) throws + func perform(action: DatabaseAction, with model: DBModel) throws func perform(action: DatabaseAction, with models: [DBModelConvertible]) throws - func perform(action: DatabaseAction, with models: [DBModelProtocol]) throws + func perform(action: DatabaseAction, with models: [DBModel]) throws } diff --git a/Nynja/DBObserver.swift b/Nynja/DBObserver.swift index e5ceec7a7c9ab3d0d880b288dbebce620c5bf6d8..d7efa23fb54128a856969c4f948c2166b862a346 100644 --- a/Nynja/DBObserver.swift +++ b/Nynja/DBObserver.swift @@ -8,19 +8,29 @@ import GRDBCipher -class DBObserver: StorageObserver, TransactionObserver { +final class DBObserver: StorageObserver, TransactionObserver { + + // TODO: Notify only if subscribers exists! static let `default` = DBObserver() private init() {} + private let processingQueue = DispatchQueue( + label: String.label(withSuffix: "db-observer.processing-queue"), + qos: .utility) + + // MARK: - Properties + var subscribers: [SubscribeType: [StorageSubscriberReference]] = [:] + let isolationQueue = DispatchQueue(label: String.label(withSuffix: "db-observer.isolation-queue")) + private var allChanges: Dictionary = [:] - - private var tables: [Table.Type] { - return [ + + private let tableNames: [String] = { + let tables: [Table.Type] = [ JobTable.self, MessageTable.self, ContactTable.self, @@ -32,11 +42,8 @@ class DBObserver: StorageObserver, TransactionObserver { RecentStickerTable.self, StickerPackTable.self ] - } - - private var tableNames: [String] { return tables.map { $0.name } - } + }() // MARK: - TransactionObserver func observes(eventsOfKind eventKind: DatabaseEventKind) -> Bool { @@ -54,12 +61,24 @@ class DBObserver: StorageObserver, TransactionObserver { } func databaseDidChange(with event: DatabaseEvent) { + processingQueue.async { [unowned self] in + self.handleDidChange(with: event) + } + } + + private func handleDidChange(with event: DatabaseEvent) { var temp = allChanges[event.tableName] ?? [] temp.append(ChangeInfo(event: event)) allChanges[event.tableName] = temp } func databaseWillCommit() throws { + processingQueue.async { [unowned self] in + self.handleWillCommit() + } + } + + private func handleWillCommit() { allChanges.forEach { (tableName, changes) in changes.forEach { info in guard info.kind != .insert else { return } @@ -69,124 +88,34 @@ class DBObserver: StorageObserver, TransactionObserver { } func databaseDidCommit(_ db: Database) { + processingQueue.async { [unowned self] in + self.handleDidCommit(db) + } + } + + private func handleDidCommit(_ db: Database) { allChanges.forEach { (tableName, changes) in - var storageChanges: [StorageChange] = [] - switch tableName { case JobTable.name: - changes.forEach { info in - let change = storageChange(from: info) - storageChanges.append(change) - - let job = change.entity as? Job - notify(with: [change], type: .job(job?.id)) - } - - notify(with: storageChanges, type: .job(nil)) + handleJobDidCommit(changes: changes) case MessageTable.name: - func handle(changes: [ChangeInfo]) { - changes.forEach { info in - let message = changedValue(from: info) as? Message - - if let receiver = message?.p2pFeed?.opponentId { - notify(with: info.event, entity: message, type: .chat(receiver)) - } else if let roomId = message?.mucFeed?.name { - notify(with: info.event, entity: message, type: .chat(roomId)) - } - - notify(with: info.event, entity: message, type: .reply(message?.id)) - } - } - - if changes.count == 2, - case let firstInfo = changes[0], - case let secondInfo = changes[1], - let firstMessage = changedValue(from: firstInfo) as? Message, - let secondMessage = changedValue(from: secondInfo) as? Message, - case let isFirstReply = firstMessage.isReply && firstMessage.linkedId == secondMessage.id, - case let isSecondReply = secondMessage.isReply && secondMessage.linkedId == firstMessage.id, - isFirstReply || isSecondReply { - handle(changes: [firstInfo]) - handle(changes: [secondInfo]) - } else if changes.count > 1, let info = changes.first, let message = changedValue(from: info) as? Message { - if let receiver = message.p2pFeed?.opponentId { - notify(with: [], type: .chat(receiver)) - } else if let roomId = message.mucFeed?.name { - notify(with: [], type: .chat(roomId)) - } - } else { - handle(changes: changes) - } + handleMessageDidCommit(changes: changes) case ContactTable.name: - if changes.count == 1, let info = changes.first { - let contact = changedValue(from: info) as? DBContact - notify(with: info.event, entity: contact, type: .contact(nil)) - } else { - notify(with: [], type: .contact(nil)) - } - - changes.forEach { info in - let contact = changedValue(from: info) as? DBContact - notify(with: info.event, entity: contact, type: .contact(contact?.phoneId)) - } + handleContactDidCommit(changes: changes) case RoomTable.name: - changes.forEach { info in - let change = storageChange(from: info) - storageChanges.append(change) - - let room = change.entity as? DBRoom - notify(with: [change], type: .room(room?.id)) - } - - notify(with: storageChanges, type: .room(nil)) + handleRoomDidCommit(changes: changes) case MemberTable.name: - changes.forEach { info in - let member = changedValue(from: info) as? DBMember - notify(with: info.event, entity: member, type: .member((member?.feed as? DBMuc)?.name)) - } + handleMemberDidCommit(changes: changes) case RosterTable.name: - changes.forEach { info in - let change = storageChange(from: info) - storageChanges.append(change) - - let roster = changedValue(from: info) as? DBRoster - notify(with: [change], type: .roster(roster?.id)) - } - - notify(with: storageChanges, type: .roster(nil)) + handleRosterDidCommit(changes: changes) case StarTable.name: - changes.forEach { info in - let change = storageChange(from: info) - storageChanges.append(change) - - let star = changedValue(from: info) as? DBStar - notify(with: [change], type: .star(star?.clientId)) - } - - notify(with: storageChanges, type: .star(nil)) + handleStarDidCommit(changes: changes) case ProfileTable.name: - changes.forEach { info in - let profile = changedValue(from: info) as? DBProfile - notify(with: info.event, entity: profile, type: .profile) - } + handleProfileDidCommit(changes: changes) case RecentStickerTable.name: - changes.forEach { info in - let change = storageChange(from: info) - storageChanges.append(change) - - let recentSticker = changedValue(from: info) as? DBRecentSticker - notify(with: [change], type: .recentSticker(recentSticker?.id)) - } - notify(with: storageChanges, type: .recentSticker(nil)) + handleRecentStickerDidCommit(changes: changes) case StickerPackTable.name: - changes.forEach { info in - let change = storageChange(from: info) - storageChanges.append(change) - - let stickerPack = changedValue(from: info) as? StickerPack - notify(with: [change], type: .stickerPack(stickerPack?.id)) - } - notify(with: storageChanges, type: .stickerPack(nil)) + handleStickerPackDidCommit(changes: changes) default: break } @@ -195,8 +124,156 @@ class DBObserver: StorageObserver, TransactionObserver { clear() } + private func handleJobDidCommit(changes: [ChangeInfo]) { + var storageChanges: [StorageChange] = [] + + changes.forEach { info in + let change = storageChange(from: info) + storageChanges.append(change) + + let job = change.entity as? Job + notify(with: [change], type: .job(job?.id)) + } + + notify(with: storageChanges, type: .job(nil)) + } + + private func handleMessageDidCommit(changes: [ChangeInfo]) { + func handle(changes: [ChangeInfo]) { + changes.forEach { info in + let message = changedValue(from: info) as? Message + + if let receiver = message?.p2pFeed?.opponentId { + notify(with: info.event, entity: message, type: .chat(receiver)) + } else if let roomId = message?.mucFeed?.name { + notify(with: info.event, entity: message, type: .chat(roomId)) + } + + notify(with: info.event, entity: message, type: .reply(message?.id)) + } + } + + if changes.count == 2, + case let firstInfo = changes[0], + case let secondInfo = changes[1], + let firstMessage = changedValue(from: firstInfo) as? Message, + let secondMessage = changedValue(from: secondInfo) as? Message, + case let isFirstReply = firstMessage.isReply && firstMessage.linkedId == secondMessage.id, + case let isSecondReply = secondMessage.isReply && secondMessage.linkedId == firstMessage.id, + isFirstReply || isSecondReply { + handle(changes: [firstInfo]) + handle(changes: [secondInfo]) + } else if changes.count > 1, let info = changes.first, let message = changedValue(from: info) as? Message { + if let receiver = message.p2pFeed?.opponentId { + notify(with: [], type: .chat(receiver)) + } else if let roomId = message.mucFeed?.name { + notify(with: [], type: .chat(roomId)) + } + } else { + handle(changes: changes) + } + } + + private func handleContactDidCommit(changes: [ChangeInfo]) { + if changes.count == 1, let info = changes.first { + let contact = changedValue(from: info) as? DBContact + notify(with: info.event, entity: contact, type: .contact(nil)) + } else { + notify(with: [], type: .contact(nil)) + } + + changes.forEach { info in + let contact = changedValue(from: info) as? DBContact + notify(with: info.event, entity: contact, type: .contact(contact?.phoneId)) + } + } + + private func handleRoomDidCommit(changes: [ChangeInfo]) { + var storageChanges: [StorageChange] = [] + + changes.forEach { info in + let change = storageChange(from: info) + storageChanges.append(change) + + let room = change.entity as? DBRoom + notify(with: [change], type: .room(room?.id)) + } + + notify(with: storageChanges, type: .room(nil)) + } + + private func handleMemberDidCommit(changes: [ChangeInfo]) { + changes.forEach { info in + let member = changedValue(from: info) as? DBMember + notify(with: info.event, entity: member, type: .member((member?.feed as? DBMuc)?.name)) + } + } + + private func handleRosterDidCommit(changes: [ChangeInfo]) { + var storageChanges: [StorageChange] = [] + + changes.forEach { info in + let change = storageChange(from: info) + storageChanges.append(change) + + let roster = changedValue(from: info) as? DBRoster + notify(with: [change], type: .roster(roster?.id)) + } + + notify(with: storageChanges, type: .roster(nil)) + } + + private func handleStarDidCommit(changes: [ChangeInfo]) { + var storageChanges: [StorageChange] = [] + + changes.forEach { info in + let change = storageChange(from: info) + storageChanges.append(change) + + let star = changedValue(from: info) as? DBStar + notify(with: [change], type: .star(star?.clientId)) + } + + notify(with: storageChanges, type: .star(nil)) + } + + private func handleProfileDidCommit(changes: [ChangeInfo]) { + changes.forEach { info in + let profile = changedValue(from: info) as? DBProfile + notify(with: info.event, entity: profile, type: .profile) + } + } + + private func handleRecentStickerDidCommit(changes: [ChangeInfo]) { + var storageChanges: [StorageChange] = [] + + changes.forEach { info in + let change = storageChange(from: info) + storageChanges.append(change) + + let recentSticker = changedValue(from: info) as? DBRecentSticker + notify(with: [change], type: .recentSticker(recentSticker?.id)) + } + notify(with: storageChanges, type: .recentSticker(nil)) + } + + private func handleStickerPackDidCommit(changes: [ChangeInfo]) { + var storageChanges: [StorageChange] = [] + + changes.forEach { info in + let change = storageChange(from: info) + storageChanges.append(change) + + let stickerPack = changedValue(from: info) as? StickerPack + notify(with: [change], type: .stickerPack(stickerPack?.id)) + } + notify(with: storageChanges, type: .stickerPack(nil)) + } + func databaseDidRollback(_ db: Database) { - clear() + processingQueue.async { + self.clear() + } } // MARK: - Private diff --git a/Nynja/DatabaseManager.swift b/Nynja/DatabaseManager.swift index 315b4c23312118e822f8bce995adbe5559658747..83c0dc9d758a85fcd77f97e0ddb7cf7cfaf282d5 100644 --- a/Nynja/DatabaseManager.swift +++ b/Nynja/DatabaseManager.swift @@ -191,7 +191,7 @@ final class DatabaseManager: DBManagerProtocol { try perform(action: action, with: dbModel) } - func perform(action: DatabaseAction, with model: DBModelProtocol) throws { + func perform(action: DatabaseAction, with model: DBModel) throws { try perform(action: action, with: [model]) } @@ -200,7 +200,7 @@ final class DatabaseManager: DBManagerProtocol { try perform(action: action, with: models) } - func perform(action: DatabaseAction, with models: [DBModelProtocol]) throws { + func perform(action: DatabaseAction, with models: [DBModel]) throws { try writeInTransaction { db in try models.forEach { model in switch action { diff --git a/Nynja/ExtendedStarHandler.swift b/Nynja/ExtendedStarHandler.swift index 0322062f2e031322ad7f8afdbbb6ff2e5b64bf09..184b5d6a093c462ff1dd25e627971968f3473d87 100644 --- a/Nynja/ExtendedStarHandler.swift +++ b/Nynja/ExtendedStarHandler.swift @@ -10,11 +10,15 @@ import Foundation final class ExtendedStarHandler: BaseHandler { + private static var storageService: StorageService { + return .sharedInstance + } + static func executeHandle(data: BertList) { let extendedStars = data.elements.compactMap { get_ExtendedStar().parse(bert: $0) as? ExtendedStar } let stars = extendedStars.compactMap { extendedStar -> DBStar? in - guard let rosterId = StorageService.sharedInstance.rosterId else { return nil } + guard let rosterId = storageService.rosterId else { return nil } if let star = extendedStar.star, StarActionDAO.containsDeleteAction(for: star) { star.starStatus = .remove @@ -22,7 +26,6 @@ final class ExtendedStarHandler: BaseHandler { return DBStar(extendedStar: extendedStar, rosterId: rosterId) } - try? StorageService.sharedInstance.perform(action: .save, with: stars) + try? storageService.perform(action: .save, with: stars) } - } diff --git a/Nynja/Extensions/Models/Contact/Contact+DB.swift b/Nynja/Extensions/Models/Contact/Contact+DB.swift index 47b6784513818514623fca550707221734d78725..0aff9a2ebad5ee1bde174c7a21fa516fee3b2e66 100644 --- a/Nynja/Extensions/Models/Contact/Contact+DB.swift +++ b/Nynja/Extensions/Models/Contact/Contact+DB.swift @@ -38,7 +38,7 @@ extension Contact { extension Contact: DBModelConvertible { - var databaseModel: DBModelProtocol? { + var databaseModel: DBModel? { return DBContact(contact: self, rosterId: StorageService.sharedInstance.rosterId) } diff --git a/Nynja/Extensions/Models/Job+DB.swift b/Nynja/Extensions/Models/Job+DB.swift index d01e59b571c3c9eb398c69c95929c41cf12f3fe8..e3b94670ffd47dde0c8fdc38179745471bb9f531 100644 --- a/Nynja/Extensions/Models/Job+DB.swift +++ b/Nynja/Extensions/Models/Job+DB.swift @@ -8,7 +8,7 @@ extension Job: DBModelConvertible { - var databaseModel: DBModelProtocol? { + var databaseModel: DBModel? { return DBJob(job: self) } diff --git a/Nynja/Extensions/Models/Member/MemberExtension.swift b/Nynja/Extensions/Models/Member/MemberExtension.swift index 5af8936a1d2198911b20137206a516ee294e377c..ef11c8e04fb8038a73b5db114cfda40952c6bb22 100644 --- a/Nynja/Extensions/Models/Member/MemberExtension.swift +++ b/Nynja/Extensions/Models/Member/MemberExtension.swift @@ -93,7 +93,7 @@ extension Member { } extension Member: DBModelConvertible { - var databaseModel: DBModelProtocol? { + var databaseModel: DBModel? { return DBMember(member: self) } } @@ -104,7 +104,7 @@ extension Array where Element == Member { var contacts: [Contact] { let phoneIds = compactMap { $0.phone_id } - let tempContacts = ContactDAO.fetchContacts(with: phoneIds) + let tempContacts = ContactDAO.fetchPlainContacts(with: phoneIds) var contacts: [Contact] = [] diff --git a/Nynja/Extensions/Models/Message/Message+DB.swift b/Nynja/Extensions/Models/Message/Message+DB.swift index 7f4df71cfdbcd14202b79a77e72a27c3fb70a5c9..87770aade0ff89ee1f652e4dfd2476428038e15e 100644 --- a/Nynja/Extensions/Models/Message/Message+DB.swift +++ b/Nynja/Extensions/Models/Message/Message+DB.swift @@ -121,7 +121,7 @@ extension Message: DBModelConvertible { return DBMessage(message: self) } - var databaseModel: DBModelProtocol? { + var databaseModel: DBModel? { return dbMessage } } diff --git a/Nynja/Extensions/Models/ProfileExtension.swift b/Nynja/Extensions/Models/ProfileExtension.swift index b2d0014832c9786c3b421502bdee99ca883e8db1..6b633fe577195c740e822460c3d989e9fd70648f 100644 --- a/Nynja/Extensions/Models/ProfileExtension.swift +++ b/Nynja/Extensions/Models/ProfileExtension.swift @@ -34,7 +34,7 @@ extension Profile { extension Profile: DBModelConvertible { - var databaseModel: DBModelProtocol? { + var databaseModel: DBModel? { return DBProfile(profile: self) } } diff --git a/Nynja/Extensions/Models/Room/Room+DB.swift b/Nynja/Extensions/Models/Room/Room+DB.swift index 7bb5c5631549382b569db5a292046c51e0c3d43f..d229126a0be99561d06aed56094cd1f7650e5adf 100644 --- a/Nynja/Extensions/Models/Room/Room+DB.swift +++ b/Nynja/Extensions/Models/Room/Room+DB.swift @@ -46,7 +46,7 @@ extension Room: DBModelConvertible { return DBRoom(room: self, rosterId: StorageService.sharedInstance.rosterId) } - var databaseModel: DBModelProtocol? { + var databaseModel: DBModel? { return dbRoom } } diff --git a/Nynja/Extensions/Models/Roster+DB.swift b/Nynja/Extensions/Models/Roster+DB.swift index f7485cc7a6de080d67bad4d0fca1bd021ebeb20f..075433a680ba6c3005a0f6600d0ed27ab0445f81 100644 --- a/Nynja/Extensions/Models/Roster+DB.swift +++ b/Nynja/Extensions/Models/Roster+DB.swift @@ -30,7 +30,7 @@ extension Roster { // MARK: - DBModelConvertible extension Roster: DBModelConvertible { - var databaseModel: DBModelProtocol? { + var databaseModel: DBModel? { guard let profileId = StorageService.sharedInstance.phone else { return nil } diff --git a/Nynja/Extensions/Models/RosterExtension.swift b/Nynja/Extensions/Models/RosterExtension.swift index ddd7a130b8fbb1fe5a824652eb8ddc01e92bc69e..ccb1e2c3bf2e806c992e597ee7bca81539a2470f 100644 --- a/Nynja/Extensions/Models/RosterExtension.swift +++ b/Nynja/Extensions/Models/RosterExtension.swift @@ -6,37 +6,23 @@ // Copyright © 2017 TecSynt Solutions. All rights reserved. // -extension Roster { +extension Roster: FullNameRepresentable { // Returns self contact var myContact: Contact? { // Check that 'contacts' and 'phone' exist - guard let contacts = self.userlist, let phone = self.phone else { - return nil + guard let contacts = self.userlist, + let phoneId = self.phoneId, + let contact = contacts.first(where: { $0.phone_id == phoneId }) else { + return nil } - //Find index of self contact - guard let index = contacts.index(where: { contact in - // Check that 'phoneNumber' exists in contact - guard let phoneNumber = contact.phoneNumber else { - return false - } - - return phone == phoneNumber - }) else { - return nil - } - - let contact = contacts[index] contact.names = self.names contact.surnames = self.surnames contact.avatar = self.avatar + return contact } - - var hasName: Bool { - return names != nil && names != "" - } var phoneId: String? { guard let phone = phone, let rosterId = id else { @@ -45,5 +31,4 @@ extension Roster { return "\(phone)_\(rosterId)" } - } diff --git a/Nynja/Extensions/Models/StarExtension.swift b/Nynja/Extensions/Models/StarExtension.swift index 95cbaf53a88b93451596b8e51355680792934dbf..3317259a779047b308ea3e1285187b8003787896 100644 --- a/Nynja/Extensions/Models/StarExtension.swift +++ b/Nynja/Extensions/Models/StarExtension.swift @@ -116,7 +116,7 @@ extension Star { extension Star: DBModelConvertible { - var databaseModel: DBModelProtocol? { + var databaseModel: DBModel? { return DBStar(star: self) } diff --git a/Nynja/Extensions/Models/StickerPack/StickerPack+DB.swift b/Nynja/Extensions/Models/StickerPack/StickerPack+DB.swift index 34ce67e99a8eb80addcd7be31f6059c8d91d453b..6b46e3f5a939da77d48901e2072abf7e937aaf4d 100644 --- a/Nynja/Extensions/Models/StickerPack/StickerPack+DB.swift +++ b/Nynja/Extensions/Models/StickerPack/StickerPack+DB.swift @@ -23,7 +23,7 @@ extension StickerPack: DBModelConvertible { downloaded = stickerPack.downloaded } - var databaseModel: DBModelProtocol? { + var databaseModel: DBModel? { return DBStickerPack(stickerPack: self) } } diff --git a/Nynja/Extensions/SwiftLibrary/Array/Array+Contact.swift b/Nynja/Extensions/SwiftLibrary/Array/Array+Contact.swift index 6ff167dc82eec1b29f1f105d9464b173705e8402..056a4d9a70f8889c068f4ac83855ceb6f791708d 100644 --- a/Nynja/Extensions/SwiftLibrary/Array/Array+Contact.swift +++ b/Nynja/Extensions/SwiftLibrary/Array/Array+Contact.swift @@ -30,3 +30,10 @@ extension Array where Element == Contact { } } + + +extension Array where Element == Contact.Status { + var strings: [String] { + return self.map { $0.rawValue } + } +} diff --git a/Nynja/FullNameRepresentable.swift b/Nynja/FullNameRepresentable.swift new file mode 100644 index 0000000000000000000000000000000000000000..78560d73c1864cd78d01130a56892acdd9c1598f --- /dev/null +++ b/Nynja/FullNameRepresentable.swift @@ -0,0 +1,36 @@ +// +// FullNameRepresentable.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/1/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +protocol FullNameRepresentable { + var names: String? { get set } + var surnames: String? { get set } + var fullName: String? { get } +} + +extension FullNameRepresentable { + + var fullName: String? { + if let name = self.names, let surname = self.surnames { + if !surname.isEmpty { + return "\(name) \(surname)" + } else { + return name + } + } else if names != nil && surnames == nil { + return names + } else if names == nil && surnames == nil { + return surnames + } + + return nil + } + + var hasName: Bool { + return names != nil && names != "" + } +} diff --git a/Nynja/Generated/LocalizableConstants.swift b/Nynja/Generated/LocalizableConstants.swift index 377325bf81ff6a7be94abcf2f0c3490e69725207..08137e75e9e4ea505aa0513b0cead97fa748a04e 100644 --- a/Nynja/Generated/LocalizableConstants.swift +++ b/Nynja/Generated/LocalizableConstants.swift @@ -1130,7 +1130,7 @@ internal extension String { static var tpTranslation: String { return localizable.tr("Localizable", "tp_translation") } /// Translation failed. Please try again. static var tpTranslationFailed: String { return localizable.tr("Localizable", "tp_translation_failed") } - /// Original and translated messages language is the same. + /// Original and translated message are the same language. static var tpTranslationLanguageIsEqual: String { return localizable.tr("Localizable", "tp_translation_language_is_equal") } /// Transcribe with Google static var transcribe: String { return localizable.tr("Localizable", "transcribe") } diff --git a/Nynja/HandlerFactory.swift b/Nynja/HandlerFactory.swift index c94fbcbee74bc48dd6bffb23471d432beba6e071..6313b5934bd936ade160a0f24d101b126c343010 100644 --- a/Nynja/HandlerFactory.swift +++ b/Nynja/HandlerFactory.swift @@ -22,8 +22,6 @@ final class HandlerFactory { return HistoryHandler.self case .message: return MessageHandler.self - case .search: - return SearchHandler.self case .room: return RoomHandler.self case .member: diff --git a/Nynja/Handlers.swift b/Nynja/Handlers.swift index 92f9b54947e0d1249403346a160f3fdfae37ca5c..fc256fa32a707c08ba3bc72a26362f5678408438 100644 --- a/Nynja/Handlers.swift +++ b/Nynja/Handlers.swift @@ -13,7 +13,6 @@ enum Handlers: String { case contact case history case message - case search case room case member case typing diff --git a/Nynja/HistoryHandlerSubscriber.swift b/Nynja/HistoryHandlerSubscriber.swift new file mode 100644 index 0000000000000000000000000000000000000000..08580e95082b0d631cb3901067284a82b2c2caeb --- /dev/null +++ b/Nynja/HistoryHandlerSubscriber.swift @@ -0,0 +1,21 @@ +// +// HistoryHandlerSubscriber.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/9/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +protocol HistoryHandlerSubscriber: class { + func getHistorySuccess() + func getJobsHistorySuccess() + func getStickerPacksSuccess() +} + +extension HistoryHandlerSubscriber { + func getHistorySuccess() {} + func getJobsHistorySuccess() {} + func getStickerPacksSuccess() {} +} diff --git a/Nynja/Improvements/StorageObserver.swift b/Nynja/Improvements/StorageObserver.swift index 83d8b76a878e332fe3dd6336f2bbea11241ae9a0..299312bc0e6d0b9b2e66cb8272baa1904b6d0230 100644 --- a/Nynja/Improvements/StorageObserver.swift +++ b/Nynja/Improvements/StorageObserver.swift @@ -9,6 +9,8 @@ protocol StorageObserver : class { var subscribers: [SubscribeType: [StorageSubscriberReference]] { get set } + var isolationQueue: DispatchQueue { get } + func register(subscriber: StorageSubscriber, type: SubscribeType) func unregister(subscriber: StorageSubscriber, type: SubscribeType) func unregister(subscribers type: SubscribeType) @@ -19,12 +21,18 @@ protocol StorageObserver : class { extension StorageObserver { func register(subscriber: StorageSubscriber, type: SubscribeType) { - let reference = StorageSubscriberReference(subscriber) - if var subs = subscribers[type] { - subs.append(reference) - subscribers[type] = subs - } else { - subscribers[type] = [reference] + isolationQueue.async { [weak self] in + guard let `self` = self else { + return + } + + let reference = StorageSubscriberReference(subscriber) + if var subs = self.subscribers[type] { + subs.append(reference) + self.subscribers[type] = subs + } else { + self.subscribers[type] = [reference] + } } } @@ -35,11 +43,19 @@ extension StorageObserver { } func unregister(subscriber: StorageSubscriber, type: SubscribeType) { - subscribers[type] = subscribers[type]?.filter { $0.subscriber != nil && $0.subscriber?.id != subscriber.id } + isolationQueue.async { [weak self] in + guard let `self` = self else { + return + } + + self.subscribers[type] = self.subscribers[type]?.filter { $0.subscriber != nil && $0.subscriber?.id != subscriber.id } + } } func unregister(subscribers type: SubscribeType) { - subscribers.removeValue(forKey: type) + isolationQueue.async { [weak self] in + self?.subscribers.removeValue(forKey: type) + } } func unregister(subscriber: StorageSubscriber) { @@ -49,12 +65,15 @@ extension StorageObserver { } func notify(with changes: [StorageChange], type: SubscribeType) { - guard let subscribers = subscribers[type] else { - return - } - for subscriberRef in subscribers { - dispatchAsyncMain { - subscriberRef.subscriber?.update(with: changes, type: type) + isolationQueue.sync { [weak self] in + guard let subscribers = self?.subscribers[type] else { + return + } + + for subscriberRef in subscribers { + dispatchAsyncMain { + subscriberRef.subscriber?.update(with: changes, type: type) + } } } } diff --git a/Nynja/JobDAO.swift b/Nynja/JobDAO.swift index 560f682c64d775a0f4487dfdfb4cb330cf69fffa..485286a44dc14cbbe7fd4ffb976672422f9563f4 100644 --- a/Nynja/JobDAO.swift +++ b/Nynja/JobDAO.swift @@ -9,8 +9,9 @@ import GRDBCipher class JobDAO: JobDAOProtocol { - + // MARK: - Fetch + static func fetchJob(for serverId: Int64) -> DBJob? { return dbManager.fetch { db in return try DBJob.job(db, serverId: serverId) @@ -18,18 +19,13 @@ class JobDAO: JobDAOProtocol { } static func findJob(by rowId: Int64) -> Job? { - guard let job = dbManager.fetch({ db in - return try DBJob.job(from: db, rowId: rowId) - }) else { - return nil + return dbManager.fetch { db in + return try DBJob.job(from: db, rowId: rowId)?.serverModel } - - return Job(job: job) } static func findJob(for serverId: Int64) -> Job? { - guard let job = fetchJob(for: serverId) else { return nil } - return Job(job: job) + return fetchJob(for: serverId)?.serverModel } static func fetchJobs() -> [DBJob] { @@ -39,14 +35,18 @@ class JobDAO: JobDAOProtocol { } static func fetchJobs(of type: DBJob.JobType) -> [Job] { - let jobs = dbManager.fetch { db in + return dbManager.fetch { db in return try DBJob.jobs(db, type: type) - } - - return jobs.map { Job(job: $0) } + }.serverModels + } + + static func fetchJobs(with args: DBJob.QueryArgs) -> [Job] { + return DAO().fetch(with: args).serverModels } + // MARK: - Delete + static func deleteJobs() { do { let jobs = fetchJobs() @@ -63,5 +63,4 @@ class JobDAO: JobDAOProtocol { try? dbManager.perform(action: .delete, with: jobs) } - } diff --git a/Nynja/JobHandler.swift b/Nynja/JobHandler.swift index cbf72bb791a2cebc289622668adfc32f874e154b..32343977dfa260d929574bd390e964fc11b93cf2 100644 --- a/Nynja/JobHandler.swift +++ b/Nynja/JobHandler.swift @@ -6,7 +6,11 @@ // Copyright © 2018 TecSynt Solutions. All rights reserved. // -class JobHandler: BaseHandler { +final class JobHandler: BaseHandler { + + private static var storageService: StorageService { + return .sharedInstance + } static func executeHandle(data: BertTuple) { guard let job = get_Job().parse(bert: data) as? Job, @@ -16,13 +20,12 @@ class JobHandler: BaseHandler { switch status { case "pending", "update": - try? StorageService.sharedInstance.perform(action: .save, with: job) + try? storageService.perform(action: .save, with: job) case "complete", "delete": - try? StorageService.sharedInstance.perform(action: .delete, with: job) + try? storageService.perform(action: .delete, with: job) default: break } } - } diff --git a/Nynja/JobService.swift b/Nynja/JobService.swift index b7777de5b6d9d765e2f38f6f2edd27cfdc4deaa2..1ccd48c050d1d4219042ce15588a2835da169123 100644 --- a/Nynja/JobService.swift +++ b/Nynja/JobService.swift @@ -16,12 +16,12 @@ class JobService { private static func fetchContacts(for job: Job) -> [Contact] { let ids = job.contactsIds - return ContactDAO.fetchContacts(with: ids) + return ContactDAO.fetchPlainContacts(with: ids) } private static func fetchRooms(for job: Job) -> [Room] { let ids = job.roomsIds - return RoomDAO.fetchRooms(with: ids) + return RoomDAO.fetchPlainRooms(with: ids) } } diff --git a/Nynja/Library/Debug/DebugLogs.swift b/Nynja/Library/Debug/DebugLogs.swift index dd8ea8f2d9f87b569818ba91f6fbc5065fde6ead..28c3c5c28a456a35fb65a29c826cd26b3451b3b8 100644 --- a/Nynja/Library/Debug/DebugLogs.swift +++ b/Nynja/Library/Debug/DebugLogs.swift @@ -9,5 +9,7 @@ import Foundation func deinited(_ object: AnyObject) { - LogService.log(topic: .arc) { "\(type(of: object)) deinited" } + #if !RELEASE + print("\(type(of: object)) deinited") + #endif } diff --git a/Nynja/Library/UI/AlertManager.swift b/Nynja/Library/UI/AlertManager.swift index f0519ed17d6365548da9f8d33f65fa2804db9291..c013d0aa9dc3d55a563ad48f5854d391470fb028 100644 --- a/Nynja/Library/UI/AlertManager.swift +++ b/Nynja/Library/UI/AlertManager.swift @@ -19,13 +19,13 @@ class AlertManager { return UIApplication.shared.keyWindow?.presentedViewController } - func showAlertOk(title: String, message: String, completion:(()->Void)? = nil) { + func showAlertOk(title: String, message: String, completion: (()->Void)? = nil) { let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) let defaultAction = UIAlertAction(title: String.localizable.ok, style: .default) { (action) in completion?() } alert.addAction(defaultAction) - presentingController?.present(alert, animated: true, completion: nil) + present(alert, from: presentingController) } func showAlert(title: String, dismissInterval: TimeInterval) { @@ -33,7 +33,7 @@ class AlertManager { dispatchAsyncMainAfter(dismissInterval) { alert.dismiss(animated: true, completion: nil) } - presentingController?.present(alert, animated: true, completion: nil) + present(alert, from: presentingController) } func showAlertOk(message: String, completion:(()->Void)? = nil) { @@ -58,7 +58,7 @@ class AlertManager { textFieldConfig.map { alert.addTextField(configurationHandler: $0) } - presentingController?.present(alert, animated: true, completion: nil) + present(alert, from: presentingController) } func showAlertWithTwoActions(title: String, message: String, firstActionTitle: String, @@ -74,13 +74,14 @@ class AlertManager { alert.addAction(firstAlertAction) alert.addAction(secondAlertAction) - presentingController?.present(alert, animated: true, completion: nil) + present(alert, from: presentingController) } func showAlertWithThreeActions(title: String?, message: String?, firstActionTitle: String, secondActionTitle: String, thirdActionTitle: String, firstAction: Handler?, secondAction: Handler?, thirdAction: Handler?) { let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) + let firstAlertAction = UIAlertAction(title: firstActionTitle, style: .default) { (action) in firstAction?() } @@ -90,11 +91,12 @@ class AlertManager { let thirdAlertAction = UIAlertAction(title: thirdActionTitle, style: .default) { (action) in thirdAction?() } + alert.addAction(firstAlertAction) alert.addAction(secondAlertAction) alert.addAction(thirdAlertAction) - presentingController?.present(alert, animated: true, completion: nil) + present(alert, from: presentingController) } func showAlertAllowPermission(title: String = "", @@ -144,8 +146,7 @@ class AlertManager { func showActionSheet(title: String?, message: String?, actions: [UIAlertAction]) { let alert = UIAlertController(title: title, message: message, preferredStyle: .actionSheet) actions.forEach { alert.addAction($0) } - - presentingController?.present(alert, animated: true, completion: nil) + present(alert, from: presentingController) } func linkMenu(open:(()->Void)? = nil, showInChat:(()->Void)? = nil, share:(()->Void)? = nil, cancel: (()->Void)? = nil) { @@ -218,21 +219,23 @@ class AlertManager { func showNativeShare(with activityItems: [Any]) { let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil) - - presentingController?.present(activityViewController, animated: true, completion: nil) + present(activityViewController, from: presentingController) } } + // MARK: - No Internet connection + extension AlertManager { func showNoInternetConnection() { showAlertOk(message: String.localizable.noInternetConnection) } - } + // MARK: - AlertImageViewController + extension AlertManager { func showImageAlert(with image: UIImage?, message: String?, on viewController: UIViewController? = nil, duration: Double? = nil, completion: AlertImageViewController.DismissCompletion? = nil) { @@ -241,19 +244,35 @@ extension AlertManager { alertController.duration = duration alertController.completion = completion - if let viewController = viewController { - viewController.present(alertController, animated: true, completion: nil) - } else { - presentingController?.present(alertController, animated: true, completion: nil) - } + let presentingVC = viewController ?? presentingController + present(alertController, from: presentingVC) } } + //MARK: - TimeFieldViewController + extension AlertManager { + func showTimeFieldAlert(with initialValue: Int, completion: TimeCallback?) { let alertControler = AlertTextFieldViewController(initialValue: initialValue) alertControler.completion = completion - presentingController?.present(alertControler, animated: true, completion: nil) + present(alertControler, from: presentingController) + } +} + + +// MARK: - Private + +private extension AlertManager { + + func present(_ vc: UIViewController, from presentingVC: UIViewController?, animated: Bool = true) { + guard let presentingVC = presentingVC else { + return + } + + dispatchAsyncMain { + presentingVC.present(vc, animated: animated, completion: nil) + } } } diff --git a/Nynja/Library/UI/Extensions/String/StringExtensions.swift b/Nynja/Library/UI/Extensions/String/StringExtensions.swift index f936adddbebab041085673c4053ceaf240cc8da9..75ad7b8a7a1271057033b5c346c0f860e328c1ab 100644 --- a/Nynja/Library/UI/Extensions/String/StringExtensions.swift +++ b/Nynja/Library/UI/Extensions/String/StringExtensions.swift @@ -379,3 +379,10 @@ extension String { self = self.capitalizingFirstLetter() } } + +extension String { + + static func label(withSuffix suffix: String) -> String { + return Bundle.main.bundleIdentifier.appending(suffix) + } +} diff --git a/Nynja/LinkHandler.swift b/Nynja/LinkHandler.swift index feebe3ae1ff00831ed081dc9d607ddb0b5965a44..620ad386d0779de2aacb8a9d9822e069f01263ba 100644 --- a/Nynja/LinkHandler.swift +++ b/Nynja/LinkHandler.swift @@ -6,13 +6,7 @@ // Copyright © 2018 TecSynt Solutions. All rights reserved. // -protocol LinkHandlerDelegate: class { - func generatedLink(_ link: Link) - func linkIsAvailable(_ link: Link) -} - - -final class LinkHandler: BaseHandler { +final class LinkHandler: BaseHandler, StaticDelegating { static weak var delegate: LinkHandlerDelegate? @@ -35,9 +29,9 @@ final class LinkHandler: BaseHandler { switch status { case .check: - delegate?.linkIsAvailable(link) + delegate { $0.linkIsAvailable(link) } case .gen: - delegate?.generatedLink(link) + delegate { $0.generatedLink(link) } case .add, .delete, .update: // TODO: will be implemented in future. break @@ -51,5 +45,4 @@ final class LinkHandler: BaseHandler { .filter { StatusCode.linkCodes.contains($0) } .forEach { statusCodeManager.notify(model: link, code: $0) } } - } diff --git a/Nynja/LinkHandlerDelegate.swift b/Nynja/LinkHandlerDelegate.swift new file mode 100644 index 0000000000000000000000000000000000000000..dd65b2588921bc01a4c9eb79bf1deaf9291293ec --- /dev/null +++ b/Nynja/LinkHandlerDelegate.swift @@ -0,0 +1,12 @@ +// +// LinkHandlerDelegate.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/9/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +protocol LinkHandlerDelegate: class { + func generatedLink(_ link: Link) + func linkIsAvailable(_ link: Link) +} diff --git a/Nynja/MemberHandler.swift b/Nynja/MemberHandler.swift index 361d24a8bb912bcf30904ab72ba52ca8e1b1c7b1..d6a52401101fdda7b713af00474762979ca4053a 100644 --- a/Nynja/MemberHandler.swift +++ b/Nynja/MemberHandler.swift @@ -8,6 +8,10 @@ class MemberHandler: BaseHandler { + private static var storageService: StorageService { + return .sharedInstance + } + static func executeHandle(data: BertTuple) { guard let member = get_Member().parse(bert: data) as? Member, let status = (member.status as? StringAtom)?.string else { @@ -20,12 +24,10 @@ class MemberHandler: BaseHandler { member.status = oldMember.status } - try? StorageService.sharedInstance.perform(action: .save, with: member) + try? storageService.perform(action: .save, with: member) default: return - } - + } } - } diff --git a/Nynja/Modules/AddContactByUsername/Interactor/AddContactByUsernameInteractor.swift b/Nynja/Modules/AddContactByUsername/Interactor/AddContactByUsernameInteractor.swift index 21d47a38e4c9670ccbf18447c72b5fbb0104b731..1882fbe249752f3b9d151cbe3f606b526fe5388f 100644 --- a/Nynja/Modules/AddContactByUsername/Interactor/AddContactByUsernameInteractor.swift +++ b/Nynja/Modules/AddContactByUsername/Interactor/AddContactByUsernameInteractor.swift @@ -33,7 +33,7 @@ final class AddContactByUsernameInteractor: AddContactByUsernameInteractorInputP func getContactByUserName(username: String) { searchAction = nil - if let contact = ContactDAO.findContactBy(username: username) { + if let contact = ContactDAO.findPlainContactBy(username: username) { if contact.phoneId == StorageService.sharedInstance.phoneId { presenter.getMyProfile() } else { diff --git a/Nynja/Modules/AddContactViaPhone/Interactor/AddContactViaPhoneInteractor.swift b/Nynja/Modules/AddContactViaPhone/Interactor/AddContactViaPhoneInteractor.swift index b3e89801c91bff6e6a5b8818808218e91989a17f..3e9ba94c05de565a72a78d75bda26f60eee37959 100644 --- a/Nynja/Modules/AddContactViaPhone/Interactor/AddContactViaPhoneInteractor.swift +++ b/Nynja/Modules/AddContactViaPhone/Interactor/AddContactViaPhoneInteractor.swift @@ -36,7 +36,7 @@ final class AddContactViaPhoneInteractor: AddContactViaPhoneInteractorInputProto if storageService.phone == number { presenter.getMyProfile() - } else if let contact = ContactDAO.findContactBy(phone: number) { + } else if let contact = ContactDAO.findPlainContactBy(phone: number) { presenter.getContactByPhoneSuccess(contact: contact) } else { searchAction = { [weak self] in diff --git a/Nynja/Modules/AddParticipants/Interactor/AddParticipantsInteractor.swift b/Nynja/Modules/AddParticipants/Interactor/AddParticipantsInteractor.swift index f059e1ebc7635e31871c74b766c5b4e42cd8f548..74ce1fb7a2fab7820ac4b9b7713e3ce8af8e1be2 100644 --- a/Nynja/Modules/AddParticipants/Interactor/AddParticipantsInteractor.swift +++ b/Nynja/Modules/AddParticipants/Interactor/AddParticipantsInteractor.swift @@ -48,8 +48,7 @@ class AddParticipantsInteractor: BaseInteractor, AddParticipantsInteractorInputP } func getMySelf() -> Contact? { - guard let id = storageService.rosterId, let roster = RosterDAO.findRosterBy(id: id) else { return nil } - return roster.myContact + return ContactDAO.currentContact } func allocateConference() { @@ -135,7 +134,7 @@ class AddParticipantsInteractor: BaseInteractor, AddParticipantsInteractorInputP //MARK: - AddParticipantsInteractorInputProtocol func fetchParticipants(`for` mode: ParticipantsModeType) { - var contacts = ContactDAO.fetchContacts(with: [.friend]) + var contacts = ContactDAO.fetchPlainContacts(with: [.friend]) if mode == .delete || mode == .admins { if let members = members { diff --git a/Nynja/Modules/Auth/VerifyNumber/Interactor/VerifyNumberInteractor.swift b/Nynja/Modules/Auth/VerifyNumber/Interactor/VerifyNumberInteractor.swift index c525eddceaeb5054e4b5b97831a66489fafe8be4..92a4a15970b74f91ae75db4c76a3ea54a554318e 100644 --- a/Nynja/Modules/Auth/VerifyNumber/Interactor/VerifyNumberInteractor.swift +++ b/Nynja/Modules/Auth/VerifyNumber/Interactor/VerifyNumberInteractor.swift @@ -174,7 +174,6 @@ final class VerifyNumberInteractor: BaseInteractor, VerifyNumberInteractorInputP } } } - } diff --git a/Nynja/Modules/Call/CallCreatorMediator.swift b/Nynja/Modules/Call/CallCreatorMediator.swift index e82b7d1e3d1b5ca70314bca51d0bef3bef539d0e..c8832cf34f21fc7a6706b48e3e26b09bd46a3e28 100644 --- a/Nynja/Modules/Call/CallCreatorMediator.swift +++ b/Nynja/Modules/Call/CallCreatorMediator.swift @@ -71,5 +71,4 @@ class CallCreatorMediator { return false } - } diff --git a/Nynja/Modules/Channel/SubscribersSelector/Interactor/SubscribersSelectorInteractor.swift b/Nynja/Modules/Channel/SubscribersSelector/Interactor/SubscribersSelectorInteractor.swift index 4aaa35199f62ea08ed4bc30555477e8bb5ae3a62..06301f43a637ad73ed099be61f18d8373bff7aa9 100644 --- a/Nynja/Modules/Channel/SubscribersSelector/Interactor/SubscribersSelectorInteractor.swift +++ b/Nynja/Modules/Channel/SubscribersSelector/Interactor/SubscribersSelectorInteractor.swift @@ -57,7 +57,7 @@ final class SubscribersSelectorInteractor: BaseInteractor, SubscribersSelectorIn private func members(from subscribers: [ChannelSubscriber]) -> [Member] { let ids = subscribers.map { $0.id } return ContactDAO - .fetchContacts(with: ids) + .fetchPlainContacts(with: ids) .map { Member(contact: $0) } } diff --git a/Nynja/Modules/ChannelsList/Interactor/ChannelsListInteractor.swift b/Nynja/Modules/ChannelsList/Interactor/ChannelsListInteractor.swift index 69b2c0e4f4ff0360c59e16746469af58ef17a9b2..dea84c7eaf93c94c30c891952280768aafcebf18 100644 --- a/Nynja/Modules/ChannelsList/Interactor/ChannelsListInteractor.swift +++ b/Nynja/Modules/ChannelsList/Interactor/ChannelsListInteractor.swift @@ -13,24 +13,19 @@ final class ChannelsListInteractor: BaseInteractor, ChannelsListInteractorInputP weak var presenter: ChannelsListInteractorOutputProtocol! private var conversationsProvider: ConversationsProviding + private var storageService: StorageService private var mode: ChannelsListMode private var channels: [Room] = [] private var searchText: String = "" - // MARK: - InitializeInjectable - - struct Dependencies { - let presenter: ChannelsListInteractorOutputProtocol - let conversationsProvider: ConversationsProviding - - let mode: ChannelsListMode - } + // MARK: - Init required init(dependencies: Dependencies) { presenter = dependencies.presenter conversationsProvider = dependencies.conversationsProvider + storageService = dependencies.storageService mode = dependencies.mode } @@ -79,10 +74,16 @@ final class ChannelsListInteractor: BaseInteractor, ChannelsListInteractorInputP //MARK: - Private private func loadChannels() { + guard let rosterId = storageService.rosterId else { + return + } + + let args = ConversationsProviding.FetchingArgs(rosterId: rosterId) + if mode == .all { - channels = conversationsProvider.fetchChannels() - } else { - channels = conversationsProvider.fetchMyChannels() + channels = conversationsProvider.fetchChannels(with: args) + } else if let phoneId = StorageService.sharedInstance.phoneId { + channels = conversationsProvider.fetchMyChannels(with: args, ownerId: phoneId) } } @@ -121,3 +122,16 @@ final class ChannelsListInteractor: BaseInteractor, ChannelsListInteractorInputP } } + +// MARK: - InitializableInjectable + +extension ChannelsListInteractor { + + struct Dependencies { + let presenter: ChannelsListInteractorOutputProtocol + let conversationsProvider: ConversationsProviding + let storageService: StorageService + + let mode: ChannelsListMode + } +} diff --git a/Nynja/Modules/ChannelsList/WireFrame/ChannelsListWireFrame.swift b/Nynja/Modules/ChannelsList/WireFrame/ChannelsListWireFrame.swift index 19077e274e4dff30004b3a1ff3813124256319a3..53436fff261542ca249e626b250bb3a5ade64cc2 100644 --- a/Nynja/Modules/ChannelsList/WireFrame/ChannelsListWireFrame.swift +++ b/Nynja/Modules/ChannelsList/WireFrame/ChannelsListWireFrame.swift @@ -25,6 +25,7 @@ final class ChannelsListWireFrame: ChannelsListWireFrameProtocol { let serviceFactory = ServiceFactory() let messagePayloadParser = serviceFactory.makeMessagePayloadParser() let conversationProvider = serviceFactory.makeConversationsProvider() + let storageService = serviceFactory.makeStorageService() // Module components let view = ChannelsListViewController() @@ -32,6 +33,7 @@ final class ChannelsListWireFrame: ChannelsListWireFrameProtocol { let interactor = ChannelsListInteractor( dependencies: .init(presenter: presenter, conversationsProvider: conversationProvider, + storageService: storageService, mode: mode)) // Connecting diff --git a/Nynja/Modules/ChatsList/Interactor/ChatsListInteractor.swift b/Nynja/Modules/ChatsList/Interactor/ChatsListInteractor.swift index 02aa173111d987dda1e4f5fa6d1a242e6bee4f26..1698753810b5910be5b64dae4f346e5dbacd2721 100644 --- a/Nynja/Modules/ChatsList/Interactor/ChatsListInteractor.swift +++ b/Nynja/Modules/ChatsList/Interactor/ChatsListInteractor.swift @@ -16,6 +16,9 @@ class ChatsListInteractor: BaseInteractor, ChatsListInteractorInputProtocol, Ini private var chats: [Contact] = [] private var searchText: String = "" + private var rosterId: Int64? { + return storageService.rosterId + } // MARK: - InitializeInjectable @@ -61,7 +64,11 @@ class ChatsListInteractor: BaseInteractor, ChatsListInteractorInputProtocol, Ini let contact = Contact(contact: dbContact) chats = updateChatsList(with: contact) } else { - chats = conversationsProvider.fetchChats() + guard let args = rosterId.map({ ConversationsProviding.FetchingArgs(rosterId: $0) }) else { + return + } + + chats = conversationsProvider.fetchChats(with: args) } applyFilter(with: searchText) @@ -71,7 +78,11 @@ class ChatsListInteractor: BaseInteractor, ChatsListInteractorInputProtocol, Ini //MARK: - Private private func fetchChats() { - chats = conversationsProvider.fetchChats() + guard let args = rosterId.map({ ConversationsProviding.FetchingArgs(rosterId: $0) }) else { + return + } + + chats = conversationsProvider.fetchChats(with: args) presenter.didFetch(chats: chats) } diff --git a/Nynja/Modules/ChatsList/WireFrame/ChatsListWireframe.swift b/Nynja/Modules/ChatsList/WireFrame/ChatsListWireframe.swift index 608c9433cb5997956536ebe4d43078ece267b25c..d54d9eb7b2f985efd06d0e5052aa7915e1f59805 100644 --- a/Nynja/Modules/ChatsList/WireFrame/ChatsListWireframe.swift +++ b/Nynja/Modules/ChatsList/WireFrame/ChatsListWireframe.swift @@ -20,7 +20,7 @@ class ChatsListWireFrame: ChatsListWireFrameProtocol { let interactor = ChatsListInteractor( dependencies: .init(storageService: StorageService.sharedInstance, - conversationsProvider: ConversationsProvider())) + conversationsProvider: ServiceFactory().makeConversationsProvider())) self.main = main diff --git a/Nynja/Modules/Contacts/Interactor/ContactsInteractor.swift b/Nynja/Modules/Contacts/Interactor/ContactsInteractor.swift index 5b0a313da3c5f5d86a112a58d55a93e5dbbf2214..bf794f6955c7b1b3881561e1cbd892b976e06fe3 100644 --- a/Nynja/Modules/Contacts/Interactor/ContactsInteractor.swift +++ b/Nynja/Modules/Contacts/Interactor/ContactsInteractor.swift @@ -75,12 +75,12 @@ class ContactsInteractor: BaseInteractor, ContactsInteractorInputProtocol, IoHan //MARK: - Private private func fetchFriends() { - contacts = ContactDAO.fetchContactWithoutSelf() + contacts = ContactDAO.fetchPlainContactsWithoutSelf() showHistory(contacts: contacts) } private func fetchAll() { - contacts = ContactDAO.fetchContacts() + contacts = ContactDAO.fetchPlainContacts() showHistory(contacts: contacts) } diff --git a/Nynja/Modules/Favorites/Presenter/FavoritesPresenter.swift b/Nynja/Modules/Favorites/Presenter/FavoritesPresenter.swift index 35f397cf45d22d4cfe248e993ca084f26d5e3181..f1661ea99fcf19f449f17e184386587ac93a53c9 100644 --- a/Nynja/Modules/Favorites/Presenter/FavoritesPresenter.swift +++ b/Nynja/Modules/Favorites/Presenter/FavoritesPresenter.swift @@ -22,7 +22,7 @@ class FavoritesPresenter: BasePresenter, FavoritesPresenterProtocol, FavoritesIn } func getStarsSuccess(stars: [Star]) { - self.view.setup(stars: stars) + view.setup(stars: stars) } func showed() { diff --git a/Nynja/Modules/ForwardSelector/Interactor/ForwardSelectorInteractor.swift b/Nynja/Modules/ForwardSelector/Interactor/ForwardSelectorInteractor.swift index 898ef5ea7e6054d6df3e82e1a224cf7ab6db72d9..271f881b91a0ac2d3329fdaadab6cedb4935e72a 100644 --- a/Nynja/Modules/ForwardSelector/Interactor/ForwardSelectorInteractor.swift +++ b/Nynja/Modules/ForwardSelector/Interactor/ForwardSelectorInteractor.swift @@ -33,6 +33,7 @@ final class ForwardSelectorInteractor: BaseInteractor, ForwardSelectorInteractor weak var presenter: ForwardSelectorInteractorOutputProtocol! private var mqttService: MQTTService! private var storageService: StorageService! + private var conversationsProviding: ConversationsProviding! private var contacts: [ForwardTarget]? private var groups: [ForwardTarget]? @@ -111,7 +112,7 @@ final class ForwardSelectorInteractor: BaseInteractor, ForwardSelectorInteractor if let contacts = self.contacts { completion(contacts) } else { - let contacts = ContactDAO.fetchContacts(with: [.friend]) + let contacts = ContactDAO.fetchPlainContacts(with: [.friend]) .map { makeForwardTarget(with: $0) } .sorted(by: sortComparator) @@ -132,12 +133,19 @@ final class ForwardSelectorInteractor: BaseInteractor, ForwardSelectorInteractor } else { let selectedRooms = targets?.groups ?? [] - let groups = RoomDAO.fetchRooms(kind: .group) - .map { room in - let isSelected = selectedRooms.contains(where: { $0.id == room.id }) - return ForwardTarget(content: .room(room), isSelected: isSelected) - } - .sorted(by: sortComparator) + let groups: [ForwardTarget] + if let rosterId = storageService.rosterId { + let args = ConversationsProviding.FetchingArgs(rosterId: rosterId) + groups = conversationsProviding + .fetchGroups(with: args) + .map { room in + let isSelected = selectedRooms.contains(where: { $0.id == room.id }) + return ForwardTarget(content: .room(room), isSelected: isSelected) + } + .sorted(by: sortComparator) + } else { + groups = [] + } self.groups = groups completion(groups) @@ -182,11 +190,13 @@ extension ForwardSelectorInteractor { let presenter: ForwardSelectorInteractorOutputProtocol let mqttService: MQTTService let storageService: StorageService + let conversationsProviding: ConversationsProviding } func inject(dependencies: Dependencies) { presenter = dependencies.presenter mqttService = dependencies.mqttService storageService = dependencies.storageService + conversationsProviding = dependencies.conversationsProviding } } diff --git a/Nynja/Modules/ForwardSelector/WireFrame/ForwardSelectorWireFrame.swift b/Nynja/Modules/ForwardSelector/WireFrame/ForwardSelectorWireFrame.swift index 6df4be8d4deb8e5ddf603d2c7fc477cade26c86a..33e951de6965ab451f7f7fdad9268636f5d34817 100644 --- a/Nynja/Modules/ForwardSelector/WireFrame/ForwardSelectorWireFrame.swift +++ b/Nynja/Modules/ForwardSelector/WireFrame/ForwardSelectorWireFrame.swift @@ -24,7 +24,13 @@ final class ForwardSelectorWireFrame: ForwardSelectorWireFrameProtocol { let view = ForwardSelectorViewController() let presenter = ForwardSelectorPresenter() - let interactorDependencies = ForwardSelectorInteractor.Dependencies(presenter: presenter, mqttService: MQTTService.sharedInstance, storageService: StorageService.sharedInstance) + let serviceFactory = ServiceFactory() + + let interactorDependencies = ForwardSelectorInteractor.Dependencies( + presenter: presenter, + mqttService: serviceFactory.makeMQTTService(), + storageService: serviceFactory.makeStorageService(), + conversationsProviding: serviceFactory.makeConversationsProvider()) let interactor = ForwardSelectorInteractor(mode: mode) // Connecting diff --git a/Nynja/Modules/GroupsList/Interactor/GroupsListInteractor.swift b/Nynja/Modules/GroupsList/Interactor/GroupsListInteractor.swift index c16ee18b592dc3cf72bec06869f27034189d6902..c44ceff9fe342dc75255fd197c952de1cdc05b61 100644 --- a/Nynja/Modules/GroupsList/Interactor/GroupsListInteractor.swift +++ b/Nynja/Modules/GroupsList/Interactor/GroupsListInteractor.swift @@ -16,6 +16,9 @@ class GroupsListInteractor: BaseInteractor, GroupsListInteractorInputProtocol, I private var chats: [Room] = [] private var searchText: String = "" + private var rosterId: Int64? { + return storageService.rosterId + } // MARK: - InitializeInjectable @@ -67,7 +70,11 @@ class GroupsListInteractor: BaseInteractor, GroupsListInteractorInputProtocol, I applyFilter(with: searchText) } } else { - chats = conversationsProvider.fetchGroups() + guard let args = rosterId.map({ ConversationsProviding.FetchingArgs(rosterId: $0) }) else { + return + } + + chats = conversationsProvider.fetchGroups(with: args) applyFilter(with: searchText) } } @@ -75,8 +82,12 @@ class GroupsListInteractor: BaseInteractor, GroupsListInteractorInputProtocol, I //MARK: - Private - private func fetchGroups(_ filter: String? = nil) { - chats = conversationsProvider.fetchGroups() + private func fetchGroups() { + guard let args = rosterId.map({ ConversationsProviding.FetchingArgs(rosterId: $0) }) else { + return + } + + chats = conversationsProvider.fetchGroups(with: args) presenter.didFetch(groups: chats) } diff --git a/Nynja/Modules/GroupsList/WireFrame/GroupsListWireframe.swift b/Nynja/Modules/GroupsList/WireFrame/GroupsListWireframe.swift index 3db6d4df5599742459d5f5299f85c2822cd85f30..e777359cf6142e2f15ca9347b50e74192f2b7147 100644 --- a/Nynja/Modules/GroupsList/WireFrame/GroupsListWireframe.swift +++ b/Nynja/Modules/GroupsList/WireFrame/GroupsListWireframe.swift @@ -15,18 +15,14 @@ class GroupsListWireFrame: GroupsListWireFrameProtocol { func presentGroupsList(navigation: UINavigationController, main: MainWireFrame, animated: Bool) { self.navigation = navigation - self.main = main - - // Dependencies - let conversationsProvider = ConversationsProvider() - + self.main = main // Compomentes let view = GroupsListViewController() let presenter = GroupsListPresenter() let interactor = GroupsListInteractor( dependencies: .init(storageService: StorageService.sharedInstance, - conversationsProvider: ConversationsProvider())) + conversationsProvider: ServiceFactory().makeConversationsProvider())) // Connecting view.presenter = presenter diff --git a/Nynja/Modules/History/Interactor/HistoryInteractor.swift b/Nynja/Modules/History/Interactor/HistoryInteractor.swift index f8cbf74de164f0d80a7cf9cb649cf7df4d6d74a3..efff84e8e800633a75adbc169be687925a82ffcd 100644 --- a/Nynja/Modules/History/Interactor/HistoryInteractor.swift +++ b/Nynja/Modules/History/Interactor/HistoryInteractor.swift @@ -15,6 +15,9 @@ class HistoryInteractor: BaseInteractor, HistoryInteractorInputProtocol, SetInje private var contacts: [Contact] = [] + private var storageService: StorageService { + return StorageService.sharedInstance + } //MARK: - BaseInteractor @@ -67,7 +70,16 @@ class HistoryInteractor: BaseInteractor, HistoryInteractorInputProtocol, SetInje //MARK: - Private private func _fetchHistory() { - contacts = contactsProvider.fetchHistory() + guard let rosterId = storageService.rosterId, + let phoneId = storageService.phoneId else { + return + } + + let args = ContactsProvidingFetchingArgs( + rosterId: rosterId, + phoneId: phoneId) + + contacts = contactsProvider.fetchHistory(with: args) presenter.fetched(contacts: contacts) } } diff --git a/Nynja/Modules/Main/Interactor/MainInteractor.swift b/Nynja/Modules/Main/Interactor/MainInteractor.swift index a2ad1f2ff875ce309d80d06eb4b1542a1a7aa0d2..244af25b0b4a4c74f89e9e7161a96ca7a9d55b70 100644 --- a/Nynja/Modules/Main/Interactor/MainInteractor.swift +++ b/Nynja/Modules/Main/Interactor/MainInteractor.swift @@ -8,93 +8,123 @@ import SDWebImage import Intercom -class MainInteractor: MainInteractorInputProtocol, EditPhotoDelegate, MQTTServiceDelegate { - func startRinging() { - - } +final class MainInteractor: BaseInteractor, MainInteractorInputProtocol, EditPhotoDelegate, MQTTServiceDelegate, ProfileHandlerDelegate, SetInjectable { weak var presenter: MainInteractorOutputProtocol! var contact: Contact? { return ContactDAO.currentContact } - - init() { - let notificationsSettings = UIUserNotificationSettings(types: [.sound, .alert, .badge], categories: nil) - UIApplication.shared.registerUserNotificationSettings(notificationsSettings) - UIApplication.shared.registerForRemoteNotifications() - let _ = PushService.sharedInstance - - MQTTService.sharedInstance.addSubscriber(self) + + private var initialPhone: String? + + + // MARK: - Dependencies + + private var storageService: StorageService! + private var mqttService: MQTTService! + private var pushService: PushService! + private var communicatorService: NynjaCommunicatorService! + private var badgeNumberService: BadgeNumberServiceProtocol! + private var notificationManager: NotificationManager! + private var userSettingsService: UserSettingsService! + private var syncFileManager: SyncFileManager! + private var alertManager: AlertManager! + + + // MARK: - BaseInteractor + + override func loadData() { + super.loadData() + + ProfileHandler.delegate = self + pushService.registerForNotifications() + mqttService.addSubscriber(self) setupIntercom() } - - func checkSession() { - MQTTService.sharedInstance.reconnect() - } + + + // MARK: - Calls func call(phoneId: String) { if let ctc = ContactDAO.findContactBy(phoneId: phoneId), let name = ctc.fullName { - NynjaCommunicatorService.sharedInstance.call(userId: phoneId, name: name, withVideo: false) + communicatorService.call(userId: phoneId, name: name, withVideo: false) } } func videoCall(phoneId: String) { if let ctc = ContactDAO.findContactBy(phoneId: phoneId), let name = ctc.names { - NynjaCommunicatorService.sharedInstance.call(userId: phoneId, name: name, withVideo: true) + communicatorService.call(userId: phoneId, name: name, withVideo: true) } } func dialInGroup(name: String) { - NynjaCommunicatorService.sharedInstance.dialInGroup(groupname: name) + communicatorService.dialInGroup(groupname: name) } func createGroupCall(callId: String?, contacts: [Contact], room: Room?) { if let cid = callId { - NynjaCommunicatorService.sharedInstance.addAndStartConference(callId: cid, contacts: contacts, room: room) + communicatorService.addAndStartConference(callId: cid, contacts: contacts, room: room) } else { - NynjaCommunicatorService.sharedInstance.createConference(contacts: contacts, room: room) + communicatorService.createConference(contacts: contacts, room: room) } } func createConferenceCall(callId: String?, contacts: [Contact], room: Room?) { if let cid = callId { - NynjaCommunicatorService.sharedInstance.addAndStartConference(callId: cid, contacts: contacts, room: room) + communicatorService.addAndStartConference(callId: cid, contacts: contacts, room: room) } else { - NynjaCommunicatorService.sharedInstance.createConference(contacts: contacts, room: room) + communicatorService.createConference(contacts: contacts, room: room) } } + + + // MARK: - MainInteractorInputProtocol - func updateGroupCall(contacts: [Contact]) { + func checkSession() { + mqttService.reconnect() } - + func findContactBy(phoneId: String) -> Contact? { return ContactDAO.findContactBy(phoneId: phoneId) } func logout() { - MQTTService.sharedInstance.logout() + mqttService.logout() LogService.log(topic: .db) { return "Clear storage: logout" } cleanServices() + mqttService.reconnect() } func deleteAccount() { - if let phone = StorageService.sharedInstance.phone, phone != "" { - MQTTService.sharedInstance.deleteUser(number: phone) + if let phone = storageService.phone, phone != "" { + mqttService.deleteUser(number: phone) } LogService.log(topic: .db) { return "Clear storage: delete account" } + presenter.showUILocker() + } + + private func handleProfileDeleting() { cleanServices() + alertManager.showAlertOk(message: String.localizable.authAttemptsRemoved) + mqttService.reconnect() + presenter.hideUILocker() } private func cleanServices() { - StorageService.sharedInstance.clearStorage() + storageService.clearStorage() + clearSDWebImageCache() + + badgeNumberService.clean() + notificationManager.clean() + userSettingsService.reset() + } + + private func clearSDWebImageCache() { let cache = SDImageCache.shared() cache.clearMemory() cache.clearDisk() - BadgeNumberService.shared.clean() - NotificationManager.shared.clean() - UserSettingsService.shared.reset() } func photoEditFinished(url: URL) { @@ -102,58 +132,103 @@ class MainInteractor: MainInteractorInputProtocol, EditPhotoDelegate, MQTTServic } func updateAvatar(url: URL) { - guard let roster = RosterDAO.currentRoster else { return } - - let sync = SyncFileManager.sharedInstance - sync.downloader = AmazonManager.shared - - roster.avatar = url.absoluteString.getShortPath() + guard let rosterId = storageService.rosterId else { + return + } + do { - try StorageService.sharedInstance.perform(action: .updateColumns([RosterTable.Column.avatar.title]), with: roster) - - SyncFileManager.sharedInstance.saveExternalFileLink(localUrl: url.path) { (ext, progress,request) in - if ext != nil { - if let id = roster.id { - MQTTService.sharedInstance.updateAvatar(id: id, link: String(describing: ext!)) - } - } + try updateAvatar(url: url, rosterId: rosterId) + uploadAvatar(with: url, rosterId: rosterId) + } catch { + assertionFailure("Failed to save avatar url for roster with id \(rosterId)") + } + } + + private func updateAvatar(url: URL, rosterId: Int64) throws { + let roster = Roster() + roster.id = rosterId + roster.avatar = url.absoluteString.getShortPath() + try storageService.perform(action: .updateColumns([RosterTable.Column.avatar.title]), with: roster) + } + + private func uploadAvatar(with url: URL, rosterId: Int64) { + syncFileManager.saveExternalFileLink(localUrl: url.path) { [mqttService](ext, progress, request) in + if ext != nil { + mqttService?.updateAvatar(id: rosterId, link: String(describing: ext!)) } - } catch {} + } } - // MARK: MQTT subscribing + // MARK: - MQTTServiceDelegate func mqttServiceDidConnect(_ mqttService: MQTTService) { - DispatchQueue.main.async { - self.presenter.hideUILocker() - } + presenter.hideUILocker() } func mqttServiceDidReceiveAuthenticationFailure(_ mqttService: MQTTService) { - DispatchQueue.main.async { - self.logout() - self.presenter.changeScreenToAuth() - } - } - - func saveLogoutState() { - MQTTService.sharedInstance.state = .notAuthenticated(isLoggedOutFromServer: false) + logout() + presenter.changeScreenToAuth() } private func setupIntercom() { Intercom.logout() - guard let roster = RosterDAO.currentRoster else { + + guard let contact = ContactDAO.currentContact else { Intercom.registerUnidentifiedUser() return } - let rand = "\(UIDevice.current.persistentIdentifier)" + let userAttributes = ICMUserAttributes() - let id = roster.id != nil ? "\(roster.phoneId!)" : rand + let id = contact.phone_id ?? "\(UIDevice.current.persistentIdentifier)" Intercom.registerUser(withUserId: id) - userAttributes.name = roster.myContact?.fullName - userAttributes.phone = roster.phone + userAttributes.name = contact.fullName + userAttributes.phone = contact.phoneNumber Intercom.updateUser(userAttributes) } + + + // MARK: - ProfileHandlerDelegate + + func profileDeleted(with phone: String) { + guard initialPhone == phone else { + return + } + + handleProfileDeleting() + presenter.changeScreenToAuth() + } +} + +// MARK: - SetInjectable + +extension MainInteractor { + struct Dependencies { + let presenter: MainInteractorOutputProtocol + let storageService: StorageService + let mqttService: MQTTService + let pushService: PushService + let communicatorService: NynjaCommunicatorService + let badgeNumberService: BadgeNumberServiceProtocol + let notificationManager: NotificationManager + let userSettingsService: UserSettingsService + let syncFileManager: SyncFileManager + let alertManager: AlertManager + } + + func inject(dependencies: MainInteractor.Dependencies) { + presenter = dependencies.presenter + storageService = dependencies.storageService + mqttService = dependencies.mqttService + pushService = dependencies.pushService + communicatorService = dependencies.communicatorService + badgeNumberService = dependencies.badgeNumberService + notificationManager = dependencies.notificationManager + userSettingsService = dependencies.userSettingsService + syncFileManager = dependencies.syncFileManager + alertManager = dependencies.alertManager + + initialPhone = storageService.phone + } } diff --git a/Nynja/Modules/Main/MainProtocols.swift b/Nynja/Modules/Main/MainProtocols.swift index 98d9743d629afc3b07f5e446db94b1c08230b531..48a9e0382f33ac5202f7c18610e72afd17441cab 100644 --- a/Nynja/Modules/Main/MainProtocols.swift +++ b/Nynja/Modules/Main/MainProtocols.swift @@ -48,8 +48,8 @@ protocol MainWireFrameProtocol: class { func showAddParticipantsToCreateConferenceCall() func showPayment(profile: Profile, to contact: Contact) - func showScheduleMessage(with mode: ScheduledMessageMode, delegate: ScheduleMessageDelegate?) - func showScheduleMessage(with inputMessage: InputScheduleMessage, delegate: ScheduleMessageDelegate?) + func showScheduleMessage(with mode: ScheduledMessageMode) + func showScheduleMessage(with inputMessage: InputScheduleMessage) func showForwardSelector(with mode: ForwardSelectorMode, delegate: ForwardSelectorDelegate?) @@ -135,7 +135,7 @@ protocol MainViewProtocol: WheelOutProtocol { func notifyAvailability(snackBar: SnackBar, isAvailable: Bool) } -protocol MainPresenterProtocol: class { +protocol MainPresenterProtocol: BasePresenterProtocol { var view: MainViewProtocol! { get set } var interactor: MainInteractorInputProtocol! { get set } @@ -219,13 +219,12 @@ protocol MainInteractorOutputProtocol: class { * Add here your methods for communication INTERACTOR -> PRESENTER */ - func logout() func showUILocker() func hideUILocker() func changeScreenToAuth() } -protocol MainInteractorInputProtocol: class { +protocol MainInteractorInputProtocol: BaseInteractorProtocol { var contact: Contact? { get } @@ -238,19 +237,15 @@ protocol MainInteractorInputProtocol: class { func logout() func deleteAccount() func updateAvatar(url: URL) - func saveLogoutState() func dialInGroup(name: String) func createGroupCall(callId: String?, contacts: [Contact], room: Room?) func createConferenceCall(callId: String?, contacts: [Contact], room: Room?) - func updateGroupCall(contacts: [Contact]) func findContactBy(phoneId: String)->Contact? } -// Provide for compiler info, about extension MainWireFrameProtocol { func showChat(_ chat: ChatModel) { showChat(chat, initialMessage: nil) } - } diff --git a/Nynja/Modules/Main/Presenter/MainPresenter.swift b/Nynja/Modules/Main/Presenter/MainPresenter.swift index 5292588ec19362b679ba34d43d2077e74897c3bb..6a2574f2f3c4c9468accede9ec07229b1eae48a2 100644 --- a/Nynja/Modules/Main/Presenter/MainPresenter.swift +++ b/Nynja/Modules/Main/Presenter/MainPresenter.swift @@ -6,15 +6,21 @@ // Copyright © 2017 TecSynt Solutions. All rights reserved. // -class MainPresenter: MainPresenterProtocol, MainInteractorOutputProtocol, ScheduleMessageDelegate, EditParticipantsDelegate { +final class MainPresenter: BasePresenter, MainPresenterProtocol, MainInteractorOutputProtocol, EditParticipantsDelegate, SetInjectable { func returnToCall(call: NYNCall?) { self.wireFrame.returnToCall(call: call) } weak var view: MainViewProtocol! - var interactor: MainInteractorInputProtocol! var wireFrame: MainWireFrameProtocol! + var interactor: MainInteractorInputProtocol! { + didSet { + _interactor = interactor + } + } + + private var reachabilityService: ReachabilityService! func showQRReader() { wireFrame.showQRReader() @@ -135,13 +141,12 @@ class MainPresenter: MainPresenterProtocol, MainInteractorOutputProtocol, Schedu } func logout() { - self.interactor.saveLogoutState() - self.interactor.logout() - self.changeScreenToAuth() + interactor.logout() + changeScreenToAuth() } func changeScreenToAuth() { - self.wireFrame.logout() + wireFrame.logout() } func showMessages (room: Room, call: NYNCall, callVC: CallInProgressViewProtocol, isVideo: Bool = false) { @@ -233,8 +238,9 @@ class MainPresenter: MainPresenterProtocol, MainInteractorOutputProtocol, Schedu } func deleteAccount() { - self.interactor.deleteAccount() - self.wireFrame.logout() + reachabilityService.performIfConnected { + interactor.deleteAccount() + } } func showDataAndStorage() { @@ -292,13 +298,9 @@ class MainPresenter: MainPresenterProtocol, MainInteractorOutputProtocol, Schedu view?.hideUILocker() } - // MARK: - ScheduleMessageDelegate - func scheduleMessageHasBeenSent() { - //TODO: -// view.cleanTextInput() - } - + // MARK: EditParticpantsDelegate + func participantsUpdated(contacts: [Contact]) { } @@ -316,5 +318,23 @@ class MainPresenter: MainPresenterProtocol, MainInteractorOutputProtocol, Schedu break } } +} + + +// MARK: - SetInjectable +extension MainPresenter { + struct Dependencies { + let view: MainViewProtocol + let wireFrame: MainWireFrameProtocol + let interactor: MainInteractorInputProtocol + let reachabilityService: ReachabilityService + } + + func inject(dependencies: MainPresenter.Dependencies) { + view = dependencies.view + wireFrame = dependencies.wireFrame + interactor = dependencies.interactor + reachabilityService = dependencies.reachabilityService + } } diff --git a/Nynja/Modules/Main/View/MainViewController+Recents.swift b/Nynja/Modules/Main/View/MainViewController+Recents.swift index 8963fb3d5abea932f2ce09d450da457d80a4d00d..3e7a8cb5f9bbc43fbc2fc12214b58e6b29d69211 100644 --- a/Nynja/Modules/Main/View/MainViewController+Recents.swift +++ b/Nynja/Modules/Main/View/MainViewController+Recents.swift @@ -43,15 +43,30 @@ extension MainViewController { // MARK: - Chats func showRecentP2PChats(indexPath: IndexPath?) { - showRecentConversation(indexPath: indexPath, conversations: ConversationsProvider().fetchChats()) + let conversations = fetchConversation( + using: ServiceFactory().makeConversationsProvider().fetchChats(with:)) + showRecentConversation(indexPath: indexPath, conversations: conversations) } func showRecentGroupChats(indexPath: IndexPath?) { - showRecentConversation(indexPath: indexPath, conversations: ConversationsProvider().fetchGroups()) + let conversations = fetchConversation( + using: ServiceFactory().makeConversationsProvider().fetchGroups(with:)) + showRecentConversation(indexPath: indexPath, conversations: conversations) } - + func showRecentChannels(indexPath: IndexPath?) { - showRecentConversation(indexPath: indexPath, conversations: ConversationsProvider().fetchChannels()) + let conversations = fetchConversation( + using: ServiceFactory().makeConversationsProvider().fetchChannels(with:)) + showRecentConversation(indexPath: indexPath, conversations: conversations) + } + + private func fetchConversation(using fetcher: (ConversationsProviding.FetchingArgs) -> [ChatModel]) -> [ChatModel] { + guard let rosterId = StorageService.sharedInstance.rosterId else { + return [] + } + + let args = ConversationsProviding.FetchingArgs(rosterId: rosterId) + return fetcher(args) } private func showRecentConversation(indexPath: IndexPath?, conversations: [ChatModel]) { @@ -86,5 +101,4 @@ extension MainViewController { } } } - } diff --git a/Nynja/Modules/Main/View/MainViewController.swift b/Nynja/Modules/Main/View/MainViewController.swift index 36d8fc95ffb81febca43d1bfca7ba9b69bb74448..2531476ff639e3b43c5db80f84b15721fb8e1f80 100644 --- a/Nynja/Modules/Main/View/MainViewController.swift +++ b/Nynja/Modules/Main/View/MainViewController.swift @@ -13,11 +13,15 @@ import MobileCoreServices import AssetsLibrary import AVFoundation -class MainViewController: BaseVC, MainViewProtocol, HitTestDelegate, UINavigationControllerDelegate, WheelPreviewProtocol { +final class MainViewController: BaseVC, MainViewProtocol, HitTestDelegate, UINavigationControllerDelegate, WheelPreviewProtocol { private var cameraCoordinator: CameraFlowCoordinatorProtocol? private var galleryCoordinator: GalleryFlowCoordinatorProtocol? - var presenter: MainPresenterProtocol! + var presenter: MainPresenterProtocol! { + didSet { + _presenter = presenter + } + } var galleryFetchController: GalleryFetchController? diff --git a/Nynja/Modules/Main/WireFrame/MainWireframe.swift b/Nynja/Modules/Main/WireFrame/MainWireframe.swift index be8b325b8c8e32faec0470ebff160a241e91b914..e7e186cf71919a553355b98f720d01edc5ab96dc 100644 --- a/Nynja/Modules/Main/WireFrame/MainWireframe.swift +++ b/Nynja/Modules/Main/WireFrame/MainWireframe.swift @@ -10,7 +10,7 @@ import UIKit import CoreLocation import SnapKit -class MainWireFrame: MainWireFrameProtocol, NynjaCommunicatorServiceDelegate { +final class MainWireFrame: MainWireFrameProtocol, NynjaCommunicatorServiceDelegate { weak var navigation : UINavigationController? weak var contentNavigation: UINavigationController! @@ -20,6 +20,8 @@ class MainWireFrame: MainWireFrameProtocol, NynjaCommunicatorServiceDelegate { weak var external: EditParticipantsDelegate? = nil func presentMain(navigation: UINavigationController, isRegistered: Bool, checkSession: Bool = false) { + let serviceFactory = ServiceFactory() + let view = MainViewController() let presenter = MainPresenter() let interactor = MainInteractor() @@ -31,10 +33,26 @@ class MainWireFrame: MainWireFrameProtocol, NynjaCommunicatorServiceDelegate { // Connecting view.presenter = presenter - presenter.view = view - presenter.wireFrame = self - presenter.interactor = interactor - interactor.presenter = presenter + + presenter.inject( + dependencies: .init( + view: view, + wireFrame: self, + interactor: interactor, + reachabilityService: serviceFactory.makeReachabilityService())) + + interactor.inject( + dependencies: .init( + presenter: presenter, + storageService: serviceFactory.makeStorageService(), + mqttService: serviceFactory.makeMQTTService(), + pushService: serviceFactory.makePushService(), + communicatorService: serviceFactory.makeNynjaCommunicatorService(), + badgeNumberService: serviceFactory.makeBadgeNumberService(), + notificationManager: serviceFactory.makeNotificationManager(), + userSettingsService: serviceFactory.makeUserSettingsService(), + syncFileManager: serviceFactory.makeSyncFileManager(with: .amazon), + alertManager: serviceFactory.makeAlertManager())) // set content view let contentNavigation = ContentNavigationVC() @@ -145,15 +163,15 @@ class MainWireFrame: MainWireFrameProtocol, NynjaCommunicatorServiceDelegate { HistoryWireFrame().presentHistory(navigation: contentNavigation, mainWireFrame: self) } - func showScheduleMessage(with mode: ScheduledMessageMode, delegate: ScheduleMessageDelegate? = nil) { + func showScheduleMessage(with mode: ScheduledMessageMode) { if let navigation = self.navigation { ScheduleMessageWireFrame().presentScheduleMessage(navigation: navigation, main: self, mode: mode) } } - func showScheduleMessage(with inputMessage: InputScheduleMessage, delegate: ScheduleMessageDelegate? = nil) { + func showScheduleMessage(with inputMessage: InputScheduleMessage) { if let info = messageinteractor?.scheduleInfo(for: inputMessage) { - showScheduleMessage(with: .create(info: info), delegate: delegate) + showScheduleMessage(with: .create(info: info)) } } @@ -275,10 +293,6 @@ class MainWireFrame: MainWireFrameProtocol, NynjaCommunicatorServiceDelegate { isVideo = false self.showReturnToCallView() self.view?.hidePartnerVideoView() - -// if let call = self.call { -// self.view?.showReturnToCall(call: call) -// } } func returnToCall(call: NYNCall?) { diff --git a/Nynja/Modules/MapSearch/Interactor/MapSearchInteractor.swift b/Nynja/Modules/MapSearch/Interactor/MapSearchInteractor.swift index d49dc2aafdbdca48122f5a88f2a6936d88e602dd..e0ccb32f73f63c19ca84a2d468232012725563db 100644 --- a/Nynja/Modules/MapSearch/Interactor/MapSearchInteractor.swift +++ b/Nynja/Modules/MapSearch/Interactor/MapSearchInteractor.swift @@ -49,6 +49,7 @@ class MapSearchInteractor: MapSearchInteractorInputProtocol { guard let id = StorageService.sharedInstance.rosterId else { return [] } + // TODO: rewrite as SQL script return StarDAO.fetchStars(rosterId: id).values.compactMap { $0.message?.files?.first?.placeId } } diff --git a/Nynja/Modules/Message/Interactor/MessageInteractor.swift b/Nynja/Modules/Message/Interactor/MessageInteractor.swift index d455ff61bb155d1ae3b1223047236d17fb0d0cf5..686bad549bf6bea715adb3ff57d8750e031de29e 100644 --- a/Nynja/Modules/Message/Interactor/MessageInteractor.swift +++ b/Nynja/Modules/Message/Interactor/MessageInteractor.swift @@ -10,7 +10,7 @@ import UIKit import CoreLocation -final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, HistoryHandlerDelegate, TypingHandlerDelegate, ConnectionServiceDelegate, MQTTServiceDelegate, MessageProcessingDelegate, MessageHandlerSubscriber, MessageInteractorCallProtocol { +final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, HistoryHandlerSubscriber, TypingHandlerDelegate, ConnectionServiceDelegate, MQTTServiceDelegate, MessageProcessingDelegate, MessageHandlerSubscriber, MessageInteractorCallProtocol { private var callService = NynjaCommunicatorService.sharedInstance @@ -915,7 +915,7 @@ final class MessageInteractor: BaseInteractor, MessageInteractorInputProtocol, H } } - // MARK: - HistoryHandlerDelegate + // MARK: - HistoryHandlerSubscriber private var isNew = false private var isHistoryUpdating: Bool = false diff --git a/Nynja/Modules/Message/WireFrame/MessageWireframe.swift b/Nynja/Modules/Message/WireFrame/MessageWireframe.swift index bdb7e114f5512109a9dcfeb8024767863cd48199..71987cd8cb5f6cfbdcb08beb1386e8ef126d680d 100644 --- a/Nynja/Modules/Message/WireFrame/MessageWireframe.swift +++ b/Nynja/Modules/Message/WireFrame/MessageWireframe.swift @@ -127,7 +127,7 @@ class MessageWireFrame: MessageWireframeProtocol, DocumentInteractionWireFrame { } func openSchedule(with inputMessage: InputScheduleMessage) { - main?.showScheduleMessage(with: inputMessage, delegate: nil) + main?.showScheduleMessage(with: inputMessage) } func deleteAndLeave() { diff --git a/Nynja/Modules/Participants/Interactor/ParticipantsInteractor.swift b/Nynja/Modules/Participants/Interactor/ParticipantsInteractor.swift index babd3b0c4482bd136a25a35d649b297d32a50d29..5e2db5099eb3ab2dee089799851f60ee9a6659cd 100644 --- a/Nynja/Modules/Participants/Interactor/ParticipantsInteractor.swift +++ b/Nynja/Modules/Participants/Interactor/ParticipantsInteractor.swift @@ -84,7 +84,7 @@ class ParticipantsInteractor: BaseInteractor, ParticipantsInteractorInputProtoco //MARK: - Private private func loadContacts() { - let contacts = ContactDAO.fetchContacts(with: [.friend, .ban, .banned]) + let contacts = ContactDAO.fetchPlainContacts(with: [.friend, .ban, .banned]) mapToParticipants(contacts) } diff --git a/Nynja/Modules/Profile/Interactor/HomeDataProvider/HomeDataProvider.swift b/Nynja/Modules/Profile/Interactor/HomeDataProvider/HomeDataProvider.swift new file mode 100644 index 0000000000000000000000000000000000000000..eb45e28c1432c39daeea1f881579b20d71c5d98b --- /dev/null +++ b/Nynja/Modules/Profile/Interactor/HomeDataProvider/HomeDataProvider.swift @@ -0,0 +1,20 @@ +// +// HomeDataProvider.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/2/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +protocol HomeDataProvider { + var conversationsProviding: ConversationsProviding { get } + var contactsProviding: ContactsProviding { get } + + var limit: Int { get } + + func fetchChats(rosterId: Int64) -> [Contact] + func fetchGroups(rosterId: Int64) -> [Room] + func fetchHistory(rosterId: Int64, phoneId: String) -> [Contact] + func fetchStars(rosterId: Int64) -> [Star] + func fetchScheduledMessages() -> [ScheduledMessage] +} diff --git a/Nynja/Modules/Profile/Interactor/HomeDataProvider/HomeDataProviderImpl.swift b/Nynja/Modules/Profile/Interactor/HomeDataProvider/HomeDataProviderImpl.swift new file mode 100644 index 0000000000000000000000000000000000000000..876ff879590c389636c0aa6435b2a54889bfe3e4 --- /dev/null +++ b/Nynja/Modules/Profile/Interactor/HomeDataProvider/HomeDataProviderImpl.swift @@ -0,0 +1,119 @@ +// +// HomeDataProviderImpl.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/2/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import GRDBCipher + +final class HomeDataProviderImpl: HomeDataProvider, InitializeInjectable { + let conversationsProviding: ConversationsProviding + let contactsProviding: ContactsProviding + let limit: Int + + required init(dependencies: Dependencies) { + conversationsProviding = dependencies.services.conversationsProviding + contactsProviding = dependencies.services.contactsProviding + limit = dependencies.fields.limit + } + + // MARK: - HomeDataProvider + // MARK: - Conversations + + func fetchChats(rosterId: Int64) -> [Contact] { + let fetcher = conversationsProviding.fetchChats(with:) + return fetchConversations(rosterId: rosterId, fetcher: fetcher) + } + + func fetchGroups(rosterId: Int64) -> [Room] { + let fetcher = conversationsProviding.fetchGroups(with:) + return fetchConversations(rosterId: rosterId, fetcher: fetcher) + } + + private func fetchConversations( + rosterId: Int64, + fetcher: (ConversationsProviding.FetchingArgs) -> [T]) -> [T] { + + let args = makeConversationsProvidingFetchingArgs(rosterId: rosterId) + return fetcher(args) + } + + private func makeConversationsProvidingFetchingArgs(rosterId: Int64) -> ConversationsProviding.FetchingArgs { + return ConversationsProviding.FetchingArgs( + rosterId: rosterId, + onlyUnread: true, + limit: limit) + } + + + // MARK: - History + + func fetchHistory(rosterId: Int64, phoneId: String) -> [Contact] { + let args = makeContactsProvidingFetchingArgs(rosterId: rosterId, phoneId: phoneId) + return contactsProviding.fetchHistory(with: args) + } + + private func makeContactsProvidingFetchingArgs(rosterId: Int64, phoneId: String) -> ContactsProvidingFetchingArgs { + return ContactsProvidingFetchingArgs( + rosterId: rosterId, + phoneId: phoneId, + statuses: [.request, .ignore], + limit: limit) + } + + + // MARK: - Stars + + func fetchStars(rosterId: Int64) -> [Star] { + let args = makeStarQueryArgs(rosterId: rosterId) + return StarDAO.fetchStars(with: args) + } + + private func makeStarQueryArgs(rosterId: Int64) -> RosterRelatedQueryArgs { + return RosterRelatedQueryArgs( + rosterId: rosterId, + args: .init( + limit: limit, + orderingTerms: [Column.created.desc])) + } + + + // MARK: - Jobs + + func fetchScheduledMessages() -> [ScheduledMessage] { + let args = makeDBJobQueryArgs() + + return DAO() + .fetch(with: args) + .serverModels + .map { ScheduledMessage(job: $0) } + } + + private func makeDBJobQueryArgs() -> DBJob.QueryArgs { + return DBJob.QueryArgs( + type: .schedule, + args: .init( + limit: limit, + orderingTerms: [Column.id.desc])) + } +} + + +extension HomeDataProviderImpl { + + struct Dependencies { + let services: Services + let fields: Fields + } + + struct Services { + let conversationsProviding: ConversationsProviding + let contactsProviding: ContactsProviding + } + + struct Fields { + let limit: Int + } +} diff --git a/Nynja/Modules/Profile/Interactor/ProfileInteractor.swift b/Nynja/Modules/Profile/Interactor/ProfileInteractor.swift index 0b73d2bbf27c8549af0f788996db16b08311efc1..613cb6390408365aef6abf6583c3e5b33a4fcaf7 100644 --- a/Nynja/Modules/Profile/Interactor/ProfileInteractor.swift +++ b/Nynja/Modules/Profile/Interactor/ProfileInteractor.swift @@ -15,13 +15,13 @@ class ProfileInteractor: BaseInteractor, ProfileInteractorInputProtocol { var myContact: Contact! var chats: CellModels = [] - var history: CellModels = [] var rooms: CellModels = [] + var history: CellModels = [] var starred: CellModels = [] var scheduled: CellModels = [] + private var homeDataProvider: HomeDataProvider! private var contactsProvider: ContactsProviding! - private var conversationsProvider: ConversationsProviding! private var mqttService: MQTTService! //MARK: - BaseInteractor @@ -101,41 +101,43 @@ fileprivate extension ProfileInteractor { } func fetchChats() { - let models = conversationsProvider - .fetchChats() - .filter { $0.unreadCount > 0 } - - chats = Array(models.prefix(presenter.elemsInSection)) + performIfRosterIdExists { rosterId in + chats = homeDataProvider.fetchChats(rosterId: rosterId) + } } func fetchRooms() { - let models = conversationsProvider - .fetchGroups() - .filter { $0.unreadCount > 0 } + performIfRosterIdExists { rosterId in + rooms = homeDataProvider.fetchGroups(rosterId: rosterId) + } + } + + private func performIfRosterIdExists(_ closure: (Int64) -> Void) { + guard let rosterId = StorageService.sharedInstance.rosterId else { + return + } - rooms = Array(models.prefix(presenter.elemsInSection)) + closure(rosterId) } func fetchHistory() { - let models = contactsProvider.fetchHistory(without: [.request, .ignore]) - history = Array(models.prefix(presenter.elemsInSection)) + performIfRosterIdExists { rosterId in + guard let phoneId = StorageService.sharedInstance.phoneId else { + return + } + + history = homeDataProvider.fetchHistory(rosterId: rosterId, phoneId: phoneId) + } } func fetchStar() { - guard let id = StorageService.sharedInstance.rosterId else { - starred = [Star]() - return + performIfRosterIdExists { rosterId in + starred = homeDataProvider.fetchStars(rosterId: rosterId) } - let stars = StarDAO.fetchStars(rosterId: id).values.sorted { $0.timestamp > $1.timestamp } - starred = Array(stars.prefix(presenter.elemsInSection)) } func fetchScheduledMessages() { - let jobs = JobDAO.fetchJobs(of: .schedule).sorted { $0.id ?? 0 > $1.id ?? 0 } - let scheduledMessages = jobs.map { - ScheduledMessage(job: $0) - } - scheduled = Array(scheduledMessages.prefix(presenter.elemsInSection)) + scheduled = homeDataProvider.fetchScheduledMessages() } func fetchLastEvents() { @@ -159,15 +161,15 @@ fileprivate extension ProfileInteractor { extension ProfileInteractor: SetInjectable { func inject(dependencies: ProfileInteractor.Dependencies) { presenter = dependencies.presenter + homeDataProvider = dependencies.homeDataProvider contactsProvider = dependencies.contactsProvider - conversationsProvider = dependencies.conversationsProvider mqttService = dependencies.mqttService } struct Dependencies { let presenter: ProfileInteractorOutputProtocol + let homeDataProvider: HomeDataProvider let contactsProvider: ContactsProviding - let conversationsProvider: ConversationsProviding let mqttService: MQTTService } } diff --git a/Nynja/Modules/Profile/WireFrame/ProfileWireframe.swift b/Nynja/Modules/Profile/WireFrame/ProfileWireframe.swift index 119210b333d4166f8356782538a7e9dff47d225b..3e001cd9d92c3d22955b5d6180d71f77b2ddf96a 100644 --- a/Nynja/Modules/Profile/WireFrame/ProfileWireframe.swift +++ b/Nynja/Modules/Profile/WireFrame/ProfileWireframe.swift @@ -15,27 +15,25 @@ class ProfileWireFrame: ProfileWireFrameProtocol { func presentProfile(navigation: UINavigationController, main: MainWireFrame?) { - // Dependencies - let contactsProvider = ContactsProvider() - let conversationsProvider = ConversationsProvider() - let mqttService = MQTTService.sharedInstance + let serviceFactory = ServiceFactory() // Components let view = ProfileViewController() let presenter = ProfilePresenter() let interactor = ProfileInteractor() - - let interactorDependencies = ProfileInteractor.Dependencies(presenter: presenter, - contactsProvider: contactsProvider, - conversationsProvider: conversationsProvider, - mqttService: mqttService) - let presenterDependencies = ProfilePresenter.Dependencies(view: view, wireFrame: self, interactor: interactor) - let viewDependencies = ProfileViewController.Dependencies(presenter: presenter) - - - interactor.inject(dependencies: interactorDependencies) - presenter.inject(dependencies: presenterDependencies) - view.inject(dependencies: viewDependencies) + + interactor.inject( + dependencies: .init( + presenter: presenter, + homeDataProvider: serviceFactory.makeHomeDataProvider(limit: 5), + contactsProvider: serviceFactory.makeContactsProvider(), + mqttService: serviceFactory.makeMQTTService())) + presenter.inject( + dependencies: .init( + view: view, + wireFrame: self, + interactor: interactor)) + view.inject(dependencies: .init(presenter: presenter)) self.navigation = navigation self.main = main diff --git a/Nynja/Modules/QRCodeReader/Interactor/QRCodeReaderInteractor.swift b/Nynja/Modules/QRCodeReader/Interactor/QRCodeReaderInteractor.swift index 60cf171cd21e43a42267ef7b9f62a73bf58541e6..0f7e8eaaf8aad61fc960c1dcedcddf46a4f070d5 100644 --- a/Nynja/Modules/QRCodeReader/Interactor/QRCodeReaderInteractor.swift +++ b/Nynja/Modules/QRCodeReader/Interactor/QRCodeReaderInteractor.swift @@ -21,7 +21,7 @@ class QRCodeReaderInteractor: QRCodeReaderInteractorInputProtocol, IoHandlerDele currentNumer = number if StorageService.sharedInstance.phone == number { self.presenter.getMyProfile() - } else if let contact = ContactDAO.findContactBy(phone: number) { + } else if let contact = ContactDAO.findPlainContactBy(phone: number) { self.presenter.getContactSuccess(contact: contact) } else { MQTTService.sharedInstance.tryFindContact(number: number, modelReference: .QRCODE) diff --git a/Nynja/Modules/ScheduleMessage/Presenter/ScheduleMessagePresenter.swift b/Nynja/Modules/ScheduleMessage/Presenter/ScheduleMessagePresenter.swift index 781e36ed9e694223a911afb5e1b48bd16b118ba9..c99265c6c6ed1a0d14788852461a81efa462e6f5 100644 --- a/Nynja/Modules/ScheduleMessage/Presenter/ScheduleMessagePresenter.swift +++ b/Nynja/Modules/ScheduleMessage/Presenter/ScheduleMessagePresenter.swift @@ -8,8 +8,6 @@ final class ScheduleMessagePresenter: BasePresenter, ScheduleMessagePresenterProtocol, ScheduleMessageInteractorOutputProtocol, DateTimePickerDelegate, TimeZoneSelectorDelegate, ForwardSelectorDelegate, ConnectionServiceDelegate { - weak var delegate: ScheduleMessageDelegate? - weak var view: ScheduleMessageViewProtocol! var wireFrame: ScheduleMessageWireFrameProtocol! var interactor: ScheduleMessageInteractorInputProtocol! { @@ -96,7 +94,6 @@ final class ScheduleMessagePresenter: BasePresenter, ScheduleMessagePresenterPro } else { self.goBack() } - self.delegate?.scheduleMessageHasBeenSent() } } diff --git a/Nynja/Modules/ScheduleMessage/ScheduleMessageProtocols.swift b/Nynja/Modules/ScheduleMessage/ScheduleMessageProtocols.swift index fb6c9a4ab37bda58094e0448e055400f28497e96..848f0670e7c9a96f128f5d0c479b7ea0eef15917 100644 --- a/Nynja/Modules/ScheduleMessage/ScheduleMessageProtocols.swift +++ b/Nynja/Modules/ScheduleMessage/ScheduleMessageProtocols.swift @@ -13,15 +13,9 @@ enum ScheduledMessageMode { case edit(jobId: Int64) } -protocol ScheduleMessageDelegate: class { - - func scheduleMessageHasBeenSent() - -} - protocol ScheduleMessageWireFrameProtocol: class { - func presentScheduleMessage(navigation: UINavigationController, main: MainWireFrameProtocol?, mode: ScheduledMessageMode, delegate: ScheduleMessageDelegate?) + func presentScheduleMessage(navigation: UINavigationController, main: MainWireFrameProtocol?, mode: ScheduledMessageMode) /** * Add here your methods for communication PRESENTER -> WIREFRAME diff --git a/Nynja/Modules/ScheduleMessage/WireFrame/ScheduleMessageWireframe.swift b/Nynja/Modules/ScheduleMessage/WireFrame/ScheduleMessageWireframe.swift index 179084cb606b881203ee856a0ca51e94deb322e0..6de7d02c2a60ae3d4034d919e98ad77caf6053b3 100644 --- a/Nynja/Modules/ScheduleMessage/WireFrame/ScheduleMessageWireframe.swift +++ b/Nynja/Modules/ScheduleMessage/WireFrame/ScheduleMessageWireframe.swift @@ -13,14 +13,12 @@ final class ScheduleMessageWireFrame: ScheduleMessageWireFrameProtocol { weak var main: MainWireFrameProtocol? weak var navigation: UINavigationController? - func presentScheduleMessage(navigation: UINavigationController, main: MainWireFrameProtocol?, mode: ScheduledMessageMode, delegate: ScheduleMessageDelegate? = nil) { + func presentScheduleMessage(navigation: UINavigationController, main: MainWireFrameProtocol?, mode: ScheduledMessageMode) { let view = ScheduleMessageViewController() let presenter = ScheduleMessagePresenter() let interactor = ScheduleMessageInteractor(mode: mode) - presenter.delegate = delegate - self.main = main self.navigation = navigation diff --git a/Nynja/Modules/Settings/Privacy/Interactor/PrivacyListInteractor.swift b/Nynja/Modules/Settings/Privacy/Interactor/PrivacyListInteractor.swift index f549baa5662d4f9168704c5ecb33842186b66310..05273084c39acabb3b241d8181889df969bae906 100644 --- a/Nynja/Modules/Settings/Privacy/Interactor/PrivacyListInteractor.swift +++ b/Nynja/Modules/Settings/Privacy/Interactor/PrivacyListInteractor.swift @@ -13,7 +13,7 @@ final class PrivacyListInteractor: BaseInteractor, PrivacyListInteractorInputPr weak var presenter: PrivacyListInteractorOutputProtocol! private var filterText: String? private var contacts: [Contact] { - return ContactDAO.fetchContacts(with: [.banned]) + return ContactDAO.fetchPlainContacts(with: [.banned]) } // MARK: - PrivacyListInteractorInputProtocol diff --git a/Nynja/Modules/Splash/Interactor/SplashInteractor.swift b/Nynja/Modules/Splash/Interactor/SplashInteractor.swift index cec6ce8a9625787480116f4fedbe45c300be8deb..f683f5febe573d05aaf2002f5109fa30a1ca6b5f 100644 --- a/Nynja/Modules/Splash/Interactor/SplashInteractor.swift +++ b/Nynja/Modules/Splash/Interactor/SplashInteractor.swift @@ -45,7 +45,7 @@ class SplashInteractor: SplashInteractorInputProtocol { application.applicationIconBadgeNumber = Int(badgeNumber) } - mqttService.initialize() + mqttService.connect() callService.initialize() MediaDownloadManager.setupAppDataUsageSettingsIfNeeded() @@ -59,9 +59,9 @@ class SplashInteractor: SplashInteractorInputProtocol { } guard storageService.isUserLogined, let phoneId = storageService.phoneId else { - LogService.log(topic: .db) { return """ - Clear storage: hasPhone = \(storageService.hasPhone), hasToken = \(storageService.hasToken), - phoneId = \(storageService.phoneId ?? "none") + LogService.log(topic: .db) { [weak self] in return """ + Clear storage: hasPhone = \(self?.storageService.hasPhone), hasToken = \(self?.storageService.hasToken), + phoneId = \(self?.storageService.phoneId ?? "none") """ } prepareToShowAuth() return @@ -70,16 +70,16 @@ class SplashInteractor: SplashInteractorInputProtocol { LogService.log(topic: .db) { return "Setup DB: Splash" } storageService.setupDatabase(with: phoneId, application: UIApplication.shared) - guard let roster = RosterDAO.currentRoster else { - LogService.log(topic: .db) { return "Clear storage: can't find current roster" } + guard let contact = ContactDAO.currentContact else { + LogService.log(topic: .db) { return "Clear storage: can't find current contact" } prepareToShowAuth() return } - if roster.hasName { + if contact.hasName { presenter.showMain() } else { - presenter.showEditProfile(roster: roster) + presenter.showEditProfile() } } diff --git a/Nynja/Modules/Splash/Presenter/SplashPresenter.swift b/Nynja/Modules/Splash/Presenter/SplashPresenter.swift index 8ea3595dc9c4f78a5e66ac2e7c0b482bb702c4e9..f001fed03c86c84548e7b3b774b7258d4ea5a274 100644 --- a/Nynja/Modules/Splash/Presenter/SplashPresenter.swift +++ b/Nynja/Modules/Splash/Presenter/SplashPresenter.swift @@ -33,8 +33,8 @@ class SplashPresenter: BasePresenter, SplashPresenterProtocol, SplashInteractorO self.wireFrame.showMain() } - func showEditProfile(roster: Roster) { - self.wireFrame.showEditProfile(roster: roster) + func showEditProfile() { + self.wireFrame.showEditProfile() } func showJailbreakAlert(with completion: @escaping () -> Void) { diff --git a/Nynja/Modules/Splash/SplashProtocols.swift b/Nynja/Modules/Splash/SplashProtocols.swift index ba8af6fb133dc989434c5727b011b4be7277699a..93800181eae0cb56ecbd7c2dc4d330be875a2a7d 100644 --- a/Nynja/Modules/Splash/SplashProtocols.swift +++ b/Nynja/Modules/Splash/SplashProtocols.swift @@ -19,7 +19,7 @@ protocol SplashWireFrameProtocol: class { func showAuth() func showTutorial() func showMain() - func showEditProfile(roster: Roster) + func showEditProfile() } protocol SplashViewProtocol: class { @@ -51,7 +51,7 @@ protocol SplashInteractorOutputProtocol: class { func showAuth() func showTutorial() func showMain() - func showEditProfile(roster: Roster) + func showEditProfile() func showJailbreakAlert(with completion: @escaping () -> Void) } diff --git a/Nynja/Modules/Splash/View/SplashViewController.swift b/Nynja/Modules/Splash/View/SplashViewController.swift index 5ad891dfd7de0bff951df4c5c207f1e6cc20438a..43a74e7c4d9f7dd4ab61bb029abd3b671f12db33 100644 --- a/Nynja/Modules/Splash/View/SplashViewController.swift +++ b/Nynja/Modules/Splash/View/SplashViewController.swift @@ -16,7 +16,9 @@ class SplashViewController: BaseVC, SplashViewProtocol { } } + // MARK: - Views + lazy var splash: UIView = { let launchScreen = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController() if let launchView = launchScreen?.view { @@ -40,7 +42,7 @@ class SplashViewController: BaseVC, SplashViewProtocol { let lbl = UILabel() lbl.font = UIFont(name: FontFamily.NotoSans.bold.name, size: 16)! lbl.numberOfLines = 0 - lbl.textColor = UIColor.nynja.manatee //UIColor.nynja.almostBlack + lbl.textColor = UIColor.nynja.manatee self.view.addSubview(lbl) lbl.snp.makeConstraints({ (make) in @@ -50,16 +52,15 @@ class SplashViewController: BaseVC, SplashViewProtocol { return lbl }() + // MARK: - View lifecycle + override func viewDidLoad() { super.viewDidLoad() self.view.frame = UIScreen.main.bounds splash.isHidden = false - if let dict = Bundle.main.infoDictionary { - if let buildV = dict["CFBundleVersion"] as? String { - let host = MQTTService.sharedInstance.host - info.text = "Build: \(buildV)"//" \nServer: \(host) (\"\(host.url)\")" - } + if let buildVersion = Bundle.main.buildVersion { + info.text = "Build: \(buildVersion)" } } diff --git a/Nynja/Modules/Splash/WireFrame/SplashWireframe.swift b/Nynja/Modules/Splash/WireFrame/SplashWireframe.swift index decce33128789c0c43ff81e1b325ae7079533f0d..949955c740463634f2c0eae9538bbb637a592731 100644 --- a/Nynja/Modules/Splash/WireFrame/SplashWireframe.swift +++ b/Nynja/Modules/Splash/WireFrame/SplashWireframe.swift @@ -41,11 +41,7 @@ class SplashWireFrame: SplashWireFrameProtocol { MainWireFrame().presentMain(navigation: navigation!, isRegistered: false, checkSession: true) } - func showEditProfile(roster: Roster) { - guard let contact = roster.myContact else { - assertionFailure("Contact should exist!") - return - } + func showEditProfile() { EditProfileWireFrame().presentEditProfile(navigation: navigation!, isRegistered: true, main: nil) } } diff --git a/Nynja/NotificationManager.swift b/Nynja/NotificationManager.swift index 7b86fdf0a68ffae8a0eedb199e04f95d7ce7b39d..16778475e3166dfccae989cad19d9a2a2b675e99 100644 --- a/Nynja/NotificationManager.swift +++ b/Nynja/NotificationManager.swift @@ -53,6 +53,10 @@ final class NotificationManager { // MARK: - Properties + private let processingQueue = DispatchQueue( + label: String.label(withSuffix: "notification-manager.processing-queue"), + qos: .utility) + private(set) var previousView: NotificationView? private(set) var typeForNavigate: InAppNotificationType? { @@ -107,15 +111,19 @@ final class NotificationManager { return } - handleNavigation(presenter: mainPresenter) + processingQueue.async { [weak self] in + self?.handleNavigation(presenter: mainPresenter) + } } // MARK: - Handle BERT func handle(bert: BertTuple, type: InAppNotificationType) { - tupleForNavigate = bert - typeForNavigate = type + processingQueue.async { [weak self] in + self?.tupleForNavigate = bert + self?.typeForNavigate = type + } } @@ -138,10 +146,12 @@ final class NotificationManager { switch type { case .message: if let vc = self.getMessageVC { - DispatchQueue.main.async { - if self.isFromPush { - vc.navigationController?.popToRootViewController(animated: false) + dispatchAsyncMain { [weak self] in + guard let `self` = self, self.isFromPush else { + return } + + vc.navigationController?.popToRootViewController(animated: false) } } @@ -154,7 +164,6 @@ final class NotificationManager { navigateToHistory(presenter: presenter) case .friend(let requestType): navigateToFriend(presenter: presenter, requestType: requestType) - default: break } } @@ -205,7 +214,10 @@ final class NotificationManager { self.showNotificationView(view: view) } else { - presenter.wireFrame.showHistory() + let time = withDelay ? DispatchTime.now() + 0.3 : DispatchTime.now() + DispatchQueue.main.asyncAfter(deadline: time) { + presenter.wireFrame.showHistory() + } clean() } } @@ -261,32 +273,29 @@ final class NotificationManager { } private func getContainer(isFromPush: Bool) -> Container? { - guard let message = getMessage(), message.messageStatus != .delete else { + guard let message = getMessage(), + message.messageStatus != .delete, + let phoneId = StorageService.sharedInstance.phoneId else { return nil } let sender = message.from - guard let id = StorageService.sharedInstance.rosterId, let roster = RosterDAO.findRosterBy(id: id) else { return nil } - guard let contacts = roster.userlist else { return nil } - guard let myContactID = roster.myContact?.phone_id else { return nil } - let isCursorInOwn = message.isInOwnChat && message.isCursor - if sender == myContactID && !isCursorInOwn { + if sender == phoneId, !isCursorInOwn { return nil } - var senderContact = contacts.first { (contact) -> Bool in - return contact.phone_id == sender - } - + var senderContact = sender.flatMap { ContactDAO.findContactBy(phoneId: $0) } var room_s: Room? = nil - if let room = roster.roomlist?.first(where: { $0.id == message.to }), - let member = room.allMembersWithoutFilter?.first(where: { $0.phone_id == StorageService.sharedInstance.phoneId }) { + if let room = message.to.flatMap({ RoomDAO.findRoom(by: $0) }), + let member = room.allMembersWithoutFilter?.first(where: { $0.phone_id == phoneId }) { + senderContact = nil room_s = room - if !member.shouldNotify(for: message) { + + guard member.shouldNotify(for: message) else { return nil } } @@ -359,9 +368,15 @@ final class NotificationManager { // MARK: - Show Notification View private func showNotificationView(view: NotificationView) { - previousView?.remove() - previousView = view - view.show(duration: 3) + dispatchAsyncMain { [weak self] in + guard let `self` = self else { + return + } + + self.previousView?.remove() + self.previousView = view + view.show(duration: 3) + } } // MARK: - Get Notification View @@ -430,7 +445,9 @@ final class NotificationManager { // MARK: - Clean func clean() { - self.typeForNavigate = nil - self.tupleForNavigate = nil + processingQueue.async { [weak self] in + self?.typeForNavigate = nil + self?.tupleForNavigate = nil + } } } diff --git a/Nynja/ProfileHandlerDelegate.swift b/Nynja/ProfileHandlerDelegate.swift new file mode 100644 index 0000000000000000000000000000000000000000..9fdd8ef6706afaea59e46a220816528a41c526f7 --- /dev/null +++ b/Nynja/ProfileHandlerDelegate.swift @@ -0,0 +1,11 @@ +// +// ProfileHandlerDelegate.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/14/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +protocol ProfileHandlerDelegate: class { + func profileDeleted(with phone: String) +} diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Contents.json b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Contents.json deleted file mode 100644 index 406236518a3691f14aec3dae5e95eb318a784dde..0000000000000000000000000000000000000000 --- a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Contents.json +++ /dev/null @@ -1,158 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "57x57", - "idiom" : "iphone", - "filename" : "Icon-App-57x57@1x.png", - "scale" : "1x" - }, - { - "size" : "57x57", - "idiom" : "iphone", - "filename" : "Icon-App-57x57@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "50x50", - "idiom" : "ipad", - "filename" : "Icon-Small-50x50@1x.png", - "scale" : "1x" - }, - { - "size" : "50x50", - "idiom" : "ipad", - "filename" : "Icon-Small-50x50@2x.png", - "scale" : "2x" - }, - { - "size" : "72x72", - "idiom" : "ipad", - "filename" : "Icon-App-72x72@1x.png", - "scale" : "1x" - }, - { - "size" : "72x72", - "idiom" : "ipad", - "filename" : "Icon-App-72x72@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "ItunesArtwork@2x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-20x20@1x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-20x20@1x.png deleted file mode 100644 index bb676f8c4bba57356bbeb0848322855fbdefa576..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-20x20@1x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-20x20@2x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-20x20@2x.png deleted file mode 100644 index beb24e4e21b20d7623fc2e98a3b2f2067e4b0737..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-20x20@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-20x20@3x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index ba5bf8699c8433a52d871b9fe9e1380a613abf53..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-20x20@3x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-29x29@1x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-29x29@1x.png deleted file mode 100644 index 8ee922c9d4a256126e532dfde177ba455d054ba4..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-29x29@1x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-29x29@2x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index 9c7c309b7f2406db4a4a48b8a9308213798f8fd3..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-29x29@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-29x29@3x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-29x29@3x.png deleted file mode 100644 index fc202f0686b7a1c916c1294a4b071eac31e105f2..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-29x29@3x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-40x40@1x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-40x40@1x.png deleted file mode 100644 index beb24e4e21b20d7623fc2e98a3b2f2067e4b0737..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-40x40@1x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-40x40@2x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index a8d751472cc9fea2a94a08c51eebe205e11a63ac..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-40x40@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-40x40@3x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index 2e68bfd69244aed1f418ad8da95e8451ed886350..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-40x40@3x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-57x57@1x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-57x57@1x.png deleted file mode 100644 index a053209e804005fe0da9ec6caf7a1f1ba057b685..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-57x57@1x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-57x57@2x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-57x57@2x.png deleted file mode 100644 index be6eb04b7a849fdae7a191125f0306e560f7b3dc..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-57x57@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-60x60@2x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index 2e68bfd69244aed1f418ad8da95e8451ed886350..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-60x60@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-60x60@3x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index 098a96998f645e5fedb7e8bd94cde276c18c3e11..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-60x60@3x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-72x72@1x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-72x72@1x.png deleted file mode 100644 index 0f2a4f9bfc185a564c57d5cf04494b70ca0d8197..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-72x72@1x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-72x72@2x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-72x72@2x.png deleted file mode 100644 index 336135dc0ffd907c1214528403ea0310af9a948d..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-72x72@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-76x76@1x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index 07fe4980249f70a9c266f492741049a9eac74ad8..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-76x76@1x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-76x76@2x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-76x76@2x.png deleted file mode 100644 index 9f7d35a2f2ca582085eb9ae21fa49419897ff444..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-76x76@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-83.5x83.5@2x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-83.5x83.5@2x.png deleted file mode 100644 index 2008709fa59ab71b591943718f074f3f5472f0c9..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-App-83.5x83.5@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-Small-50x50@1x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-Small-50x50@1x.png deleted file mode 100644 index 392fec50044c22bf63c6fe97073a74499b2271ef..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-Small-50x50@1x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-Small-50x50@2x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-Small-50x50@2x.png deleted file mode 100644 index a9a4d33d6d6b9fa76a5ab58137b63fae6ab941d7..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/Icon-Small-50x50@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/ItunesArtwork@2x.png b/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/ItunesArtwork@2x.png deleted file mode 100644 index b3974deb8782eb2991bbb6f659617dd36969b39e..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconChannels.appiconset/ItunesArtwork@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconSpotify.appiconset/Contents.json b/Nynja/Resources/Assets.xcassets/AppIconSpotify.appiconset/Contents.json index 191d433565a1b8bc451e6e434fa4dd0c062dbe12..406236518a3691f14aec3dae5e95eb318a784dde 100644 --- a/Nynja/Resources/Assets.xcassets/AppIconSpotify.appiconset/Contents.json +++ b/Nynja/Resources/Assets.xcassets/AppIconSpotify.appiconset/Contents.json @@ -149,30 +149,6 @@ "idiom" : "ios-marketing", "filename" : "ItunesArtwork@2x.png", "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "iphone", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@3x.png", - "scale" : "3x" } ], "info" : { diff --git a/Nynja/Resources/Assets.xcassets/AppIconSpotify.appiconset/Icon-App-60x60@1x.png b/Nynja/Resources/Assets.xcassets/AppIconSpotify.appiconset/Icon-App-60x60@1x.png deleted file mode 100644 index ba5bf8699c8433a52d871b9fe9e1380a613abf53..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconSpotify.appiconset/Icon-App-60x60@1x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconSpotify.appiconset/Icon-App-76x76@3x.png b/Nynja/Resources/Assets.xcassets/AppIconSpotify.appiconset/Icon-App-76x76@3x.png deleted file mode 100644 index ca372c421e2455ac4f7d9ada4a2dd634a7f81ddc..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconSpotify.appiconset/Icon-App-76x76@3x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Contents.json b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Contents.json deleted file mode 100755 index 406236518a3691f14aec3dae5e95eb318a784dde..0000000000000000000000000000000000000000 --- a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Contents.json +++ /dev/null @@ -1,158 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "57x57", - "idiom" : "iphone", - "filename" : "Icon-App-57x57@1x.png", - "scale" : "1x" - }, - { - "size" : "57x57", - "idiom" : "iphone", - "filename" : "Icon-App-57x57@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "50x50", - "idiom" : "ipad", - "filename" : "Icon-Small-50x50@1x.png", - "scale" : "1x" - }, - { - "size" : "50x50", - "idiom" : "ipad", - "filename" : "Icon-Small-50x50@2x.png", - "scale" : "2x" - }, - { - "size" : "72x72", - "idiom" : "ipad", - "filename" : "Icon-App-72x72@1x.png", - "scale" : "1x" - }, - { - "size" : "72x72", - "idiom" : "ipad", - "filename" : "Icon-App-72x72@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "ItunesArtwork@2x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-20x20@1x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-20x20@1x.png deleted file mode 100644 index bb676f8c4bba57356bbeb0848322855fbdefa576..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-20x20@1x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-20x20@2x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-20x20@2x.png deleted file mode 100644 index beb24e4e21b20d7623fc2e98a3b2f2067e4b0737..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-20x20@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-20x20@3x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index ba5bf8699c8433a52d871b9fe9e1380a613abf53..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-20x20@3x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-29x29@1x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-29x29@1x.png deleted file mode 100644 index 8ee922c9d4a256126e532dfde177ba455d054ba4..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-29x29@1x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-29x29@2x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index 9c7c309b7f2406db4a4a48b8a9308213798f8fd3..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-29x29@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-29x29@3x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-29x29@3x.png deleted file mode 100644 index fc202f0686b7a1c916c1294a4b071eac31e105f2..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-29x29@3x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-40x40@1x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-40x40@1x.png deleted file mode 100644 index beb24e4e21b20d7623fc2e98a3b2f2067e4b0737..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-40x40@1x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-40x40@2x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index a8d751472cc9fea2a94a08c51eebe205e11a63ac..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-40x40@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-40x40@3x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index 2e68bfd69244aed1f418ad8da95e8451ed886350..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-40x40@3x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-57x57@1x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-57x57@1x.png deleted file mode 100644 index a053209e804005fe0da9ec6caf7a1f1ba057b685..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-57x57@1x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-57x57@2x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-57x57@2x.png deleted file mode 100644 index be6eb04b7a849fdae7a191125f0306e560f7b3dc..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-57x57@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-60x60@2x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index 2e68bfd69244aed1f418ad8da95e8451ed886350..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-60x60@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-60x60@3x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index 098a96998f645e5fedb7e8bd94cde276c18c3e11..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-60x60@3x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-72x72@1x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-72x72@1x.png deleted file mode 100644 index 0f2a4f9bfc185a564c57d5cf04494b70ca0d8197..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-72x72@1x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-72x72@2x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-72x72@2x.png deleted file mode 100644 index 336135dc0ffd907c1214528403ea0310af9a948d..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-72x72@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-76x76@1x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index 07fe4980249f70a9c266f492741049a9eac74ad8..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-76x76@1x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-76x76@2x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-76x76@2x.png deleted file mode 100644 index 9f7d35a2f2ca582085eb9ae21fa49419897ff444..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-76x76@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-83.5x83.5@2x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-83.5x83.5@2x.png deleted file mode 100644 index 2008709fa59ab71b591943718f074f3f5472f0c9..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-App-83.5x83.5@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-Small-50x50@1x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-Small-50x50@1x.png deleted file mode 100644 index 392fec50044c22bf63c6fe97073a74499b2271ef..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-Small-50x50@1x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-Small-50x50@2x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-Small-50x50@2x.png deleted file mode 100644 index a9a4d33d6d6b9fa76a5ab58137b63fae6ab941d7..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/Icon-Small-50x50@2x.png and /dev/null differ diff --git a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/ItunesArtwork@2x.png b/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/ItunesArtwork@2x.png deleted file mode 100644 index b3974deb8782eb2991bbb6f659617dd36969b39e..0000000000000000000000000000000000000000 Binary files a/Nynja/Resources/Assets.xcassets/AppIconStickers.appiconset/ItunesArtwork@2x.png and /dev/null differ diff --git a/Nynja/Resources/Info.plist b/Nynja/Resources/Info.plist index dda23f3845685fd71c2ec1b987e5b0f5db50a1cf..d4fda774a3efb9dce590ab6ac2776c06b050e7a9 100644 --- a/Nynja/Resources/Info.plist +++ b/Nynja/Resources/Info.plist @@ -23,7 +23,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 0.5.4.Dev + 0.5.4.Load ConfServerAddress $(ConfServerAddress) ConfServerPort diff --git a/Nynja/Resources/LoadDBConfig.xcconfig b/Nynja/Resources/LoadDBConfig.xcconfig new file mode 100644 index 0000000000000000000000000000000000000000..7b7891380aaca508808afbf6d17043c8406f720e --- /dev/null +++ b/Nynja/Resources/LoadDBConfig.xcconfig @@ -0,0 +1,20 @@ +// +// LoadDBConfig.xcconfig +// Nynja +// +// Created by Anton Makarov on 10/24/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +BundleIdentifier = com.nynja.dev.mobile.communicator +ExtensionBundleIdentifier = com.nynja.dev.mobile.communicator.NynjaShare +ServerURL = loaddb.ci.nynja.net +AppName = NYNJALoad +ServerPort = 1883 +Config = dev +AppGroup = group.com.nynja.mobile.communicator.dev +ModelsVersion = 10 +isServerConnectionSecure = false +ConfServerAddress = 35.198.118.190 +ConfServerPort = 80 +ConfServerSecure = false diff --git a/Nynja/Resources/ReleaseConfig.xcconfig b/Nynja/Resources/ReleaseConfig.xcconfig index f8b763d10d53ab772501aae33538bbfb15346308..f65b25f2e8785121a37b607380cc213fcad8e8e6 100644 --- a/Nynja/Resources/ReleaseConfig.xcconfig +++ b/Nynja/Resources/ReleaseConfig.xcconfig @@ -14,7 +14,7 @@ AppName = NYNJA ServerPort = 8443 Config = release AppGroup = group.com.nynja.mobile.communicator -ModelsVersion = 9 +ModelsVersion = 10 isServerConnectionSecure = true ConfServerAddress = call.nynja.net ConfServerPort = 443 diff --git a/Nynja/Resources/ThirdPartyServices.swift b/Nynja/Resources/ThirdPartyServices.swift index 7bb8ffee29b93bfca549906912c96665dfa87d7f..e86091ba90a5c5c7494da62b043076a4696c4662 100644 --- a/Nynja/Resources/ThirdPartyServices.swift +++ b/Nynja/Resources/ThirdPartyServices.swift @@ -22,6 +22,7 @@ enum AppConfig: String { case devAutoTests case prerelease case release + case loadDB case spotify } @@ -46,7 +47,7 @@ struct AmazonService: ThirdPartyService { init(config: AppConfig) { switch config { - case .dev, .devAutoTests, .spotify: + case .dev, .devAutoTests, .loadDB, .spotify: serviceConfig = Config(accessKey: "AKIAIVFYOPZSACBLBBSA", secretKey: "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", defaultBucketName: "nynja-defaults", @@ -75,7 +76,7 @@ struct GoogleService: ThirdPartyService { init(config: AppConfig) { switch config { - case .dev, .devAutoTests, .spotify: serviceConfig = Config(apiKey: "AIzaSyCAi5Ea_zkYzbIARCqfMMrq4NOy935BltA") + case .dev, .devAutoTests, .loadDB, .spotify: serviceConfig = Config(apiKey: "AIzaSyCAi5Ea_zkYzbIARCqfMMrq4NOy935BltA") case .prerelease: serviceConfig = Config(apiKey: "AIzaSyCAi5Ea_zkYzbIARCqfMMrq4NOy935BltA") case .release: serviceConfig = Config(apiKey: "AIzaSyCAi5Ea_zkYzbIARCqfMMrq4NOy935BltA") } @@ -92,7 +93,7 @@ struct IntercomService: ThirdPartyService { init(config: AppConfig) { switch config { - case .dev, .devAutoTests, .spotify: serviceConfig = Config(apiKey: "ios_sdk-3f0f8a4f52e4ed08a2bf6f1a39a1e9eb8b0763d5", appId: "s3isdm0n") + case .dev, .devAutoTests, .loadDB, .spotify: serviceConfig = Config(apiKey: "ios_sdk-3f0f8a4f52e4ed08a2bf6f1a39a1e9eb8b0763d5", appId: "s3isdm0n") case .prerelease: serviceConfig = Config(apiKey: "ios_sdk-3f0f8a4f52e4ed08a2bf6f1a39a1e9eb8b0763d5", appId: "s3isdm0n") case .release: serviceConfig = Config(apiKey: "ios_sdk-3f0f8a4f52e4ed08a2bf6f1a39a1e9eb8b0763d5", appId: "s3isdm0n") } @@ -112,7 +113,7 @@ struct SupportService: ThirdPartyService { init(config: AppConfig) { switch config { - case .dev, .devAutoTests, .spotify: serviceConfig = Config(mailAddress: "support@nynja.biz", + case .dev, .devAutoTests, .loadDB, .spotify: serviceConfig = Config(mailAddress: "support@nynja.biz", faq: URL(string: "https://landing.nynja.io/-temporary-slug-81be145c-f4aa-4787-8d71-b3ab51a1aef2?hs_preview=XOrlQzBx-6108791186")!, privacyPolicy: URL(string: "https://landing.nynja.io/privacy-policy")!, terms: URL(string:"https://landing.nynja.io/terms-of-use")!) @@ -139,7 +140,7 @@ struct TestFairyService: ThirdPartyService { init(config: AppConfig) { switch config { - case .dev, .devAutoTests, .spotify: serviceConfig = Config(key: "4e58695a5ce5ee4ccddbb4b852d3927626f7da36") + case .dev, .devAutoTests, .loadDB, .spotify: serviceConfig = Config(key: "4e58695a5ce5ee4ccddbb4b852d3927626f7da36") case .prerelease: serviceConfig = Config(key: "4e58695a5ce5ee4ccddbb4b852d3927626f7da36") case .release: serviceConfig = Config(key: "4e58695a5ce5ee4ccddbb4b852d3927626f7da36") } diff --git a/Nynja/RoomDAO.swift b/Nynja/RoomDAO.swift index 7ceca3d9fba26cd75333584b3eeaf5e62c1873c7..6a4612ff9ff720e9c14310a64794ca2cf5d5a918 100644 --- a/Nynja/RoomDAO.swift +++ b/Nynja/RoomDAO.swift @@ -16,6 +16,7 @@ class RoomDAO: RoomDAOProtocol { // MARK: - Fetch // MARK: -- Room + static func fetchRoom(by rowId: Int64) -> DBRoom? { return dbManager.fetch { db in return try DBRoom.room(from: db, rowId: rowId) @@ -33,37 +34,24 @@ class RoomDAO: RoomDAOProtocol { return Room(room: dbRoom) } - // MARK: -- Rooms - static func fetchRooms(kind: Room.Kind? = nil) -> [Room] { - guard let rosterId = StorageService.sharedInstance.rosterId else { - return [] - } - - let rooms = dbManager.fetch { db -> [DBRoom] in - return try DBRoom.rooms(db, rosterId: rosterId, type: kind?.rawValue) - } - - return rooms.map { Room(room: $0) } - } + // MARK: -- Rooms - static func fetchRooms(with ids: [String], kind: Room.Kind? = nil) -> [Room] { - let rooms = dbManager.fetch { db -> [DBRoom] in + static func fetchPlainRooms(with ids: [String], kind: Room.Kind? = nil) -> [Room] { + return dbManager.fetch { db -> [DBRoom] in return try DBRoom.rooms(db, ids: ids, type: kind?.rawValue) - } - - return rooms.map { Room(room: $0) } + }.serverModels } - static func fetchUserRooms(with contactId: String, isAdmin: Bool, kind: Room.Kind?) -> [Room] { - let rooms = dbManager.fetch { db in - return try DBRoom.rooms(db, contactId: contactId, isAdmin: isAdmin, type: kind?.rawValue) - } - - return rooms.map { Room(room: $0) } + static func fetchPlainRooms(with args: DBRoom.RequestArgs) -> [Room] { + return dbManager.fetch { db in + return try DBRoom.rooms(db, args: args) + }.serverModels } + // MARK: -- Reader + static func fetchReader(for roomId: String, kind: ReaderKind) -> Int64? { if kind == .other { return dbManager.fetch { db in diff --git a/Nynja/RoomDAOProtocol.swift b/Nynja/RoomDAOProtocol.swift index 8256c6e3b0d38c342fd3f49fea32f77ccc7c2107..15da8a2214d3e6488c02ec8baafa04ac42b30e54 100644 --- a/Nynja/RoomDAOProtocol.swift +++ b/Nynja/RoomDAOProtocol.swift @@ -10,30 +10,39 @@ protocol RoomDAOProtocol: DAOProtocol { // MARK: - Fetch // MARK: -- Room + static func fetchRoom(by rowId: Int64) -> DBRoom? static func fetchRoom(by id: String) -> DBRoom? static func findRoom(by id: String) -> Room? + // MARK: -- Rooms - static func fetchRooms(kind: Room.Kind?) -> [Room] - static func fetchRooms(with ids: [String], kind: Room.Kind?) -> [Room] - static func fetchUserRooms(with contactId: String, isAdmin: Bool, kind: Room.Kind?) -> [Room] + + static func fetchPlainRooms(with ids: [String], kind: Room.Kind?) -> [Room] + static func fetchPlainRooms(with args: DBRoom.RequestArgs) -> [Room] + // MARK: -- Reader + static func fetchReader(for roomId: String, kind: ReaderKind) -> Int64? + // MARK: - Update // MARK: -- Room + static func patchRoom(_ room: Room) static func updateColumns(_ columns: Set, room: Room) + // MARK: -- Fields + static func updateReader(_ reader: Int64, roomId: String, phoneId: String, kind: ReaderKind) static func updatedMentions(with message: Message, roomId: String) -> UpdateResult<[MessageServerId]> static func isCurrentUserMentioned(in message: Message, phoneId: String) -> Bool + // MARK: - Contains Members - static func containsMembers(with ids: [Int64], roomId: String) -> Bool? + static func containsMembers(with ids: [Int64], roomId: String) -> Bool? } diff --git a/Nynja/RosterDAO.swift b/Nynja/RosterDAO.swift index 514e7ecc58bcbb589c35ba4812b0025680a02268..60dea23223044f20b461ce882dfab4cfae5e8b40 100644 --- a/Nynja/RosterDAO.swift +++ b/Nynja/RosterDAO.swift @@ -16,7 +16,7 @@ class RosterDAO: RosterDAOProtocol { return Roster(roster: roster) } - static var currentDBRoster: DBRoster? { + private static var currentDBRoster: DBRoster? { guard let id = StorageService.sharedInstance.rosterId else { return nil } return fetchRosterBy(id: id) } @@ -37,5 +37,4 @@ class RosterDAO: RosterDAOProtocol { guard let roster = fetchRosterBy(id: id) else { return nil } return Roster(roster: roster) } - } diff --git a/Nynja/RosterDAOProtocol.swift b/Nynja/RosterDAOProtocol.swift index f3d36e82422b564d962faa3e3d278f9c2e9c3f2f..6615f93cc7e8e4ff0fb3475bce22fe5619db6218 100644 --- a/Nynja/RosterDAOProtocol.swift +++ b/Nynja/RosterDAOProtocol.swift @@ -10,11 +10,9 @@ protocol RosterDAOProtocol: DAOProtocol { // MARK: - Fetch static var currentRoster: Roster? { get } - static var currentDBRoster: DBRoster? { get } static func fetchRosterBy(rowId: Int64) -> DBRoster? static func fetchRosterBy(id: Int64) -> DBRoster? static func findRosterBy(id: Int64) -> Roster? - } diff --git a/Nynja/ServerModel/SercerModelConvertible.swift b/Nynja/ServerModel/SercerModelConvertible.swift new file mode 100644 index 0000000000000000000000000000000000000000..99c695c191499d8cb0324dbdd3cc502faf50f01e --- /dev/null +++ b/Nynja/ServerModel/SercerModelConvertible.swift @@ -0,0 +1,19 @@ +// +// SercerModelConvertible.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/7/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +protocol ServerModelConvertible { + associatedtype ServerModel + var serverModel: ServerModel { get } +} + +extension Array where Element: ServerModelConvertible { + + var serverModels: [Element.ServerModel] { + return self.map { $0.serverModel } + } +} diff --git a/Nynja/Services/Amazon+FileSync.swift b/Nynja/Services/Amazon+FileSync.swift index 5ccd350c3188f0b9f1076cd3052b1580b7bf35a0..c76edb8c5202796ff4a6956118ef32f76e365374 100644 --- a/Nynja/Services/Amazon+FileSync.swift +++ b/Nynja/Services/Amazon+FileSync.swift @@ -10,7 +10,7 @@ import Foundation import AWSCore import AWSS3 -extension AmazonManager: FileNetworkProtocol { +extension AmazonManager: FileDownloader { func download(url: String, from destination: RemoteStorageDestination, result: ((_ result: String?, _ transferInfo: TransferInfo?) -> ())?) -> AWSRequest? { diff --git a/Nynja/Services/Aps.swift b/Nynja/Services/Aps.swift index 371112e55b17bb06f1a7a305feeb43a639955230..dc39a64b1f61552d0a54f829448fb31f819a22dd 100644 --- a/Nynja/Services/Aps.swift +++ b/Nynja/Services/Aps.swift @@ -56,7 +56,7 @@ struct Aps { self.contact = contact self.room = room - self.isValidIp = (nyn.dns == MQTTService.sharedInstance.host.url) + self.isValidIp = (nyn.dns == Host().url) self.isValidVersion = (NynjaPush.validVersion(version: nyn.version)) self.shouldNotify = NynjaPush.shouldNotify(room: room, contact: contact) @@ -238,7 +238,7 @@ extension Aps { static func validVersion(version: String) -> Bool { guard let versionString = version.components(separatedBy: "/").last, let version = Int(versionString) else { return false } - return version == MQTTService.version + return version == Bundle.main.modelsVersion } static func getContact(model: String) -> Contact? { diff --git a/Nynja/Services/Audio/AudioSessionManager/AudioSessionManager.swift b/Nynja/Services/Audio/AudioSessionManager/AudioSessionManager.swift index 3ec40b6522dcc33b60ac95d33e97aa4ee7d639d8..bf34a7ce33fd5ea9644b2e52a52caf227e4975c6 100644 --- a/Nynja/Services/Audio/AudioSessionManager/AudioSessionManager.swift +++ b/Nynja/Services/Audio/AudioSessionManager/AudioSessionManager.swift @@ -172,7 +172,7 @@ final class AudioSessionManager { LogService.log(topic: .audioSystem) { return "audioSessionRouteChange reason oldDeviceUnavailable" } case .categoryChange: LogService.log(topic: .audioSystem) { return "audioSessionRouteChange reason categoryChange" } - LogService.log(topic: .audioSystem) { return "AVAudioSession Category: \(session.category)" } + LogService.log(topic: .audioSystem) {[weak self] in return "AVAudioSession Category: \(self?.session.category)" } case .override: LogService.log(topic: .audioSystem) { return "audioSessionRouteChange reason override" } case .wakeFromSleep: diff --git a/Nynja/Services/Audio/SystemSoundManager/SystemSoundManager.swift b/Nynja/Services/Audio/SystemSoundManager/SystemSoundManager.swift index 8291eea75f59f234ef2ffc39823808bc987bb448..bb06789be99093d737ca7768d199d84d3003f3e7 100644 --- a/Nynja/Services/Audio/SystemSoundManager/SystemSoundManager.swift +++ b/Nynja/Services/Audio/SystemSoundManager/SystemSoundManager.swift @@ -47,11 +47,11 @@ final class SystemSoundManager { // MARK: - Play Sound func playSound(with url: URL) { - guard canPlaySound(with: url) else { - return - } - - dispatchAsyncMain { + dispatchAsyncMain { [unowned self] in + guard self.canPlaySound(with: url) else { + return + } + let soundId = self.makeSoundId(with: url) self.cachedSounds[url] = SoundInfo(soundId: soundId, lastTimeSoundPlayed: CFAbsoluteTimeGetCurrent()) AudioServicesPlaySystemSound(soundId) diff --git a/Nynja/Services/Debug/LogService/LogService/LogService.swift b/Nynja/Services/Debug/LogService/LogService/LogService.swift index 68538a764dafde965a501f491c91fb02d34d31e0..375961628225c84e69cf0f1251bcf2335e336093 100644 --- a/Nynja/Services/Debug/LogService/LogService/LogService.swift +++ b/Nynja/Services/Debug/LogService/LogService/LogService.swift @@ -9,22 +9,28 @@ import Foundation import os.log -class LogService { +final class LogService { - static var enabledTopics: [LogServiceTopic] = [.userDefaults, .wallet, .keychain, .fileSystem, - .audioSystem, .MQTT, .amazon, .callSystem, .locationSystem, + private static let logWriter: LogWriterProtocol? = ServiceFactory().makeLogWriter() + + private static var enabledTopics: [LogServiceTopic] = [.userDefaults, .wallet, .keychain, .fileSystem, + .audioSystem, .MQTT, .db, .amazon, .callSystem, .locationSystem, .system, .network, .galery, .videoConverter, .QRCode, - .passphrase, .arc, .connectionState] + .passphrase, .connectionState] + + private static let backgroundQueue = DispatchQueue(label:"logQueue",qos:.background) - static func log(topic: LogServiceTopic, block: () -> String) { + static func log(topic: LogServiceTopic, block: (() -> String)?) { #if !RELEASE + backgroundQueue.async { if !enabledTopics.contains(topic) { return } - LogService.executeLogs(topic: topic, text: block(), thread: Thread.current.debugDescription) + LogService.executeLogs(topic: topic, text: block?() ?? "", thread: Thread.current.debugDescription) + } #endif } private static func executeLogs(topic: LogServiceTopic, text: String, thread: String) { - LogWriter.shared?.writeLog(topic: topic.rawValue, description: text, thread: thread) + logWriter?.writeLog(topic: topic.rawValue, description: text, thread: thread) printToLog(topic: topic, text: text, thread: thread) } diff --git a/Nynja/Services/Debug/LogService/LogService/LogServiceProtocol.swift b/Nynja/Services/Debug/LogService/LogService/LogServiceProtocol.swift index 5f6c98b232e5efef35f71955b131f85043a5cb31..94822e1a55ea2000a896182971d64746dcb3db98 100644 --- a/Nynja/Services/Debug/LogService/LogService/LogServiceProtocol.swift +++ b/Nynja/Services/Debug/LogService/LogService/LogServiceProtocol.swift @@ -10,3 +10,4 @@ protocol LogServiceProtocol { static var enabledTopics: [LogServiceTopic] { get } static func log(topic: LogServiceTopic, block: () -> String) } + diff --git a/Nynja/Services/Debug/LogService/LogService/LogServiceStub.swift b/Nynja/Services/Debug/LogService/LogService/LogServiceStub.swift deleted file mode 100644 index a2155916c99dce700a0241f59ebd549d71b2916a..0000000000000000000000000000000000000000 --- a/Nynja/Services/Debug/LogService/LogService/LogServiceStub.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// LogServiceStub.swift -// Nynja -// -// Created by Volodymyr Hryhoriev on 10/2/18. -// Copyright © 2018 TecSynt Solutions. All rights reserved. -// - -/// In order to fix errors in test targets -class LogService: LogServiceProtocol { - static let enabledTopics: [LogServiceTopic] = [] - static func log(topic: LogServiceTopic, block: () -> String) {} -} diff --git a/Nynja/Services/Debug/LogService/LogService/LogServiceTopic.swift b/Nynja/Services/Debug/LogService/LogService/LogServiceTopic.swift index 96e71c63739af9004f99b6800f5a24fb382747d6..550f7724b6a39a62c70eb6141c94798826e38157 100644 --- a/Nynja/Services/Debug/LogService/LogService/LogServiceTopic.swift +++ b/Nynja/Services/Debug/LogService/LogService/LogServiceTopic.swift @@ -24,13 +24,12 @@ enum LogServiceTopic: String { case passphrase = "Passphrase" case push = "Push notification" case userDefaults = "User Defaults" - case arc = "ARC" case connectionState = "Connection State" static let allValues: [LogServiceTopic] = [.userDefaults, .wallet, .keychain, .fileSystem, .db, .audioSystem, .MQTT, .amazon, .callSystem, .locationSystem, .system, .network, .galery, .videoConverter, .QRCode, - .passphrase, .arc, .connectionState] + .passphrase, .connectionState] static var allValuesStrings: String { return allValues.reduce("") { (result, topic) -> String in diff --git a/Nynja/Services/Debug/LogService/LogService/LogWriterProtocol.swift b/Nynja/Services/Debug/LogService/LogService/LogWriterProtocol.swift new file mode 100644 index 0000000000000000000000000000000000000000..d496f9b351ebe93901c02212779ab9b018bfe2ec --- /dev/null +++ b/Nynja/Services/Debug/LogService/LogService/LogWriterProtocol.swift @@ -0,0 +1,13 @@ +// +// LogWriterProtocol.swift +// Nynja +// +// Created by Anton Makarov on 10/25/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +protocol LogWriterProtocol: class { + func writeLog(topic: String, description: String, thread: String) +} diff --git a/Nynja/Services/Debug/LogService/LogWriter.swift b/Nynja/Services/Debug/LogService/LogWriter.swift index 1a1453996317c2eddf70009f308f09ba730dcfd6..01f2ce91e57531817b1c4ad3d20eddd3ecca0b5d 100644 --- a/Nynja/Services/Debug/LogService/LogWriter.swift +++ b/Nynja/Services/Debug/LogService/LogWriter.swift @@ -8,7 +8,7 @@ import Foundation -class LogWriter { +final class LogWriter: LogWriterProtocol { static let shared: LogWriter? = LogWriter() diff --git a/Nynja/Services/HandleServices/ContactHandler.swift b/Nynja/Services/HandleServices/ContactHandler.swift index 8a6aa1cfee40e159df9bea6d0fac0f7605583454..42e1e46bc286f04702f8a5b1a22cd95693633b22 100644 --- a/Nynja/Services/HandleServices/ContactHandler.swift +++ b/Nynja/Services/HandleServices/ContactHandler.swift @@ -6,16 +6,18 @@ // Copyright © 2017 TecSynt Solutions. All rights reserved. // -import Foundation - -class ContactHandler: BaseHandler { +final class ContactHandler: BaseHandler { // MARK: - Dependencies - static var storageService: StorageService { + private static var storageService: StorageService { return .sharedInstance } + private static var notificationManager: NotificationManager { + return .shared + } + // MARK: - Handler @@ -79,7 +81,7 @@ class ContactHandler: BaseHandler { if [.request, .authorization, .ignore].contains(prevStatus) { let friendRequestType: InAppNotificationType.FriendRequestType = prevStatus == .request ? .outcoming : .incoming - NotificationManager.shared.handle(bert: data, + notificationManager.handle(bert: data, type: .friend(friendRequestType) ) } } catch { @@ -90,7 +92,7 @@ class ContactHandler: BaseHandler { private static func handleAuthorization(_ contact: Contact, data: BertTuple) { do { try storageService.perform(action: .save, with: contact) - NotificationManager.shared.handle(bert: data, type: .request) + notificationManager.handle(bert: data, type: .request) } catch { LogService.log(topic: .db) { return "Storage Service Error: can't save contact with status 'authorization'" } } diff --git a/Nynja/Services/HandleServices/HistoryHandler.swift b/Nynja/Services/HandleServices/HistoryHandler.swift index 11d6bf8246d6aeeb6d5450abd9f793e983d53fb8..4a7180294520a8097d7df1f66750aaeb067717c0 100644 --- a/Nynja/Services/HandleServices/HistoryHandler.swift +++ b/Nynja/Services/HandleServices/HistoryHandler.swift @@ -8,18 +8,6 @@ import Foundation -protocol HistoryHandlerDelegate: class { - func getHistorySuccess() - func getJobsHistorySuccess() - func getStickerPacksSuccess() -} - -extension HistoryHandlerDelegate { - func getHistorySuccess() {} - func getJobsHistorySuccess() {} - func getStickerPacksSuccess() {} -} - final class HistoryHandler: BaseHandler { // MARK: - Subscribers @@ -28,13 +16,13 @@ final class HistoryHandler: BaseHandler { private static var subscribers = [WeakRef]() - private static func notify(block: (HistoryHandlerDelegate) -> Void) { + private static func notify(block: (HistoryHandlerSubscriber) -> Void) { subscribersLock.lock() - subscribers.forEach { ($0.value as? HistoryHandlerDelegate).map { block($0) } } + subscribers.forEach { ($0.value as? HistoryHandlerSubscriber).map { block($0) } } subscribersLock.unlock() } - static func addSubscriber(_ subscriber: HistoryHandlerDelegate) { + static func addSubscriber(_ subscriber: HistoryHandlerSubscriber) { subscribersLock.lock() defer { subscribersLock.unlock() } @@ -45,7 +33,7 @@ final class HistoryHandler: BaseHandler { subscribers.append(ref) } - static func removeSubscriber(_ subscriber: HistoryHandlerDelegate) { + static func removeSubscriber(_ subscriber: HistoryHandlerSubscriber) { subscribersLock.lock() subscribers = subscribers.filter { $0.value != nil && $0.value !== subscriber } subscribersLock.unlock() @@ -54,15 +42,15 @@ final class HistoryHandler: BaseHandler { // MARK: - Dependencies - static var storageService: StorageService { + private static var storageService: StorageService { return StorageService.sharedInstance } - static var messageEditService: MessageEditServiceProtocol { + private static var messageEditService: MessageEditServiceProtocol { return MessageEditService(dependencies: .init(storageService: storageService)) } - static let stickersDownloadingService: StickersDownloadingService = { + private static let stickersDownloadingService: StickersDownloadingService = { return StickersDownloadingService() }() diff --git a/Nynja/Services/HandleServices/MessageHandlerSubscriber.swift b/Nynja/Services/HandleServices/MessageHandlerSubscriber.swift index 35f8bf9ef5aef7bf8aa057718292095f2fd44f14..08e51eb721215fe9428b3c258817fbef29ce12d0 100644 --- a/Nynja/Services/HandleServices/MessageHandlerSubscriber.swift +++ b/Nynja/Services/HandleServices/MessageHandlerSubscriber.swift @@ -10,6 +10,7 @@ protocol MessageHandlerSubscriber: class { func willSave(_ message: Message) func didSave(_ message: Message) } + extension MessageHandlerSubscriber { func willSave(_ message: Message) { } func didSave(_ message: Message) { } diff --git a/Nynja/Services/HandleServices/ProfileHandler.swift b/Nynja/Services/HandleServices/ProfileHandler.swift index d2d68a05cd793a71650300a0c934c2a3ae184792..88d1508715738346662fded765d1f189190be8f1 100644 --- a/Nynja/Services/HandleServices/ProfileHandler.swift +++ b/Nynja/Services/HandleServices/ProfileHandler.swift @@ -6,30 +6,31 @@ // Copyright © 2017 TecSynt Solutions. All rights reserved. // -class ProfileHandler: BaseHandler { +final class ProfileHandler: BaseHandler, StaticDelegating { + + static weak var delegate: ProfileHandlerDelegate? // MARK: - Dependencies - static var mqttService: MQTTService { - return MQTTService.sharedInstance + private static var mqttService: MQTTService { + return .sharedInstance } - static var historyFactory: HistoryRequestModelFactoryProtocol { + private static var historyFactory: HistoryRequestModelFactoryProtocol { return HistoryRequestModelFactory() } - static var storageService: StorageService { - return StorageService.sharedInstance + private static var storageService: StorageService { + return .sharedInstance } - static var messageBackgroundTaskHandler: BackgroundTaskHandler { + private static var messageBackgroundTaskHandler: BackgroundTaskHandler { return MessageBackgroundTaskHandler() } - - static var alertManager: AlertManager { - return AlertManager.sharedInstance - } + private static var communicatorService: NynjaCommunicatorService { + return .sharedInstance + } // MARK: - Handler @@ -74,7 +75,7 @@ class ProfileHandler: BaseHandler { return } configureTestFairy(with: roster) - configureNynjaCommunicatorService(profile) + configureNynjaCommunicatorService(with: roster) requestJobs(with: phoneId) requestStickerPacks(with: phoneId) @@ -147,15 +148,18 @@ class ProfileHandler: BaseHandler { } private static func configureTestFairy(with roster: Roster) { - TestFairy.setUserId("\(roster.myContact?.phone_id ?? "")_\(roster.myContact?.fullName ?? "")") + let phoneId = roster.phoneId ?? "" + let fullName = roster.fullName ?? "" + let userId = "\(phoneId)_\(fullName)" + TestFairy.setUserId(userId) } - private static func configureNynjaCommunicatorService(_ profile: Profile) { - guard let rosterId = (profile.rosters?.first as? Roster)?.myContact?.phone_id else { + private static func configureNynjaCommunicatorService(with roster: Roster) { + guard roster.phoneId != nil else { return } - NynjaCommunicatorService.sharedInstance.initialize() + communicatorService.initialize() } private static func requestJobs(with phoneId: String) { @@ -176,13 +180,14 @@ class ProfileHandler: BaseHandler { } } - + // MARK: Remove private static func handleRemove(_ profile: Profile) { - try? storageService.perform(action: .delete, with: profile) - storageService.phone = nil - alertManager.showAlertOk(message: String.localizable.authAttemptsRemoved) - mqttService.reconnect() + guard let phone = profile.phone else { + return + } + + delegate { $0.profileDeleted(with: phone) } } } diff --git a/Nynja/Services/HandleServices/RoomHandler.swift b/Nynja/Services/HandleServices/RoomHandler.swift index 6193bb93165b4f1981a579924b9920c7061b479d..a29be4c96b26654494420b2fb87d193631c184b2 100644 --- a/Nynja/Services/HandleServices/RoomHandler.swift +++ b/Nynja/Services/HandleServices/RoomHandler.swift @@ -6,15 +6,15 @@ // Copyright © 2017 TecSynt Solutions. All rights reserved. // -class RoomHandler: BaseHandler { +final class RoomHandler: BaseHandler { // MARK: - Dependencies - static var storageService: StorageService { + private static var storageService: StorageService { return .sharedInstance } - static var notificationManager: NotificationManager { + private static var notificationManager: NotificationManager { return .shared } @@ -205,7 +205,7 @@ class RoomHandler: BaseHandler { oldRoom.readers = room.readers oldRoom.originalStatus = .get - oldRoom.unread = 0 + oldRoom.unread = 0 // TODO: need to check. try? storageService.perform(action: .save, with: oldRoom) } } @@ -228,5 +228,4 @@ class RoomHandler: BaseHandler { oldRoom.readers = room.readers oldRoom.status = room.status } - } diff --git a/Nynja/Services/HandleServices/RosterHandler.swift b/Nynja/Services/HandleServices/RosterHandler.swift index 419d89e79e2b9b98e44842f63957df0f916714d3..2596cd69d26737090de3058ebcc3ed5c001090bd 100644 --- a/Nynja/Services/HandleServices/RosterHandler.swift +++ b/Nynja/Services/HandleServices/RosterHandler.swift @@ -6,9 +6,11 @@ // Copyright © 2017 TecSynt Solutions. All rights reserved. // -import Foundation - -class RosterHandler: BaseHandler { +final class RosterHandler: BaseHandler { + + private static var storageService: StorageService { + return .sharedInstance + } static func executeHandle(data: BertTuple) { guard let roster = get_Roster().parse(bert: data) as? Roster, @@ -17,12 +19,7 @@ class RosterHandler: BaseHandler { } if status == "patch" || status == "nick" { - if let currentRoster = RosterDAO.currentRoster { - roster.favorite = currentRoster.favorite - } - - try? StorageService.sharedInstance.perform(action: .save, with: roster) + try? storageService.perform(action: .save, with: roster) } } - } diff --git a/Nynja/Services/HandleServices/SearchHandler.swift b/Nynja/Services/HandleServices/SearchHandler.swift deleted file mode 100644 index 9d8570b15de66e722bcc946a1585f1b35d51705d..0000000000000000000000000000000000000000 --- a/Nynja/Services/HandleServices/SearchHandler.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// SearchHandler.swift -// Nynja -// -// Created by Anton Makarov on 23.10.17. -// Copyright © 2017 TecSynt Solutions. All rights reserved. -// - -import Foundation - -class SearchHandler: BaseHandler { - - static func executeHandle(data: BertTuple) { - guard let search = get_Search().parse(bert: data) as? Search, - let ref = search.ref, - let refType = SearchModelReference(rawValue: ref) else { - return - } - - switch refType { - case .PHONE: - break - case .PHONEBOOK: - break - case .QRCODE: - break - case .USERNAME: - break - } - } - -} diff --git a/Nynja/Services/HandleServices/StarHandler.swift b/Nynja/Services/HandleServices/StarHandler.swift index 41665196251603d483d7086d284b8761f1057901..1b830f2ad78939afaff056afeeb32639e4406562 100644 --- a/Nynja/Services/HandleServices/StarHandler.swift +++ b/Nynja/Services/HandleServices/StarHandler.swift @@ -6,20 +6,26 @@ // Copyright © 2018 TecSynt Solutions. All rights reserved. // -class StarHandler: BaseHandler { +final class StarHandler: BaseHandler { + + private static var storageService: StorageService { + return .sharedInstance + } static func executeHandle(data: BertTuple) { - guard let star = get_Star().parse(bert: data) as? Star, let status = star.starStatus else { - return + guard let star = get_Star().parse(bert: data) as? Star, + let status = star.starStatus else { + return } + do { switch status { case .add: if !StarActionDAO.containsDeleteAction(for: star) { - try StorageService.sharedInstance.perform(action: .save, with: star) + try storageService.perform(action: .save, with: star) } case .remove: - try StorageService.sharedInstance.perform(action: .delete, with: star) + try storageService.perform(action: .delete, with: star) } } catch { } } diff --git a/Nynja/Services/HandleServices/TypingHandler.swift b/Nynja/Services/HandleServices/TypingHandler.swift index e9023cba80c1b03a0e44589a87eb7567a1a43e88..ce19b3bb3e1215e366ea4d7d43aa6c4707d7ce80 100644 --- a/Nynja/Services/HandleServices/TypingHandler.swift +++ b/Nynja/Services/HandleServices/TypingHandler.swift @@ -8,18 +8,12 @@ import Foundation -protocol TypingHandlerDelegate: class { - func getTyping(typing: Typing) -} - -class TypingHandler: BaseHandler { - +final class TypingHandler: BaseHandler, StaticDelegating { static weak var delegate: TypingHandlerDelegate? static func executeHandle(data: BertTuple) { if let typing = get_Typing().parse(bert: data) as? Typing { - delegate?.getTyping(typing: typing) + delegate { $0.getTyping(typing: typing) } } } - } diff --git a/Nynja/Services/MQTT/MQTTServiceAuth.swift b/Nynja/Services/MQTT/API/MQTTServiceAuth.swift similarity index 86% rename from Nynja/Services/MQTT/MQTTServiceAuth.swift rename to Nynja/Services/MQTT/API/MQTTServiceAuth.swift index 30e1c90945a782ecaa173c455fbd7b110ca405f1..a495ab33a22596f65f305aa95ac2b94a78e65a99 100644 --- a/Nynja/Services/MQTT/MQTTServiceAuth.swift +++ b/Nynja/Services/MQTT/API/MQTTServiceAuth.swift @@ -11,8 +11,8 @@ import Foundation extension MQTTService { func logout() { - let token = StorageService.sharedInstance.tokenAsNSData - let userID = StorageService.sharedInstance.phone + let token = storageService.tokenData + let userID = storageService.phone let model = AuthModel(type: .LOGOUT, phoneNumber: nil, userID: userID, @@ -24,7 +24,7 @@ extension MQTTService { } func login(number: String) { - if let token = StorageService.sharedInstance.tokenAsNSData { + if let token = storageService.tokenData { let model = AuthModel(type: .LOGIN, phoneNumber: number, userID: nil, @@ -49,11 +49,11 @@ extension MQTTService { publish(model: model) } - func updatePushToken(push_token: String) { - if - let phone = StorageService.sharedInstance.phone, - let token = StorageService.sharedInstance.tokenAsNSData, - let clientID = StorageService.sharedInstance.clientId { + func update(pushToken: String) { + if let phone = storageService.phone, + let token = storageService.tokenData, + let clientID = storageService.clientId { + let model = AuthModel(type: .PUSH, phoneNumber: phone, userID: nil, @@ -63,17 +63,19 @@ extension MQTTService { token: token, smsCode: nil, languages: nil, - pushID: push_token) + pushID: pushToken) publish(model: model) } } func removeTokenAndLogin(number: String) { - StorageService.sharedInstance.dropUserInfo() + storageService.dropUserInfo() login(number: number) } func checkSMS(code: String, phone: String) { + let wasRunApp = storageService.wasRun + let model = AuthModel(type: .VERIFY, phoneNumber: phone, userID: nil, diff --git a/Nynja/Services/MQTT/MQTTServiceChat.swift b/Nynja/Services/MQTT/API/MQTTServiceChat.swift similarity index 96% rename from Nynja/Services/MQTT/MQTTServiceChat.swift rename to Nynja/Services/MQTT/API/MQTTServiceChat.swift index f1129894f0ee9ed998d63fcbe5675e95be6de4db..5fa321e5fa6ef1dd8cefe16f02837d0d642d49b3 100644 --- a/Nynja/Services/MQTT/MQTTServiceChat.swift +++ b/Nynja/Services/MQTT/API/MQTTServiceChat.swift @@ -168,7 +168,7 @@ extension MQTTService { func block(_ isBlock: Bool, to: String) { let friend = Friend() - friend.phone_id = StorageService.sharedInstance.phoneId + friend.phone_id = storageService.phoneId friend.friend_id = to friend.settings = [] friend.status = isBlock ? ("ban" as AnyObject) : ("unban" as AnyObject) @@ -187,7 +187,7 @@ extension MQTTService { func updateSettings(_ features: [Feature], to: String) { let friend = Friend() - friend.phone_id = StorageService.sharedInstance.phoneId + friend.phone_id = storageService.phoneId friend.friend_id = to friend.settings = features friend.status = "update" as AnyObject @@ -210,7 +210,7 @@ extension MQTTService { func ignore(_ contact: Contact) { let friend = Friend() - friend.phone_id = StorageService.sharedInstance.phoneId + friend.phone_id = storageService.phoneId friend.friend_id = contact.phoneId friend.settings = [] friend.status = ("ignore" as AnyObject) diff --git a/Nynja/Services/MQTT/MQTTServiceFriend.swift b/Nynja/Services/MQTT/API/MQTTServiceFriend.swift similarity index 79% rename from Nynja/Services/MQTT/MQTTServiceFriend.swift rename to Nynja/Services/MQTT/API/MQTTServiceFriend.swift index 7d67421c60636c351eb253c83549cfe07e733efe..29f24c2abb64c585b79ea8207650a2bb09306f07 100644 --- a/Nynja/Services/MQTT/MQTTServiceFriend.swift +++ b/Nynja/Services/MQTT/API/MQTTServiceFriend.swift @@ -11,7 +11,7 @@ import Foundation extension MQTTService { func searchContactByUsername(nick : String) { - guard let rosterId = StorageService.sharedInstance.rosterId else { + guard let rosterId = storageService.rosterId else { return } let model = SearchModel(rosterID: rosterId, numbers: [nick], reference: .USERNAME, type: .EQUALS, status: .CONTACT, field: "nick") @@ -19,7 +19,7 @@ extension MQTTService { } func tryFindContact(number: String, modelReference: SearchModelReference = .PHONE) { - guard let rosterId = StorageService.sharedInstance.rosterId else { + guard let rosterId = storageService.rosterId else { return } let model = SearchModel(rosterID: rosterId, numbers: [number], reference: modelReference, type: .EQUALS, status: .CONTACT) @@ -27,7 +27,7 @@ extension MQTTService { } func searchContacts(numbers: [String]) { - guard let rosterId = StorageService.sharedInstance.rosterId else { + guard let rosterId = storageService.rosterId else { return } let model = SearchModel(rosterID: rosterId, numbers: numbers, reference: .PHONEBOOK, type: .EQUALS, status: .CONTACT) @@ -35,7 +35,7 @@ extension MQTTService { } func friendRequest(friendPhoneId : String) { - guard let phoneId = StorageService.sharedInstance.phoneId else { + guard let phoneId = storageService.phoneId else { return } let model = FriendRequstModel(phoneId: phoneId, friendPhoneId: friendPhoneId, status: "request") @@ -43,7 +43,7 @@ extension MQTTService { } func confirmFriend(friendPhoneId : String) { - guard let phoneId = StorageService.sharedInstance.phoneId else { + guard let phoneId = storageService.phoneId else { return } let model = FriendRequstModel(phoneId: phoneId, friendPhoneId: friendPhoneId, status: "confirm") diff --git a/Nynja/Services/MQTT/MQTTServiceLink.swift b/Nynja/Services/MQTT/API/MQTTServiceLink.swift similarity index 100% rename from Nynja/Services/MQTT/MQTTServiceLink.swift rename to Nynja/Services/MQTT/API/MQTTServiceLink.swift diff --git a/Nynja/Services/MQTT/MQTTServiceProfile.swift b/Nynja/Services/MQTT/API/MQTTServiceProfile.swift similarity index 90% rename from Nynja/Services/MQTT/MQTTServiceProfile.swift rename to Nynja/Services/MQTT/API/MQTTServiceProfile.swift index 93221a3ae9af90d981e85ade211db748d7f76c58..b30cbb93aadfc6b607866d26a0252cd594497e0c 100644 --- a/Nynja/Services/MQTT/MQTTServiceProfile.swift +++ b/Nynja/Services/MQTT/API/MQTTServiceProfile.swift @@ -11,7 +11,7 @@ import Foundation extension MQTTService { func updateAccount(id: Int64, name: String, surname: String, phone: String) { - if let _ = StorageService.sharedInstance.token { + if let _ = storageService.token { var model:UpdateRosterModel! model = UpdateRosterModel(id: id, name: name, surname: nil, avatar: nil, email: nil,nick: nil) publish(model: model) @@ -19,7 +19,7 @@ extension MQTTService { } func updateAvatar(id: Int64, link: String) { - if let _ = StorageService.sharedInstance.token { + if let _ = storageService.token { let model = UpdateRosterModel(id: id, name: nil, surname: nil, avatar: link, email: nil,nick: nil) publish(model: model) } diff --git a/Nynja/Services/MQTT/MQTTServiceSchedule.swift b/Nynja/Services/MQTT/API/MQTTServiceSchedule.swift similarity index 100% rename from Nynja/Services/MQTT/MQTTServiceSchedule.swift rename to Nynja/Services/MQTT/API/MQTTServiceSchedule.swift diff --git a/Nynja/Services/MQTT/MQTTServiceStars.swift b/Nynja/Services/MQTT/API/MQTTServiceStars.swift similarity index 84% rename from Nynja/Services/MQTT/MQTTServiceStars.swift rename to Nynja/Services/MQTT/API/MQTTServiceStars.swift index 4c3bafde5175cf2c36a4e0e201e4dbbcee988c4a..c5cb5d2e58560db8b5ca14540bce458719df7f4a 100644 --- a/Nynja/Services/MQTT/MQTTServiceStars.swift +++ b/Nynja/Services/MQTT/API/MQTTServiceStars.swift @@ -10,7 +10,7 @@ import Foundation extension MQTTService { func getFavorites() { - guard StorageService.sharedInstance.token != nil else { + guard storageService.token != nil else { return } let model = GetExtendedStarsModel() diff --git a/Nynja/Services/MQTT/MQTTServiceWallet.swift b/Nynja/Services/MQTT/API/MQTTServiceWallet.swift similarity index 90% rename from Nynja/Services/MQTT/MQTTServiceWallet.swift rename to Nynja/Services/MQTT/API/MQTTServiceWallet.swift index fefee8208aedeac6446d2407a9cc4665310ed94c..600599ade396f2cb3e1940abc77acb52340d2ef5 100644 --- a/Nynja/Services/MQTT/MQTTServiceWallet.swift +++ b/Nynja/Services/MQTT/API/MQTTServiceWallet.swift @@ -9,7 +9,7 @@ extension MQTTService { func addWalletToProfile(walletAddress: String, balance: NYNMoney, password: String) { - guard let phoneId = StorageService.sharedInstance.phone else { + guard let phoneId = storageService.phone else { return } @@ -23,7 +23,7 @@ extension MQTTService { } func changeProfileBalance(walletAddress: String, balance: NYNMoney, password: String) { - guard let phoneId = StorageService.sharedInstance.phone else { + guard let phoneId = storageService.phone else { return } diff --git a/Nynja/Services/MQTT/Entities/Host.swift b/Nynja/Services/MQTT/Entities/Host.swift new file mode 100644 index 0000000000000000000000000000000000000000..1ce1d6f21a918902e326dd29c5cc687cc829afdc --- /dev/null +++ b/Nynja/Services/MQTT/Entities/Host.swift @@ -0,0 +1,19 @@ +// +// Host.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/9/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +struct Host { + let url: String + let port: UInt16 + + init() { + url = Bundle.main.serverUrl + port = Bundle.main.serverPort + } +} diff --git a/Nynja/Services/MQTT/MQTTServiceHelper.swift b/Nynja/Services/MQTT/Extensions/MQTTService+Helper.swift similarity index 70% rename from Nynja/Services/MQTT/MQTTServiceHelper.swift rename to Nynja/Services/MQTT/Extensions/MQTTService+Helper.swift index 41e228ac7aa8a5c3f8e63103c73e744f4a953898..052f202458fef5b816c8ed0c2247f985b5b585d6 100644 --- a/Nynja/Services/MQTT/MQTTServiceHelper.swift +++ b/Nynja/Services/MQTT/Extensions/MQTTService+Helper.swift @@ -1,5 +1,5 @@ // -// MQTTServiceHelper.swift +// MQTTService+Helper.swift // Nynja // // Created by Anton Makarov on 07.09.2017. @@ -11,24 +11,19 @@ import CocoaMQTT extension MQTTService { - func publish(model: BaseMQTTModel) { - guard let mqtt = self.mqtt, mqtt.connState == .connected else { - return - } - - let finalModel: CocoaMQTTMessage = model.getMessage() - mqtt.publish(finalModel) - } - func printMessage(msg: CocoaMQTTMessage, isSent: Bool) { - let desc = self.prepareOutputMessage(msg: msg, isSent: isSent) - guard let bert = desc.1 as? BertTuple else { - return + queuePool.loggingQueue.async { [weak self] in + guard let `self` = self else { return } + + let desc = self.prepareOutputMessage(msg: msg, isSent: isSent) + guard let bert = desc.1 as? BertTuple else { + return + } + + if self.shouldShowLog(for: bert) { + LogService.log(topic: .MQTT) { return desc.0 } + } } - - if shouldShowLog(for: bert) { - LogService.log(topic: .MQTT) { return desc.0 } - } } private func shouldShowLog(for bert: BertTuple) -> Bool { diff --git a/Nynja/Services/MQTT/Extensions/MQTTService+QueuePool.swift b/Nynja/Services/MQTT/Extensions/MQTTService+QueuePool.swift new file mode 100644 index 0000000000000000000000000000000000000000..077d7db174373c00e75ba95cd7780800ce43e692 --- /dev/null +++ b/Nynja/Services/MQTT/Extensions/MQTTService+QueuePool.swift @@ -0,0 +1,25 @@ +// +// MQTTService+QueuePool.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/9/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +extension MQTTService { + + struct QueuePool { + let subscribersQueue = DispatchQueue(label: String.label(withSuffix: "mqtt-service.subscribers-queue")) + let loggingQueue = DispatchQueue(label: String.label(withSuffix: "mqtt-service.logging-queue")) + let processingQueue = DispatchQueue( + label: String.label(withSuffix: "mqtt-service.processing-queue"), + qos: .utility) + let receivingQueue = DispatchQueue.main + // NOTE: can't use background queue because of undetermined crash. +// DispatchQueue( +// label: String.label(withSuffix: "mqtt-service.receiving-queue"), +// qos: .utility) + } +} diff --git a/Nynja/Services/MQTT/MQTTService.swift b/Nynja/Services/MQTT/MQTTService.swift index b3639a0f79a4ecd7bc13b08fd60e49cf29d70cc6..9272ea8511ee956ebd58fccfa913137d9477c39a 100644 --- a/Nynja/Services/MQTT/MQTTService.swift +++ b/Nynja/Services/MQTT/MQTTService.swift @@ -9,44 +9,21 @@ import Foundation import CocoaMQTT -struct Host { - let url: String - let port: UInt16 +/// Performs works in the several serial background queues +final class MQTTService: NSObject, MQTTServiceProtocol, CocoaMQTTDelegate, ConnectionServiceDelegate { - init() { - url = Bundle.main.serverUrl - port = Bundle.main.serverPort - } -} - -final class MQTTService: NSObject, CocoaMQTTDelegate, ConnectionServiceDelegate { + private var mqtt: CocoaMQTT? - enum ConnectionState { - case connected - case disconnected + let queuePool = QueuePool() + let showHandlers: Set = [] + + var deviceId: String { + return UIDevice.current.persistentIdentifier } - var mqtt: CocoaMQTT? - - static let version = Bundle.main.modelsVersion - - let host = Host() - - var push: String? - - let deviceId = UIDevice.current.persistentIdentifier - - let semaphore = DispatchSemaphore(value: 1) - - var wasRunApp: Bool = false - - typealias MQTTServiceSubscribers = [WeakRef] - private var subscribers = MQTTServiceSubscribers() - private let subscribersQueue = DispatchQueue(label: "com.nynja.mobile.communicator.mqttservice.subscribers") - - let showHandlers : Set = [] - - static let sharedInstance = MQTTService() + var storageService: StorageService { + return .sharedInstance + } private var autoReconnectTimeInterval: UInt16? { willSet { @@ -80,65 +57,38 @@ final class MQTTService: NSObject, CocoaMQTTDelegate, ConnectionServiceDelegate } } } - - // MARK: - Session State - private let userDefaults: UserDefaults = .standard + // MARK: - Subscribers - enum SessionState { - case notDetermined - case notAuthenticated(isLoggedOutFromServer: Bool) - - enum Keys { - static let state = "SessionState.AuthenticationState" - static let isLoggedOutFromServer = "SessionState.isLoggedOutFromServer" - } - } + typealias MQTTServiceSubscribers = [WeakRef] + private var subscribers = MQTTServiceSubscribers() - var state: SessionState = .notDetermined { - didSet { - switch state { - case .notDetermined: - break - case let .notAuthenticated(isLoggedOutFromServer): - let value: [String: Bool] = [ - SessionState.Keys.isLoggedOutFromServer: isLoggedOutFromServer - ] - userDefaults.set(value, forKey: SessionState.Keys.state) - } - } - } - var isAuthenticated: Bool { - return StorageService.sharedInstance.hasToken - } + // MARK: - Singeton + static let sharedInstance = MQTTService() - // MARK: - Init + private override init() {} - override init () { - super.init() - state = fetchCurrentState() - } - func initialize() { - self.setup() - myConnect() - } - - func myConnect() { - semaphore.wait() - mqtt?.connect() - semaphore.signal() + // MARK: - Setup + + func connect() { + queuePool.processingQueue.async { [unowned self] in + self.setup() + self.mqtt?.connect() + } } - func setup() { + private func setup() { // NOTE: Don't subscribe for ConnectionService because of the bug found there. None of the subscribers will be // invoked on conenction change, which might introduce regressions // ConnectionService.shared.addSubscriber(self) - if let token = StorageService.sharedInstance.token, let clientId = StorageService.sharedInstance.clientId, !token.isEmpty { + let host = Host() + + if let token = storageService.token, let clientId = storageService.clientId, !token.isEmpty { mqtt?.delegate = nil mqtt = CocoaMQTT(clientID: clientId, host: host.url, port: host.port) mqtt?.password = token @@ -151,115 +101,138 @@ final class MQTTService: NSObject, CocoaMQTTDelegate, ConnectionServiceDelegate mqtt?.username = "api" mqtt?.cleanSession = false } + mqtt?.enableSSL = Bundle.main.isServerConnectionSecure - mqtt?.willMessage = CocoaMQTTWill(topic: "version/\(MQTTService.version)", message: "") + mqtt?.willMessage = CocoaMQTTWill(topic: "version/\(Bundle.main.modelsVersion)", message: "") mqtt?.keepAlive = 0 mqtt?.delegate = self autoReconnectTimeInterval = 15 - LogService.log(topic: .MQTT) { return "setup clientID: \(mqtt?.clientID ?? "") & password: \(mqtt?.password ?? "")" } + LogService.log(topic: .MQTT) { [weak self] in + return "setup clientID: \(self?.mqtt?.clientID ?? "") & password: \(self?.mqtt?.password ?? "")" + } } - func mqtt(_ mqtt: CocoaMQTT, didConnectAck ack: CocoaMQTTConnAck) { - if ack == .badUsernameOrPassword { - if let actualToken = StorageService.sharedInstance.token { - if actualToken == mqtt.password { - LogService.log(topic: .MQTT) { return "Bad Username or Password" } - #if !SHARE_EXTENSION - LogService.log(topic: .db) { return "Clear storage: bad username" } - StorageService.sharedInstance.clearStorage() - - self.state = .notAuthenticated(isLoggedOutFromServer: true) - - IoHandler.delegate?.sessionNotFound() - notifySubscribers { (delegate) in - delegate.mqttServiceDidReceiveAuthenticationFailure(self) - } - self.reconnect() - #endif - } - } else { - LogService.log(topic: .MQTT) { return "Bad protocol version" } - isBadProtocolVersion = true - notifySubscribers { (delegate) in - delegate.mqttServiceDidReceiveWrongServerVersion() - } + func tryReconnect() { + queuePool.processingQueue.async { [unowned self] in + let states: [CocoaMQTTConnState] = [.initial, .disconnected] + if let connState = self.mqtt?.connState, states.contains(connState) { + self.reconnect() } } - if ack == .accept { - LogService.log(topic: .MQTT) { return "clientID: \(mqtt.clientID ) & password: \(mqtt.password ?? "") & clearSession: \(mqtt.cleanSession) state: \(mqtt.connState)" } - - isConnectedSuccess = true - - #if !SHARE_EXTENSION - if (mqtt.password ?? "") != "" && self.push != nil { - self.updatePushToken(push_token: self.push!) + } + + func reconnect() { + LogService.log(topic: .MQTT) { return "Reconnect" } + disconnect() + connect() + } + + func disconnect() { + queuePool.processingQueue.async { [unowned self] in + LogService.log(topic: .MQTT) { return "Disconnect" } + self.mqtt?.disconnect() + } + } + + + // MARK: - Publish + + func publish(model: BaseMQTTModel) { + queuePool.processingQueue.async { [mqtt] in + guard let mqtt = mqtt, mqtt.connState == .connected else { + return } - #endif + + let finalModel: CocoaMQTTMessage = model.getMessage() + mqtt.publish(finalModel) } } - private func fetchCurrentState() -> SessionState { - guard let sessionState = userDefaults.dictionary(forKey: SessionState.Keys.state), - let isLoggedOutFromServer = sessionState[SessionState.Keys.isLoggedOutFromServer] as? Bool else { - return .notDetermined + + // MARK: - CocoaMQTTDelegate + + func mqtt(_ mqtt: CocoaMQTT, didConnectAck ack: CocoaMQTTConnAck) { + queuePool.processingQueue.async { [unowned self] in + if ack == .badUsernameOrPassword { + self.handleBadUsernameOrPassword(mqtt) + } + if ack == .accept { + self.handleAccept(mqtt) + } } - return self.isAuthenticated ? .notDetermined : .notAuthenticated(isLoggedOutFromServer: isLoggedOutFromServer) } - private var shouldResendLogout: Bool { - let storage = StorageService.sharedInstance - if !storage.hasToken, case let .notAuthenticated(isLoggedOutFromServer) = self.state, !isLoggedOutFromServer { - return true + private func handleBadUsernameOrPassword(_ mqtt: CocoaMQTT) { + if let actualToken = storageService.token, actualToken == mqtt.password { + handleInvalidToken(mqtt) + } else { + handleBadProtocolVersion(mqtt) } - return false } - func tryReconnect() { - let states: [CocoaMQTTConnState] = [.initial, .disconnected] - if let connState = mqtt?.connState, states.contains(connState) { - reconnect() + private func handleInvalidToken(_ mqtt: CocoaMQTT) { + LogService.log(topic: .MQTT) { return "Bad Username or Password" } + #if !SHARE_EXTENSION + LogService.log(topic: .db) { return "Clear storage: bad username" } + storageService.clearStorage() + + IoHandler.delegate { $0.sessionNotFound() } + notifySubscribers { (delegate) in + delegate.mqttServiceDidReceiveAuthenticationFailure(self) } + self.reconnect() + #endif } - func reconnect() { - LogService.log(topic: .MQTT) { return "Reconnect" } - self.setup() - myConnect() + private func handleBadProtocolVersion(_ mqtt: CocoaMQTT) { + LogService.log(topic: .MQTT) { return "Bad protocol version" } + isBadProtocolVersion = true + notifySubscribers { (delegate) in + delegate.mqttServiceDidReceiveWrongServerVersion() + } } - func disconnect(shouldRemoveConnectionSubscriber: Bool = true) { - LogService.log(topic: .MQTT) { return "Disconnect" } - // NOTE: Don't unsubscribe for ConnectionService because of the bug found there. None of the subscribers will be - // invoked on conenction change, which might introduce regressions -// if shouldRemoveConnectionSubscriber { -// ConnectionService.shared.removeSubscriber(self) -// } - mqtt?.disconnect() + private func handleAccept(_ mqtt: CocoaMQTT) { + LogService.log(topic: .MQTT) { return "clientID: \(mqtt.clientID ) & password: \(mqtt.password ?? "") & clearSession: \(mqtt.cleanSession) state: \(mqtt.connState)" } + + isConnectedSuccess = true + + #if !SHARE_EXTENSION + if let pushToken = storageService.pushToken, (mqtt.password ?? "") != "" { + self.update(pushToken: pushToken) + } + #endif } func mqtt(_ mqtt: CocoaMQTT, didPublishMessage message: CocoaMQTTMessage, id: UInt16) { printMessage(msg: message, isSent: true) } - func mqtt(_ mqtt: CocoaMQTT, didReceiveMessage message: CocoaMQTTMessage, id: UInt16 ) { + func mqtt(_ mqtt: CocoaMQTT, didReceiveMessage message: CocoaMQTTMessage, id: UInt16) { printMessage(msg: message, isSent: false) - HandlerService.handle(response: message) + queuePool.receivingQueue.async { + HandlerService.handle(response: message) + } } func mqttDidDisconnect(_ mqtt: CocoaMQTT, withError err: Error?) { - LogService.log(topic: .MQTT) { return "clientID: \(mqtt.clientID ) & password: \(mqtt.password ?? "") & clearSession: \(mqtt.cleanSession) state: \(mqtt.connState) & error: \(err?.localizedDescription)" } - - if let error = err as NSError?, error.code == 7 { - LogService.log(topic: .MQTT) { return "Something went wrong" } + queuePool.processingQueue.async { [unowned self] in + LogService.log(topic: .MQTT) { return "clientID: \(mqtt.clientID ) & password: \(mqtt.password ?? "") & clearSession: \(mqtt.cleanSession) state: \(mqtt.connState) & error: \(err?.localizedDescription)" } + + if let error = err as NSError?, error.code == 7 { + LogService.log(topic: .MQTT) { return "Something went wrong" } + } + + self.isConnectedSuccess = false } - - isConnectedSuccess = false } func mqtt(_ mqtt: CocoaMQTT, didConnect host: String, port: Int) { - isConnectedSuccess = true + queuePool.processingQueue.async { [unowned self] in + self.isConnectedSuccess = true + } } func mqtt(_ mqtt: CocoaMQTT, didSubscribeTopic topic: String) {} @@ -268,33 +241,38 @@ final class MQTTService: NSObject, CocoaMQTTDelegate, ConnectionServiceDelegate func mqttDidPing(_ mqtt: CocoaMQTT) {} func mqttDidReceivePong(_ mqtt: CocoaMQTT) {} + // MARK: Subscribers + func addSubscriber(_ subscriber: MQTTServiceDelegate) { - subscribersQueue.sync { - subscribers.append(WeakRef(value: subscriber)) + queuePool.subscribersQueue.async { [unowned self, weak subscriber] in + if let subscriber = subscriber { + self.subscribers.append(WeakRef(value: subscriber)) + } } } func removeSubscriber(_ subscriber: MQTTServiceDelegate) { - subscribersQueue.sync { - subscribers = subscribers.filter { $0.value !== subscriber } + queuePool.subscribersQueue.async { [unowned self, weak subscriber] in + if let subscriber = subscriber { + self.subscribers = self.subscribers.filter { $0.value !== subscriber } + } } } func removeAllSubscribers() { - subscribersQueue.sync { - subscribers.removeAll() + queuePool.subscribersQueue.async { [unowned self] in + self.subscribers.removeAll() } } - /* Notify all subscribers that reachbility status changed */ - - private func notifySubscribers(with eventClosure: (MQTTServiceDelegate) -> Void) { - - subscribersQueue.sync { - subscribers.forEach { (weak) in + private func notifySubscribers(with eventClosure: @escaping (MQTTServiceDelegate) -> Void) { + queuePool.subscribersQueue.sync { [unowned self] in + self.subscribers.forEach { (weak) in if let delegate = weak.value as? MQTTServiceDelegate { - eventClosure(delegate) + dispatchAsyncMain { + eventClosure(delegate) + } } } } @@ -303,31 +281,20 @@ final class MQTTService: NSObject, CocoaMQTTDelegate, ConnectionServiceDelegate // MARK: - ConnectionServiceDelegate - func connectionStatusChanged(_ sender: ConnectionService, service: ConnectionService.Service, oldValue: ConnectionService.ConnectionServiceState) { + func connectionStatusChanged( + _ sender: ConnectionService, + service: ConnectionService.Service, oldValue: ConnectionService.ConnectionServiceState) { - if service == .networking { - guard let networkStatus = sender.state[.networking] else { return } - switch networkStatus { - case .connected: myConnect() - case .disconnected: disconnect(shouldRemoveConnectionSubscriber: false) - case .switched: reconnect() - default: break + queuePool.processingQueue.async { [unowned self] in + if service == .networking { + guard let networkStatus = sender.state[.networking] else { return } + switch networkStatus { + case .connected: self.mqtt?.connect() + case .disconnected: self.disconnect() + case .switched: self.reconnect() + default: break + } } } } - -} - -protocol MQTTServiceDelegate: class { - func mqttServiceDidConnect(_ mqttService: MQTTService) - func mqttServiceDidDisconnect(_ mqttService: MQTTService) - func mqttServiceDidReceiveAuthenticationFailure(_ mqttService: MQTTService) - func mqttServiceDidReceiveWrongServerVersion() -} - -extension MQTTServiceDelegate { - func mqttServiceDidConnect(_ mqttService: MQTTService) {} - func mqttServiceDidDisconnect(_ mqttService: MQTTService) {} - func mqttServiceDidReceiveAuthenticationFailure(_ mqttService: MQTTService) {} - func mqttServiceDidReceiveWrongServerVersion() {} } diff --git a/Nynja/Services/MQTT/MQTTServiceDelegate.swift b/Nynja/Services/MQTT/MQTTServiceDelegate.swift new file mode 100644 index 0000000000000000000000000000000000000000..9a2ab23ffe74f0c4e9beca546f6a1e3b65a76155 --- /dev/null +++ b/Nynja/Services/MQTT/MQTTServiceDelegate.swift @@ -0,0 +1,23 @@ +// +// MQTTServiceDelegate.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/9/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +protocol MQTTServiceDelegate: class { + func mqttServiceDidConnect(_ mqttService: MQTTService) + func mqttServiceDidDisconnect(_ mqttService: MQTTService) + func mqttServiceDidReceiveAuthenticationFailure(_ mqttService: MQTTService) + func mqttServiceDidReceiveWrongServerVersion() +} + +extension MQTTServiceDelegate { + func mqttServiceDidConnect(_ mqttService: MQTTService) {} + func mqttServiceDidDisconnect(_ mqttService: MQTTService) {} + func mqttServiceDidReceiveAuthenticationFailure(_ mqttService: MQTTService) {} + func mqttServiceDidReceiveWrongServerVersion() {} +} diff --git a/Nynja/Services/MQTT/MQTTServiceProtocol.swift b/Nynja/Services/MQTT/MQTTServiceProtocol.swift new file mode 100644 index 0000000000000000000000000000000000000000..a58000f493c6ddca7d37e6e967381b32d3e7c6e5 --- /dev/null +++ b/Nynja/Services/MQTT/MQTTServiceProtocol.swift @@ -0,0 +1,26 @@ +// +// MQTTServiceProtocol.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/14/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +//import Founda + +protocol MQTTServiceProtocol { + + var isBadProtocolVersion: Bool { get } + var isConnectedSuccess: Bool { get } + + func connect() + func tryReconnect() + func reconnect() + func disconnect() + + func publish(model: BaseMQTTModel) + + func addSubscriber(_ subscriber: MQTTServiceDelegate) + func removeSubscriber(_ subscriber: MQTTServiceDelegate) + func removeAllSubscribers() +} diff --git a/Nynja/Services/Models/AuthModel.swift b/Nynja/Services/Models/AuthModel.swift index d66ec20bb35ce0d2e5376566619739a8f37051d5..d4e04abcd91f0e9c61137fdbeb784738d663b9d4 100644 --- a/Nynja/Services/Models/AuthModel.swift +++ b/Nynja/Services/Models/AuthModel.swift @@ -38,7 +38,7 @@ class AuthModel: BaseMQTTModel { var smsCode: String? var languages: String? var pushID: String? - var token: NSData? + var token: Data? var settings: [Feature]? init(type: AuthType, @@ -47,7 +47,7 @@ class AuthModel: BaseMQTTModel { clientID : String? = nil, deviceToken: String? = nil, settings: [Feature]? = nil, - token: NSData? = nil, + token: Data? = nil, smsCode: String? = nil, languages: String? = nil, pushID: String? = nil) { @@ -89,7 +89,7 @@ class AuthModel: BaseMQTTModel { let pID = Bert.getBin(pushID) var tok: BertObject = BertNil() - if let toke = self.token { + if let toke = self.token.flatMap({ NSData(data: $0) }) { tok = BertBinary(fromNSData: toke) } diff --git a/Nynja/Services/NynjaCalls/NynjaCommunicatorService.swift b/Nynja/Services/NynjaCalls/NynjaCommunicatorService.swift index 8636a192b621c4bc6315495a8764bf3eda201f45..526cd7e54b131159ba603208a08be1f776c47146 100644 --- a/Nynja/Services/NynjaCalls/NynjaCommunicatorService.swift +++ b/Nynja/Services/NynjaCalls/NynjaCommunicatorService.swift @@ -467,8 +467,7 @@ class NynjaCommunicatorService: NSObject, NynjaCommunicatorDelegate, NYNCallDele //MARK: Helpers func getMySelf() -> Contact? { - guard let id = StorageService.sharedInstance.rosterId, let roster = RosterDAO.findRosterBy(id: id) else { return nil } - return roster.myContact + return ContactDAO.currentContact } func makeMembers(contacts: [Contact], room: Room?) -> [Member] { diff --git a/Nynja/Services/PushService.swift b/Nynja/Services/PushService.swift index 91c423ae4f43f68dfed1c24e1f8b8554d48e70b9..1525a106be0fa22b11fbd56177d88643b141a6f5 100644 --- a/Nynja/Services/PushService.swift +++ b/Nynja/Services/PushService.swift @@ -12,11 +12,11 @@ import UserNotifications final class PushService: NSObject, PKPushRegistryDelegate, UserSettingsRespondable { - static let sharedInstance: PushService = { - let instance = PushService() - instance.registerForPushNotifications() - return instance - }() + private var storageService: StorageService { + return .sharedInstance + } + + static let sharedInstance = PushService() // MARK: - Init @@ -33,7 +33,18 @@ final class PushService: NSObject, PKPushRegistryDelegate, UserSettingsRespondab // MARK: - Setup - func registerForPushNotifications() { + func registerForNotifications() { + registerForNativeNotifications() + registerForPushKitNotifications() + } + + private func registerForNativeNotifications() { + let notificationsSettings = UIUserNotificationSettings(types: [.sound, .alert, .badge], categories: nil) + UIApplication.shared.registerUserNotificationSettings(notificationsSettings) + UIApplication.shared.registerForRemoteNotifications() + } + + private func registerForPushKitNotifications() { let voipRegistry = PKPushRegistry(queue: DispatchQueue.main) voipRegistry.delegate = self voipRegistry.desiredPushTypes = [.voIP] @@ -45,14 +56,13 @@ final class PushService: NSObject, PKPushRegistryDelegate, UserSettingsRespondab func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) { let voipPushToken = credentials.token let token = voipPushToken.map { String(format: "%02.2hhx", $0) }.joined() - //MQTTService.sharedInstance.updatePushToken(push_token: token) - MQTTService.sharedInstance.push = token + storageService.pushToken = token LogService.log(topic: .system) { return "Register push token: \(token)" } } @objc(pushRegistry:didReceiveIncomingPushWithPayload:forType:) func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) { - guard MQTTService.sharedInstance.isAuthenticated, + guard storageService.hasToken, UIApplication.shared.applicationState != .active, let aps = Aps(data: payload.dictionaryPayload), aps.isValidIp, @@ -75,8 +85,8 @@ final class PushService: NSObject, PKPushRegistryDelegate, UserSettingsRespondab case historyDelete = "history_delete" case updateHistory = "history_update" case messageDelete = "message_delete" - case messageEdit = "message_edit" - case undefined = "" + case messageEdit = "message_edit" + case undefined = "" } // MARK: - Handle Notifications @@ -214,7 +224,7 @@ final class PushService: NSObject, PKPushRegistryDelegate, UserSettingsRespondab message.localStatus = status } try MessageDAO.trustIfNextMessageExists(before: message) - try StorageService.sharedInstance.perform(action: .save, with: message) + try storageService.perform(action: .save, with: message) } private func getMessage(_ aps: Aps) -> Message? { @@ -252,7 +262,7 @@ final class PushService: NSObject, PKPushRegistryDelegate, UserSettingsRespondab if let lastMessage = contact.last_msg { try prepareForSave(message: lastMessage) } - try StorageService.sharedInstance.perform(action: .save, with: contact) + try storageService.perform(action: .save, with: contact) } catch { LogService.log(topic: .db) { return error.localizedDescription } } @@ -263,7 +273,7 @@ final class PushService: NSObject, PKPushRegistryDelegate, UserSettingsRespondab if let lastMessage = room.last_msg { try prepareForSave(message: lastMessage) } - try StorageService.sharedInstance.perform(action: .save, with: room) + try storageService.perform(action: .save, with: room) } catch { LogService.log(topic: .db) { return error.localizedDescription } } diff --git a/Nynja/Services/ServiceFactory/FileDownloaderFactory/FileDownloaderFactory.swift b/Nynja/Services/ServiceFactory/FileDownloaderFactory/FileDownloaderFactory.swift new file mode 100644 index 0000000000000000000000000000000000000000..b7a1b8427c311b6d199aaa4a2e21d032df2095be --- /dev/null +++ b/Nynja/Services/ServiceFactory/FileDownloaderFactory/FileDownloaderFactory.swift @@ -0,0 +1,23 @@ +// +// FileDownloaderFactory.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/14/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +final class FileDownloaderFactory { + + let serviceFactory: ServiceFactoryProtocol + + init(serviceFactory: ServiceFactoryProtocol) { + self.serviceFactory = serviceFactory + } + + func makeFileDownloader(for kind: FileDownloaderKind) -> FileDownloader { + switch kind { + case .amazon: + return serviceFactory.makeAmazonManager() + } + } +} diff --git a/Nynja/Services/ServiceFactory/FileDownloaderFactory/FileDownloaderKind.swift b/Nynja/Services/ServiceFactory/FileDownloaderFactory/FileDownloaderKind.swift new file mode 100644 index 0000000000000000000000000000000000000000..a841644a5185ef5443e0940f8758daa74af1d756 --- /dev/null +++ b/Nynja/Services/ServiceFactory/FileDownloaderFactory/FileDownloaderKind.swift @@ -0,0 +1,11 @@ +// +// FileDownloaderKind.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/14/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +enum FileDownloaderKind { + case amazon +} diff --git a/Nynja/Services/ServiceFactory/ServiceFactory.swift b/Nynja/Services/ServiceFactory/ServiceFactory.swift index f581ec61b326b571744b34cbedbdcf94e48afb35..6ca43a469692ae64dfb435eefc26afb75e440208 100644 --- a/Nynja/Services/ServiceFactory/ServiceFactory.swift +++ b/Nynja/Services/ServiceFactory/ServiceFactory.swift @@ -34,6 +34,7 @@ protocol ServiceFactoryProtocol: SharedServiceFactoryProtocol { func makeWalletService() -> WalletService func makeSyncFileManager() -> SyncFileManager + func makeSyncFileManager(with kind: FileDownloaderKind) -> SyncFileManager func makeMuteChatService() -> MuteChatServiceProtocol @@ -47,6 +48,20 @@ protocol ServiceFactoryProtocol: SharedServiceFactoryProtocol { func makeUseCaseValidationServise() -> UseCaseValidationServiceProtocol func makeAudioSessionManager() -> AudioSessionManager + + func makeHomeDataProvider(limit: Int) -> HomeDataProvider + + func makePushService() -> PushService + + func makeNynjaCommunicatorService() -> NynjaCommunicatorService + + func makeBadgeNumberService() -> BadgeNumberServiceProtocol + + func makeNotificationManager() -> NotificationManager + + func makeUserSettingsService() -> UserSettingsService + + func makeReachabilityService() -> ReachabilityService } final class ServiceFactory: SharedServiceFactory, ServiceFactoryProtocol { @@ -100,13 +115,13 @@ final class ServiceFactory: SharedServiceFactory, ServiceFactoryProtocol { } func makeConversationsProvider() -> ConversationsProviding { - return ConversationsProvider() + return ConversationsProvider( + dependencies: .init(dbManager: makeStorageService())) } func makeStickersProvider() -> StickersProviding { return StickersProvider( - dependencies: .init(storage: makeStorageService()) - ) + dependencies: .init(storage: makeStorageService())) } func makePermissionManager() -> PermissionManager { @@ -140,6 +155,13 @@ final class ServiceFactory: SharedServiceFactory, ServiceFactoryProtocol { func makeSyncFileManager() -> SyncFileManager { return SyncFileManager.sharedInstance } + + func makeSyncFileManager(with kind: FileDownloaderKind) -> SyncFileManager { + let downloader = FileDownloaderFactory(serviceFactory: self).makeFileDownloader(for: kind) + let syncFileManager = makeSyncFileManager() + syncFileManager.downloader = downloader + return syncFileManager + } func makeMuteChatService() -> MuteChatServiceProtocol { let dependencies = MuteChatService.Dependencies(mqttService: makeMQTTService()) @@ -169,4 +191,39 @@ final class ServiceFactory: SharedServiceFactory, ServiceFactoryProtocol { func makeAudioSessionManager() -> AudioSessionManager { return AudioSessionManager.shared } + + func makeHomeDataProvider(limit: Int) -> HomeDataProvider { + return HomeDataProviderImpl( + dependencies: .init( + services: .init( + conversationsProviding: makeConversationsProvider(), + contactsProviding: makeContactsProvider() + ), + fields: .init( + limit: limit))) + } + + func makePushService() -> PushService { + return .sharedInstance + } + + func makeNynjaCommunicatorService() -> NynjaCommunicatorService { + return .sharedInstance + } + + func makeBadgeNumberService() -> BadgeNumberServiceProtocol { + return BadgeNumberService.shared + } + + func makeNotificationManager() -> NotificationManager { + return .shared + } + + func makeUserSettingsService() -> UserSettingsService { + return .shared + } + + func makeReachabilityService() -> ReachabilityService { + return .sharedInstance + } } diff --git a/Nynja/Services/StickersDownloadingService/StickersDownloadingService.swift b/Nynja/Services/StickersDownloadingService/StickersDownloadingService.swift index 438562db631d892f33e9c50e485831a25c8e2833..5eb64229bdf3a9d1fcf5ccbde89c118d9b54fcfc 100644 --- a/Nynja/Services/StickersDownloadingService/StickersDownloadingService.swift +++ b/Nynja/Services/StickersDownloadingService/StickersDownloadingService.swift @@ -11,19 +11,6 @@ import SDWebImage final class StickersDownloadingService { - private let operationQueue: OperationQueue - - - // MARK: - Init - - init() { - operationQueue = OperationQueue() - operationQueue.maxConcurrentOperationCount = 1 - } - - - // MARK: - Prefetching - func download(_ packages: [StickerPack]) { var urls = [URL]() for package in packages { diff --git a/Nynja/Services/StorageService.swift b/Nynja/Services/StorageService.swift index 9ef71ae31dc8f7c563b4e79dca10c6b262092e9a..fc79d14d2951c5819df772eb736ed0a0ace085f0 100644 --- a/Nynja/Services/StorageService.swift +++ b/Nynja/Services/StorageService.swift @@ -10,15 +10,19 @@ import Foundation import GRDBCipher import CryptoSwift -//MARK: - The Game is began +// MARK: - The Game is began + +/// Thread-safe class StorageService { private let passphraseKey = KeychainService.Keys.dataBasePassphrase let userDefaults = UserDefaults(suiteName: Bundle.main.appGroupName) let keychain = KeychainService.standard - - private let countriesProvider = CountriesProvider() + private(set) lazy var userInfo: UserInfo = { + return UserInfoImpl( + dependencies: .init(userDefaults: userDefaults)) + }() #if !SHARE_EXTENSION private let databaseManager = DatabaseManager() @@ -28,10 +32,12 @@ class StorageService { } #endif + private let isolationQueue = DispatchQueue(label: String.label(withSuffix: "storage-service.isolation-queue")) + // MARK: - Properties lazy var countries: [CountryModel] = { - return countriesProvider.fetchCountries() + return CountriesProvider().fetchCountries() }() /// It is used only for debug purposes. @@ -48,6 +54,12 @@ class StorageService { // MARK: - Setup func setupDatabase(with name: String, application: UIApplication) { + isolationQueue.sync { [weak self] in + self?._setupDatabase(with: name, application: application) + } + } + + private func _setupDatabase(with name: String, application: UIApplication) { #if !SHARE_EXTENSION LogService.log(topic: .db) { return "Setup DB: name = \(name)" } @@ -80,7 +92,7 @@ class StorageService { application: application) keychain.set(newPassphrase, forKey: passphraseKey) - LogService.log(topic: .passphrase) { return "Store new passphrase: \(keychain.string(forKey: passphraseKey))" } + LogService.log(topic: .passphrase) {[weak self] in return "Store new passphrase: \(self?.keychain.string(forKey: self?.passphraseKey ?? ""))" } } private func setupInsecureDatabase(with name: String, application: UIApplication) { @@ -103,10 +115,12 @@ class StorageService { // MARK: - Clear func clearStorage() { - #if !SHARE_EXTENSION - databaseManager.clear() - #endif - keychain.clear() + isolationQueue.sync { [weak self] in + #if !SHARE_EXTENSION + self?.databaseManager.clear() + #endif + self?.keychain.clear() + } dropUserInfo() } } @@ -134,7 +148,7 @@ extension StorageService: DBManagerProtocol { try databaseManager.perform(action: action, with: model) } - func perform(action: DatabaseAction, with model: DBModelProtocol) throws { + func perform(action: DatabaseAction, with model: DBModel) throws { try databaseManager.perform(action: action, with: model) } @@ -142,9 +156,63 @@ extension StorageService: DBManagerProtocol { try databaseManager.perform(action: action, with: models) } - func perform(action: DatabaseAction, with models: [DBModelProtocol]) throws { + func perform(action: DatabaseAction, with models: [DBModel]) throws { try databaseManager.perform(action: action, with: models) } - } #endif + + +// MARK: - UserInfo + +extension StorageService: UserInfo { + + var token: String? { + get { return userInfo.token } + set { userInfo.token = newValue } + } + + var tokenData: Data? { + get { return userInfo.tokenData } + } + + var pushToken: String? { + get { return userInfo.pushToken } + set { userInfo.pushToken = newValue } + } + + var phone: String? { + get { return userInfo.phone } + set { userInfo.phone = newValue } + } + + var rosterId: Int64? { + get { return userInfo.rosterId } + set { userInfo.rosterId = newValue } + } + + var clientId: String? { + get { return userInfo.clientId } + set { userInfo.clientId = newValue } + } + + var wasLogined: Bool { + get { return userInfo.wasLogined } + set { userInfo.wasLogined = newValue } + } + + var wasRun: Bool { + get { return userInfo.wasRun } + set { userInfo.wasRun = newValue } + } + + func setupAuth(clientId: String, token: String) { + self.clientId = clientId + self.token = token + } + + func setupUserInfo(from roster: Roster) { + phone = roster.phone + rosterId = roster.id + } +} diff --git a/Nynja/StarActionDAO.swift b/Nynja/StarActionDAO.swift index ca97857e44a245c3c6390b2861d8b42574a92e43..21c2585c7bce8cf1b1a6734ea95991c5e66346dc 100644 --- a/Nynja/StarActionDAO.swift +++ b/Nynja/StarActionDAO.swift @@ -24,7 +24,7 @@ final class StarActionDAO: StarActionDAOProtocol { } return dbManager.rowExists( in: StarActionTable.self, - where: "\(StarActionTable.Column.starId) = ?", + where: "\(Column.starId) = ?", arguments: [starLocalId] ) } diff --git a/Nynja/StarDAO.swift b/Nynja/StarDAO.swift index 239ee47573a898f8d07b775032cbdc0b23212710..44bac7df4522f02c5a79d75072ec76551365e026 100644 --- a/Nynja/StarDAO.swift +++ b/Nynja/StarDAO.swift @@ -6,24 +6,35 @@ // Copyright © 2018 TecSynt Solutions. All rights reserved. // +import GRDBCipher + class StarDAO: StarDAOProtocol { // MARK: - Fetch + static func fetchStarBy(rowId: Int64) -> DBStar? { return dbManager.fetch { db in - return try DBStar.star(db, rowId: rowId) + return try DBStar + .filter(Column.rowID == rowId) + .fetchOneConstructed(db) } } static func fetchStarBy(clientId: String) -> DBStar? { return dbManager.fetch { db in - return try DBStar.star(db, clientId: clientId) + return try DBStar + .filter(Column.clientId == clientId) + .fetchOneConstructed(db) } } static func fetchStars(rosterId: Int64) -> [String: Star] { - let stars = dbManager.fetch { db in - return try DBStar.stars(from: db, rosterId: rosterId) + let stars = dbManager.fetch { db -> [DBStar] in + let args = RosterRelatedQueryArgs(rosterId: rosterId) + return try args + .makeTypedRequest() + .filter(Column.status !== Star.Status.remove.rawValue) + .fetchAllConstructed(db) } var result: [String: Star] = [:] @@ -37,9 +48,21 @@ class StarDAO: StarDAOProtocol { return result } + static func fetchStars(with args: RosterRelatedQueryArgs) -> [Star] { + return dbManager.fetch { db in + return try args + .makeTypedRequest() + .filter(Column.status !== Star.Status.remove.rawValue) + .fetchAllConstructed(db) + }.serverModels + } + static func fetchUndeliveredStars(rosterId: Int64) -> [Star] { - return dbManager - .fetch { try DBStar.undeliveredStars(from: $0, rosterId: rosterId) } - .map { Star(star: $0) } + return dbManager.fetch { db -> [DBStar] in + return try RosterRelatedQueryArgs(rosterId: rosterId) + .makeTypedRequest() + .filter(Column.id == nil) + .fetchAllConstructed(db) + }.serverModels } } diff --git a/Nynja/SyncFileManager/SyncFileManager.swift b/Nynja/SyncFileManager/SyncFileManager.swift index 3e1263358e559876742c3ead381246d333796e38..9dcf4275fac68ab9224ce55effc331194fa2ecb1 100644 --- a/Nynja/SyncFileManager/SyncFileManager.swift +++ b/Nynja/SyncFileManager/SyncFileManager.swift @@ -10,7 +10,7 @@ import Foundation import AWSCore import AWSS3 -protocol FileNetworkProtocol: class { +protocol FileDownloader: class { func download(url: String, from destination: RemoteStorageDestination, result: ((_ result: String?, _ transferInfo: TransferInfo?) -> ())?) -> AWSRequest? @@ -18,7 +18,7 @@ protocol FileNetworkProtocol: class { func upload(localUrl: String, result: ((_ result:String?, _ transferInfo:TransferInfo?)->())?) -> AWSRequest? } -extension FileNetworkProtocol { +extension FileDownloader { func download(url: String, result: ((_ result: String?, _ transferInfo: TransferInfo?) -> ())?) -> AWSRequest? { return download(url: url, from: .default, result: result) @@ -29,7 +29,7 @@ class SyncFileManager { static let sharedInstance = SyncFileManager() - var downloader: FileNetworkProtocol = AmazonManager.shared + var downloader: FileDownloader = AmazonManager.shared func saveExternalFileLink(localUrl: String, completion: ((_ externalUrl: URL?, _ transferInfo: TransferInfo?, _ request: AWSRequest?) ->())?) { if let shortPath = localUrl.getShortPath() { diff --git a/Nynja/TypingHandlerDelegate.swift b/Nynja/TypingHandlerDelegate.swift new file mode 100644 index 0000000000000000000000000000000000000000..de1844d5f079ea07d68d21f78f5c0327a805d40f --- /dev/null +++ b/Nynja/TypingHandlerDelegate.swift @@ -0,0 +1,13 @@ +// +// TypingHandlerDelegate.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/9/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +protocol TypingHandlerDelegate: class { + func getTyping(typing: Typing) +} diff --git a/Nynja/UserInfo.swift b/Nynja/UserInfo.swift index abddc6a468ec0314e7d849aa97753de353cb48bf..c04b6179c28ad7d5dda587fa0f97ff862c504a3d 100644 --- a/Nynja/UserInfo.swift +++ b/Nynja/UserInfo.swift @@ -10,8 +10,9 @@ import Foundation enum UserIdentifiers: String { case token + case pushToken + case phone - case voxId case rosterId case clientId = "clientID" @@ -21,10 +22,11 @@ enum UserIdentifiers: String { protocol UserInfo: class { var token: String? { get set } - var tokenAsNSData: NSData? { get } + var tokenData: Data? { get } + + var pushToken: String? { get set } var phone: String? { get set } - var voxId: String? { get set } var rosterId: Int64? { get set } var clientId: String? { get set } @@ -60,9 +62,7 @@ extension UserInfo { LogService.log(topic: .userDefaults) { return "drop user info" } token = nil phone = nil - voxId = nil rosterId = nil clientId = nil } - } diff --git a/Nynja/StorageService+UserInfo.swift b/Nynja/UserInfoImpl.swift similarity index 52% rename from Nynja/StorageService+UserInfo.swift rename to Nynja/UserInfoImpl.swift index b520bbda0837d9ce41904f26744be12140b1a6cb..6f81d898d6b05ffa0cfc7f92608b69b33b386586 100644 --- a/Nynja/StorageService+UserInfo.swift +++ b/Nynja/UserInfoImpl.swift @@ -1,21 +1,35 @@ // -// StorageService+UserInfo.swift +// UserInfoImpl.swift // Nynja // -// Created by Volodymyr Hryhoriev on 7/26/18. +// Created by Volodymyr Hryhoriev on 11/9/18. // Copyright © 2018 TecSynt Solutions. All rights reserved. // import Foundation -extension StorageService: UserInfo { +class UserInfoImpl: UserInfo, InitializeInjectable { + + let userDefaults: UserDefaults? + + private var mqttService: MQTTService { + return .sharedInstance + } + + required init(dependencies: Dependencies) { + userDefaults = dependencies.userDefaults + _wasRun = value(forId: .wasRun) ?? false + } + + + // MARK: - UserInfo private var encoding: String.Encoding { return .utf8 } - private var tokenData: Data? { - return userDefaults?.data(forKey: UserIdentifiers.token.rawValue) + var tokenData: Data? { + return value(forId: .token) } var token: String? { @@ -25,7 +39,7 @@ extension StorageService: UserInfo { return token } set { - guard let token = newValue, let data = token.data(using: encoding) else { + guard let token = newValue, let data = token.data(using: self.encoding) else { userDefaults?.removeObject(forKey: UserIdentifiers.token.rawValue) userDefaults?.synchronize() return @@ -33,61 +47,58 @@ extension StorageService: UserInfo { set(data as NSData, forId: .token) LogService.log(topic: .userDefaults) { return "Save token: \(token)" } - MQTTService.sharedInstance.reconnect() + MQTTService.sharedInstance.reconnect() // FIXME: I don't think it is proper place to do such thing } } - var tokenAsNSData: NSData? { - return tokenData.flatMap { NSData(data: $0) } + var pushToken: String? { + get { return value(forId: .pushToken) } + set { set(newValue, forId: .pushToken) } } var phone: String? { - get { return userDefaults?.string(forKey: UserIdentifiers.phone.rawValue) } + get { return value(forId: .phone) } set { set(newValue, forId: .phone) } } - var voxId: String? { - get { return userDefaults?.string(forKey: UserIdentifiers.voxId.rawValue) } - set { set(newValue, forId: .voxId) } - } - var rosterId: Int64? { - get { - guard let id = userDefaults?.value(forKey: UserIdentifiers.rosterId.rawValue) as? Int64 else { return nil } - return id - } + get { return value(forId: .rosterId) } set { set(newValue, forId: .rosterId) } } var clientId: String? { - get { return userDefaults?.string(forKey: UserIdentifiers.clientId.rawValue) } + get { return value(forId: .clientId) } set { set(newValue, forId: .clientId) } } var wasLogined: Bool { - get { return userDefaults?.bool(forKey: UserIdentifiers.wasLogined.rawValue) ?? false } + get { return value(forId: .wasLogined) ?? false } set { set(newValue, forId: .wasLogined) } } + private var _wasRun: Bool = false + var wasRun: Bool { - get { return userDefaults?.bool(forKey: UserIdentifiers.wasRun.rawValue) ?? false } + get { return _wasRun } set { set(newValue, forId: .wasRun) } } - func setupAuth(clientId: String, token: String) { - self.clientId = clientId - self.token = token - } - - func setupUserInfo(from roster: Roster) { - phone = roster.phone - rosterId = roster.id - voxId = roster.myContact?.voxID + private func value(forId id: UserIdentifiers) -> T? { + return userDefaults?.value(forKey: id.rawValue) as? T } private func set(_ value: Any?, forId id: UserIdentifiers) { userDefaults?.set(value, forKey: id.rawValue) userDefaults?.synchronize() } +} + + +// MARK: - InitializeInjectable + +extension UserInfoImpl { + struct Dependencies { + let userDefaults: UserDefaults? + } } diff --git a/Nynja/UserSettings/Service/UserSettingsService.swift b/Nynja/UserSettings/Service/UserSettingsService.swift index 5ae2229d1cfe365ba204e65451fb9d4d57140440..3a82323ddf5e238d5d4dde28b4239331284261e7 100644 --- a/Nynja/UserSettings/Service/UserSettingsService.swift +++ b/Nynja/UserSettings/Service/UserSettingsService.swift @@ -46,13 +46,26 @@ final class UserSettingsService { static let theme = "theme" static let notifications = "notifications" } + + + // MARK: - Singleton + static let shared = UserSettingsService() - private let storage = UserDefaults.standard + private init() {} + + + // MARK: - Queues + + private let subscribersQueue = DispatchQueue(label: String.label(withSuffix: "user-settings-service.subscribers-queue")) // MARK: - Properties + private var storage: UserDefaults { + return .standard + } + private lazy var settings: UserSettings = { return UserSettings(wheelPosition: wheelPosition, theme: theme, notifications: notifications) }() @@ -108,23 +121,32 @@ final class UserSettingsService { private var subscribers: [UserSettingsSubscriber] = [] func addSubscriber(_ object: AnyObject, for options: UserSettingsUpdateOptions = .all, handler: @escaping UserSettingsChangeHandler) { - guard !subscribers.contains(where: { $0.object.value === object }) else { - return + subscribersQueue.sync { [unowned self] in + guard !self.subscribers.contains(where: { $0.object.value === object }) else { + return + } + let subscriber = UserSettingsSubscriber(object: object, options: options, handler: handler) + self.subscribers.append(subscriber) + + // notify subscriber with current ui settings. + subscriber.handler(settings) } - let subscriber = UserSettingsSubscriber(object: object, options: options, handler: handler) - subscribers.append(subscriber) - - // notify subscriber with current ui settings. - subscriber.handler(settings) } func removeSubscriber(_ object: AnyObject) { - subscribers = subscribers.filter { $0.object.value != nil && $0.object.value !== object } + subscribersQueue.sync { [unowned self] in + subscribers = self.subscribers.filter { $0.object.value != nil && $0.object.value !== object } + } } private func notifySubscribers(with settings: UserSettings, options: UserSettingsUpdateOptions) { - for subscriber in subscribers where subscriber.options.contains(options) { - subscriber.handler(settings) + subscribersQueue.async() { [unowned self] in + for subscriber in self.subscribers where subscriber.options.contains(options) { + + dispatchAsyncMain { + subscriber.handler(settings) + } + } } } } diff --git a/Nynja/UserSettings/Settings/Theme.swift b/Nynja/UserSettings/Settings/Theme.swift index 6bba3db8926b37ff7a37d3ec271bb093a893401a..03acaa78c22ce1473c782ad3169b2a65a081dbb0 100644 --- a/Nynja/UserSettings/Settings/Theme.swift +++ b/Nynja/UserSettings/Settings/Theme.swift @@ -13,8 +13,8 @@ enum Theme: String { static let `default` = Theme.dark static let all: [Theme] = [dark, light] - case light = "light" - case dark = "dark" + case light + case dark var name: String { switch self { diff --git a/Nynja/UserSettings/Settings/WheelPosition.swift b/Nynja/UserSettings/Settings/WheelPosition.swift index 2a0c2600cf4f464ed2a9bc4c6df92e41907e609b..acc6de35085d4148ad8ca910d4826fdc34e07c86 100644 --- a/Nynja/UserSettings/Settings/WheelPosition.swift +++ b/Nynja/UserSettings/Settings/WheelPosition.swift @@ -9,8 +9,8 @@ import Foundation enum WheelPosition: String { - case left = "left" - case right = "right" + case left + case right static let `default` = WheelPosition.right static let all: [WheelPosition] = [left, right] diff --git a/NynjaUnitTests/Services/UserInfo/UserInfoTest.swift b/NynjaUnitTests/Services/UserInfo/UserInfoTest.swift index 73ac1e54a72263fa07479afaab8e2d96ab67d1b5..33f8f8054f868343c1a409bb110bbb3ea5a4ecfa 100644 --- a/NynjaUnitTests/Services/UserInfo/UserInfoTest.swift +++ b/NynjaUnitTests/Services/UserInfo/UserInfoTest.swift @@ -93,7 +93,6 @@ class UserInfoTest: XCTestCase { userInfo.phone = phone userInfo.rosterId = rosterId userInfo.token = token - userInfo.voxId = "vox_id" userInfo.clientId = "client_id" userInfo.dropUserInfo() @@ -101,7 +100,6 @@ class UserInfoTest: XCTestCase { XCTAssertNil(userInfo.phone) XCTAssertNil(userInfo.rosterId) XCTAssertNil(userInfo.token) - XCTAssertNil(userInfo.voxId) XCTAssertNil(userInfo.clientId) } @@ -112,13 +110,12 @@ private extension UserInfoTest { class UserInfoMock: UserInfo { var token: String? - var tokenAsNSData: NSData? + var tokenData: Data? + var pushToken: String? var phone: String? - var voxId: String? var rosterId: Int64? var clientId: String? var wasLogined: Bool = true var wasRun: Bool = true } - } diff --git a/Shared/Library/Extensions/Models/Contact/ContactExtension.swift b/Shared/Library/Extensions/Models/Contact/ContactExtension.swift index 0acc8d98f105dd613bd91b9ef9e9d3fabe5baa74..5be2afe2a046d55436f55279c2da285c8b03eeb9 100644 --- a/Shared/Library/Extensions/Models/Contact/ContactExtension.swift +++ b/Shared/Library/Extensions/Models/Contact/ContactExtension.swift @@ -8,7 +8,7 @@ import UIKit -extension Contact { +extension Contact: FullNameRepresentable { enum Status: String { case get case request @@ -22,24 +22,6 @@ extension Contact { case ignore } - /// Returns fullname, name or surname depending on what exists. - /// If contact doesn't have name and surname, it will return nil - var fullName: String? { - if let name = self.names, let surname = self.surnames { - if !surname.isEmpty { - return "\(name) \(surname)" - } else { - return name - } - } else if names != nil && surnames == nil { - return names - } else if names == nil && surnames == nil { - return surnames - } - - return nil - } - var alias: String? { return fullName } @@ -121,12 +103,6 @@ extension Contact { return phone_id ?? "" } - /// Returns 'person_id' if exists, else - empty string - var voxID: String { - let ser = self.services?.first { ($0.type as? StringAtom)?.string == "vox" } - return ser?.id ?? "" - } - /// Parse roster id from the 'phone_id' property var rosterId: Int64 { if var phone = self.phone_id, let range = phone.range(of: "_") { diff --git a/Shared/Library/StaticDelegating.swift b/Shared/Library/StaticDelegating.swift new file mode 100644 index 0000000000000000000000000000000000000000..39173973ea109dfad29d557c7f20831309d2b23c --- /dev/null +++ b/Shared/Library/StaticDelegating.swift @@ -0,0 +1,30 @@ +// +// StaticDelegating.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/9/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +protocol StaticDelegating { + associatedtype Delegate + + static var delegate: Delegate? { get set } + + static func delegate(on queue: DispatchQueue, closure: @escaping (Delegate) -> Void) +} + +extension StaticDelegating { + + static func delegate(on queue: DispatchQueue = .main, closure: @escaping (Delegate) -> Void) { + guard let delegate = self.delegate else { + return + } + + queue.async { + closure(delegate) + } + } +} diff --git a/Nynja/AuthHandler.swift b/Shared/Services/Handlers/AuthHandler/AuthHandler.swift similarity index 59% rename from Nynja/AuthHandler.swift rename to Shared/Services/Handlers/AuthHandler/AuthHandler.swift index c5ec8f5f2faab78ae37eaca7954d6a64fae8cd6f..ef1dd76f4744da0c549c305fbdc4cdafb5678b78 100644 --- a/Nynja/AuthHandler.swift +++ b/Shared/Services/Handlers/AuthHandler/AuthHandler.swift @@ -8,26 +8,20 @@ import Foundation -protocol AuthHandlerDelegate: class { - func processGetAll(auths: [Auth]) - func processDelete(auth: Auth) -} - -extension AuthHandlerDelegate { - func processGetAll(auths: [Auth]) {} - func processDelete(auth: Auth) {} -} - -class AuthHandler: BaseHandler { +final class AuthHandler: BaseHandler, StaticDelegating { static weak var delegate: AuthHandlerDelegate? static func executeHandle(data: BertTuple) { - guard let auth = get_Auth().parse(bert: data) as? Auth else {return} - guard let type = StringAtom.string(auth.type) else {return} + guard let auth = get_Auth().parse(bert: data) as? Auth, + let type = StringAtom.string(auth.type) else { + return + } + if type == "deleted" { - delegate?.processDelete(auth: auth) + delegate { $0.processDelete(auth: auth) } } + if auth.type?.string == "update" { LogService.log(topic: .MQTT) { return "Recived new token: \(auth.token ?? "")" } if let token = auth.token { @@ -38,6 +32,6 @@ class AuthHandler: BaseHandler { static func executeHandle(data: BertList) { let auths = data.elements.compactMap { get_Auth().parse(bert: $0) as? Auth } - delegate?.processGetAll(auths: auths) + delegate { $0.processGetAll(auths: auths) } } } diff --git a/Shared/Services/Handlers/AuthHandler/AuthHandlerDelegate.swift b/Shared/Services/Handlers/AuthHandler/AuthHandlerDelegate.swift new file mode 100644 index 0000000000000000000000000000000000000000..aa69442894dd10bb97230d1baa8c394e9ea7b729 --- /dev/null +++ b/Shared/Services/Handlers/AuthHandler/AuthHandlerDelegate.swift @@ -0,0 +1,19 @@ +// +// AuthHandlerDelegate.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/9/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +protocol AuthHandlerDelegate: class { + func processGetAll(auths: [Auth]) + func processDelete(auth: Auth) +} + +extension AuthHandlerDelegate { + func processGetAll(auths: [Auth]) {} + func processDelete(auth: Auth) {} +} diff --git a/Shared/Services/Handlers/Base/HandlerService.swift b/Shared/Services/Handlers/Base/HandlerService.swift index 7d5c1fee45085c9400f12386f60b301652c06312..efbb47e2319c8290a194944f420ddd67a199ff39 100644 --- a/Shared/Services/Handlers/Base/HandlerService.swift +++ b/Shared/Services/Handlers/Base/HandlerService.swift @@ -17,5 +17,4 @@ class HandlerService: HandlerServiceProtocol { let handlerType = HandlerFactory.handler(for: handler) handlerType.executeHandle(data: params) } - } diff --git a/Shared/Services/Handlers/ErrorsHandler.swift b/Shared/Services/Handlers/ErrorsHandler.swift index d284a35cb0d3164681243317b280771cd59427e1..5465d157167da9920916064ac0282c45cbe45cfe 100644 --- a/Shared/Services/Handlers/ErrorsHandler.swift +++ b/Shared/Services/Handlers/ErrorsHandler.swift @@ -20,5 +20,4 @@ final class ErrorsHandler: BaseHandler { let handler = HandlerFactory.handler(for: handlerKind) handler.executeHandle(data: dataTuple, codes: Set(codes)) } - } diff --git a/Shared/Services/Handlers/IoHandler.swift b/Shared/Services/Handlers/IoHandler.swift deleted file mode 100644 index f6fd64282f4d4fac0dc2b03406c96230694292c5..0000000000000000000000000000000000000000 --- a/Shared/Services/Handlers/IoHandler.swift +++ /dev/null @@ -1,184 +0,0 @@ -// -// IoHandler.swift -// Nynja -// -// Created by Anton Makarov on 14.06.2017. -// Copyright © 2017 TecSynt Solutions. All rights reserved. -// - -import Foundation - -protocol IoHandlerDelegate: class { - func smsSent() - func logined() - func wrongCode() - func mismatchUserData() - func sessionNotFound() - func attemptsExpired() - func notAuthorized() - func added() - func invalidData() - func callInProgress() - func logout() - func numberNotAllowed() - func sessionsCleared() - func sessionDeleted() - - func getContactSuccess(contact: Contact) - func getContactQRSuccess(contact: Contact) - func getContactByUsernameSucces(contact: Contact) - func getContactsSuccess(contacts: [Contact]) - func contactNotFound() - func contactsNotFound() - func contactQRNotFound() - func contactByUsernameNotFound() - func usernameIsBusy() - -} - -extension IoHandlerDelegate { - func smsSent() {} - func logined() {} - func wrongCode() {} - func mismatchUserData() {} - func sessionNotFound() {} - func attemptsExpired() {} - func notAuthorized() {} - func added() {} - func invalidData() {} - func callInProgress() {} - func logout() {} - func sessionsCleared() {} - func sessionDeleted() {} - - func getContactSuccess(contact: Contact) {} - func getContactQRSuccess(contact: Contact) {} - func getContactByUsernameSucces(contact: Contact) {} - func getContactsSuccess(contacts: [Contact]) {} - func contactNotFound() {} - func contactsNotFound() {} - func contactQRNotFound() {} - func contactByUsernameNotFound() {} - - func usernameIsBusy() {} - func numberNotAllowed() {} - - -} - -class IoHandler:BaseHandler { - - static weak var delegate: IoHandlerDelegate? - - static var storageService: StorageService { - return .sharedInstance - } - - static var mqttService: MQTTService { - return .sharedInstance - } - - static var keychainService: KeychainService { - return .standard - } - - static func executeHandle(data: BertTuple) { - if let IO = get_io().parse(bert: data) as? io { - var code: String? = nil - if let value = ((IO.code as? ok)?.code as? StringAtom)?.string { - code = value - } - if let value = ((IO.code as? error)?.code as? StringAtom)?.string { - code = value - } - if let value = (IO.code as? ok)?.code as? String { - code = value - } - if let value = (IO.code as? error2)?.code?.string { - code = value - } - if let ok2 = IO.code as? ok2 { - code = ok2.code?.string - if let src = (IO.code as? ok2)?.src as? [AnyObject] { - if src.count > 1 { - if let clientId = src[0] as? String, let token = src[1] as? String { - storageService.setupAuth(clientId: clientId, token: token) - } - } - } - } - if let action = code { - switch action { - case "deleted": - self.delegate?.sessionDeleted() - case "cleared": - self.delegate?.sessionsCleared() - case "sms_sent": - self.delegate?.smsSent() - case "invalid_data": - self.delegate?.invalidData() - case "session_not_found": - self.delegate?.sessionNotFound() - case "call_in_progress": - self.delegate?.callInProgress() - case "login": - StorageService.sharedInstance.wasLogined = true - case "mismatch_user_data": - self.delegate?.mismatchUserData() - case "invalid_sms_code": - self.delegate?.wrongCode() - case "attempts_expired": - self.delegate?.attemptsExpired() - case "number_not_allowed": - self.delegate?.numberNotAllowed() - case "logout": - LogService.log(topic: .db) { return "Clear storage: IoHandler" } - storageService.clearStorage() - - mqttService.state = .notAuthenticated(isLoggedOutFromServer: true) - - mqttService.disconnect() - mqttService.reconnect() - self.delegate?.logout() - case "phone": - if let roster = IO.data as? Roster { - if let contact = roster.userlist?.first { - self.delegate?.getContactSuccess(contact: contact) - } else { - self.delegate?.contactNotFound() - } - } - case "phonebook": - if let roster = IO.data as? Roster { - if let contacts = roster.userlist { - self.delegate?.getContactsSuccess(contacts: contacts) - } else { - self.delegate?.contactsNotFound() - } - } - case "qrcode": - if let roster = IO.data as? Roster { - if let contact = roster.userlist?.first { - self.delegate?.getContactQRSuccess(contact: contact) - } else { - self.delegate?.contactQRNotFound() - } - } - case "nick": - self.delegate?.usernameIsBusy() - case "username": - if let roster = IO.data as? Roster { - if let contact = roster.userlist?.first { - self.delegate?.getContactByUsernameSucces(contact: contact) - } else { - self.delegate?.contactByUsernameNotFound() - } - } - default: - break - } - } - } - } - -} diff --git a/Shared/Services/Handlers/IoHandler/IoHandler.swift b/Shared/Services/Handlers/IoHandler/IoHandler.swift new file mode 100644 index 0000000000000000000000000000000000000000..58a1b1f52cfc551f3b5c3730b9ca9d372e520d45 --- /dev/null +++ b/Shared/Services/Handlers/IoHandler/IoHandler.swift @@ -0,0 +1,122 @@ +// +// IoHandler.swift +// Nynja +// +// Created by Anton Makarov on 14.06.2017. +// Copyright © 2017 TecSynt Solutions. All rights reserved. +// + +import Foundation + +final class IoHandler: BaseHandler, StaticDelegating { + + static weak var delegate: IoHandlerDelegate? + + static var storageService: StorageService { + return .sharedInstance + } + + static var mqttService: MQTTService { + return .sharedInstance + } + + static func executeHandle(data: BertTuple) { + if let IO = get_io().parse(bert: data) as? io { + var code: String? = nil + if let value = ((IO.code as? ok)?.code as? StringAtom)?.string { + code = value + } + if let value = ((IO.code as? error)?.code as? StringAtom)?.string { + code = value + } + if let value = (IO.code as? ok)?.code as? String { + code = value + } + if let value = (IO.code as? error2)?.code?.string { + code = value + } + if let ok2 = IO.code as? ok2 { + code = ok2.code?.string + if let src = (IO.code as? ok2)?.src as? [AnyObject] { + if src.count > 1 { + if let clientId = src[0] as? String, let token = src[1] as? String { + storageService.setupAuth(clientId: clientId, token: token) + } + } + } + } + if let action = code { + switch action { + case "deleted": + delegate { $0.sessionDeleted() } + case "cleared": + delegate { $0.sessionsCleared() } + case "sms_sent": + delegate { $0.smsSent() } + case "invalid_data": + delegate { $0.invalidData() } + case "session_not_found": + delegate { $0.sessionNotFound() } + case "call_in_progress": + delegate { $0.callInProgress() } + case "login": + StorageService.sharedInstance.wasLogined = true + case "mismatch_user_data": + delegate { $0.mismatchUserData() } + case "invalid_sms_code": + delegate { $0.wrongCode() } + case "attempts_expired": + delegate { $0.attemptsExpired() } + case "number_not_allowed": + delegate { $0.numberNotAllowed() } + case "phone": + if let roster = IO.data as? Roster { + if let contact = roster.userlist?.first { + delegate { $0.getContactSuccess(contact: contact) } + } else { + delegate { $0.contactNotFound() } + } + } + case "phonebook": + if let roster = IO.data as? Roster { + if let contacts = roster.userlist { + delegate { $0.getContactsSuccess(contacts: contacts) } + } else { + delegate { $0.contactsNotFound() } + } + } + case "qrcode": + if let roster = IO.data as? Roster { + if let contact = roster.userlist?.first { + delegate { $0.getContactQRSuccess(contact: contact) } + } else { + delegate { $0.contactQRNotFound() } + } + } + case "nick": + delegate { $0.usernameIsBusy() } + case "username": + if let roster = IO.data as? Roster { + if let contact = roster.userlist?.first { + delegate { $0.getContactByUsernameSucces(contact: contact) } + } else { + delegate { $0.contactByUsernameNotFound() } + } + } + default: + break + } + } + } + } + + static func delegate(_ closure: @escaping (IoHandlerDelegate) -> Void) { + guard let delegate = self.delegate else { + return + } + + dispatchAsyncMain { + closure(delegate) + } + } +} diff --git a/Shared/Services/Handlers/IoHandler/IoHandlerDelegate.swift b/Shared/Services/Handlers/IoHandler/IoHandlerDelegate.swift new file mode 100644 index 0000000000000000000000000000000000000000..7620fa52bbbf27ad4288e4a40ed4af9f177cab94 --- /dev/null +++ b/Shared/Services/Handlers/IoHandler/IoHandlerDelegate.swift @@ -0,0 +1,64 @@ +// +// IoHandlerDelegate.swift +// Nynja +// +// Created by Volodymyr Hryhoriev on 11/9/18. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +protocol IoHandlerDelegate: class { + func smsSent() + func logined() + func wrongCode() + func mismatchUserData() + func sessionNotFound() + func attemptsExpired() + func notAuthorized() + func added() + func invalidData() + func callInProgress() + func didLogout() + func numberNotAllowed() + func sessionsCleared() + func sessionDeleted() + + func getContactSuccess(contact: Contact) + func getContactQRSuccess(contact: Contact) + func getContactByUsernameSucces(contact: Contact) + func getContactsSuccess(contacts: [Contact]) + func contactNotFound() + func contactsNotFound() + func contactQRNotFound() + func contactByUsernameNotFound() + func usernameIsBusy() +} + +extension IoHandlerDelegate { + func smsSent() {} + func logined() {} + func wrongCode() {} + func mismatchUserData() {} + func sessionNotFound() {} + func attemptsExpired() {} + func notAuthorized() {} + func added() {} + func invalidData() {} + func callInProgress() {} + func didLogout() {} + func sessionsCleared() {} + func sessionDeleted() {} + + func getContactSuccess(contact: Contact) {} + func getContactQRSuccess(contact: Contact) {} + func getContactByUsernameSucces(contact: Contact) {} + func getContactsSuccess(contacts: [Contact]) {} + func contactNotFound() {} + func contactsNotFound() {} + func contactQRNotFound() {} + func contactByUsernameNotFound() {} + + func usernameIsBusy() {} + func numberNotAllowed() {} +} diff --git a/Shared/Services/SharedServiceFactory/SharedServiceFactory.swift b/Shared/Services/SharedServiceFactory/SharedServiceFactory.swift index 1b8274323a76c31a51f61c19d8371f02eea009b1..40e54e9d20b5834f88b005881abd0a05c0661bd6 100644 --- a/Shared/Services/SharedServiceFactory/SharedServiceFactory.swift +++ b/Shared/Services/SharedServiceFactory/SharedServiceFactory.swift @@ -12,6 +12,7 @@ protocol SharedServiceFactoryProtocol: class { func makeAmazonManager() -> AmazonManager func makeAmazonInitializer() -> AmazonInitializer func makeTypingSenderService() -> TypingSenderServiceProtocol + func makeLogWriter() -> LogWriterProtocol? } class SharedServiceFactory: SharedServiceFactoryProtocol { @@ -38,4 +39,8 @@ class SharedServiceFactory: SharedServiceFactoryProtocol { return TypingSenderService(dependencies: dependencies) } + + func makeLogWriter() -> LogWriterProtocol? { + return LogWriter.shared + } }