Merge pull request #757 from Microsoft/vbulavin/issue_719

#719 - Implemented
This commit is contained in:
Vadim-Bulavin 2017-11-10 20:26:02 +02:00 коммит произвёл GitHub
Родитель 36e50c38f6 352f81cc26
Коммит 2ab39e7182
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
38 изменённых файлов: 649 добавлений и 433 удалений

Просмотреть файл

@ -57,7 +57,7 @@
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-Spotlight-40@2x-1.png",
"filename" : "Icon-Spotlight-42.png",
"scale" : "2x"
},
{

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 3.6 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.5 KiB

Просмотреть файл

@ -876,7 +876,7 @@
88C661311FB46D4C0096B776 /* ReplyUpdateHint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88C661301FB46D4C0096B776 /* ReplyUpdateHint.swift */; };
88C661331FB4708E0096B776 /* Reply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88C661321FB4708E0096B776 /* Reply.swift */; };
88C661351FB478690096B776 /* TopicUpdateHint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88C661341FB478690096B776 /* TopicUpdateHint.swift */; };
88C661371FB481FF0096B776 /* HandleChangesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88C661361FB481FF0096B776 /* HandleChangesManager.swift */; };
88C661371FB481FF0096B776 /* HandleChangesMulticast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88C661361FB481FF0096B776 /* HandleChangesMulticast.swift */; };
88C665061F866B0800A7763D /* PopularUsersAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88C665051F866B0800A7763D /* PopularUsersAPI.swift */; };
88C665081F866E8500A7763D /* PopularUsersResponseProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88C665071F866E8500A7763D /* PopularUsersResponseProcessor.swift */; };
88C6650A1F8671B200A7763D /* PopularUsersAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88C665091F8671B200A7763D /* PopularUsersAPITests.swift */; };
@ -917,6 +917,9 @@
88D123261F753BF6001523D1 /* MockOutgoingCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D123251F753BF6001523D1 /* MockOutgoingCommand.swift */; };
88D123281F753D5D001523D1 /* MockOutgoingCommandOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D123271F753D5D001523D1 /* MockOutgoingCommandOperation.swift */; };
88D1232A1F754278001523D1 /* OperationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D123291F754278001523D1 /* OperationObserver.swift */; };
88D291DD1FB61C990007879F /* HandleUpdateDaemon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D291DC1FB61C990007879F /* HandleUpdateDaemon.swift */; };
88D291DF1FB61DF20007879F /* DaemonsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D291DE1FB61DF20007879F /* DaemonsFactory.swift */; };
88D291E11FB61E010007879F /* DaemonsFactoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D291E01FB61E010007879F /* DaemonsFactoryImpl.swift */; };
88D2F8D71F55C2B50004A8BC /* ReportingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D2F8D61F55C2B50004A8BC /* ReportingService.swift */; };
88D2F8D91F56A4440004A8BC /* ReportSubmittedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D2F8D81F56A4440004A8BC /* ReportSubmittedViewController.swift */; };
88D2F8DB1F56A61B0004A8BC /* ReportRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D2F8DA1F56A61B0004A8BC /* ReportRouter.swift */; };
@ -1046,6 +1049,7 @@
88F551D61F3AF8BE00F4AAB9 /* UserFollowersAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88F551D51F3AF8BE00F4AAB9 /* UserFollowersAPITests.swift */; };
88F551D81F3AF93600F4AAB9 /* MyFollowingAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88F551D71F3AF93600F4AAB9 /* MyFollowingAPITests.swift */; };
88F551DA1F3AFA8700F4AAB9 /* UserFollowingAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88F551D91F3AFA8700F4AAB9 /* UserFollowingAPITests.swift */; };
88F6F9921FB6066100802BA9 /* CacheRequestExecutorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88F6F9911FB6066100802BA9 /* CacheRequestExecutorProvider.swift */; };
88F7A7861F2788DE005FEC5F /* LoginInteractorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88F7A7851F2788DE005FEC5F /* LoginInteractorTests.swift */; };
88F7A7891F27896B005FEC5F /* MockAuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88F7A7881F27896B005FEC5F /* MockAuthService.swift */; };
88F7A78B1F2789F0005FEC5F /* MockUserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88F7A78A1F2789F0005FEC5F /* MockUserService.swift */; };
@ -2179,7 +2183,7 @@
88C661301FB46D4C0096B776 /* ReplyUpdateHint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyUpdateHint.swift; sourceTree = "<group>"; };
88C661321FB4708E0096B776 /* Reply.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reply.swift; sourceTree = "<group>"; };
88C661341FB478690096B776 /* TopicUpdateHint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopicUpdateHint.swift; sourceTree = "<group>"; };
88C661361FB481FF0096B776 /* HandleChangesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandleChangesManager.swift; sourceTree = "<group>"; };
88C661361FB481FF0096B776 /* HandleChangesMulticast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandleChangesMulticast.swift; sourceTree = "<group>"; };
88C665051F866B0800A7763D /* PopularUsersAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PopularUsersAPI.swift; sourceTree = "<group>"; };
88C665071F866E8500A7763D /* PopularUsersResponseProcessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PopularUsersResponseProcessor.swift; sourceTree = "<group>"; };
88C665091F8671B200A7763D /* PopularUsersAPITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PopularUsersAPITests.swift; sourceTree = "<group>"; };
@ -2222,6 +2226,9 @@
88D123251F753BF6001523D1 /* MockOutgoingCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockOutgoingCommand.swift; sourceTree = "<group>"; };
88D123271F753D5D001523D1 /* MockOutgoingCommandOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockOutgoingCommandOperation.swift; sourceTree = "<group>"; };
88D123291F754278001523D1 /* OperationObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationObserver.swift; sourceTree = "<group>"; };
88D291DC1FB61C990007879F /* HandleUpdateDaemon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandleUpdateDaemon.swift; sourceTree = "<group>"; };
88D291DE1FB61DF20007879F /* DaemonsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaemonsFactory.swift; sourceTree = "<group>"; };
88D291E01FB61E010007879F /* DaemonsFactoryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaemonsFactoryImpl.swift; sourceTree = "<group>"; };
88D2F8D61F55C2B50004A8BC /* ReportingService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportingService.swift; sourceTree = "<group>"; };
88D2F8D81F56A4440004A8BC /* ReportSubmittedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportSubmittedViewController.swift; sourceTree = "<group>"; };
88D2F8DA1F56A61B0004A8BC /* ReportRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportRouter.swift; sourceTree = "<group>"; };
@ -2351,6 +2358,7 @@
88F551D51F3AF8BE00F4AAB9 /* UserFollowersAPITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserFollowersAPITests.swift; sourceTree = "<group>"; };
88F551D71F3AF93600F4AAB9 /* MyFollowingAPITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyFollowingAPITests.swift; sourceTree = "<group>"; };
88F551D91F3AFA8700F4AAB9 /* UserFollowingAPITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserFollowingAPITests.swift; sourceTree = "<group>"; };
88F6F9911FB6066100802BA9 /* CacheRequestExecutorProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheRequestExecutorProvider.swift; sourceTree = "<group>"; };
88F7A7851F2788DE005FEC5F /* LoginInteractorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginInteractorTests.swift; sourceTree = "<group>"; };
88F7A7881F27896B005FEC5F /* MockAuthService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockAuthService.swift; sourceTree = "<group>"; };
88F7A78A1F2789F0005FEC5F /* MockUserService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockUserService.swift; sourceTree = "<group>"; };
@ -3233,6 +3241,7 @@
884CDD841FACA2B8000D467B /* OutgoingExecutors */,
88BE87441FACA1D300F5AF05 /* IncomingExecutors */,
8802F2191F596BBA00FD0A15 /* RequestExecutorProvider.swift */,
88F6F9911FB6066100802BA9 /* CacheRequestExecutorProvider.swift */,
);
path = RequestExecuters;
sourceTree = "<group>";
@ -3878,12 +3887,12 @@
88580A871F2213AB00C1CBAF /* Base */,
88303DDB1F8771BB00330E30 /* PaginatedListProcessor */,
88580A951F2213AB00C1CBAF /* SideMenu */,
88F6F9971FB61A7A00802BA9 /* Publisher-Subscriber */,
88580A8B1F2213AB00C1CBAF /* APIError.swift */,
88580A8C1F2213AB00C1CBAF /* AppFonts.swift */,
880435CB1F67D04500154ACD /* AsyncOperation.swift */,
8831EDE71F975CF10013C8B8 /* CacheCleanupStrategy.swift */,
88580A8A1F2213AB00C1CBAF /* CellModel.swift */,
88C6612C1FB44F2E0096B776 /* Publisher-Subscriber.swift */,
88B94EF91F66B278002392F9 /* Daemon.swift */,
9CDDB2021F38866000C46785 /* DateFormatter.swift */,
88580A8D1F2213AB00C1CBAF /* Identifiable.swift */,
@ -3891,9 +3900,6 @@
88580A8F1F2213AB00C1CBAF /* LaunchArguments.swift */,
636474921F628715000D7B3D /* MenuViewModelBuilder.swift */,
8814291F1F4B19F400FC9F1E /* MyProfileOpener.swift */,
88C6612E1FB455B40096B776 /* CommentUpdateHint.swift */,
88C661341FB478690096B776 /* TopicUpdateHint.swift */,
88C661301FB46D4C0096B776 /* ReplyUpdateHint.swift */,
9C70FCD31F9A3DEE00BEB047 /* NotificationsUpdater.swift */,
88580A901F2213AB00C1CBAF /* NavigationStack.swift */,
88580A921F2213AB00C1CBAF /* Palette.swift */,
@ -4040,8 +4046,9 @@
88F551CA1F39E7F700F4AAB9 /* AutoEquatable.swift */,
32EBE7791F9FD7920007EF53 /* FeedPresentBatchModel.swift */,
88580B031F2213AB00C1CBAF /* CrossModuleCoordinator.swift */,
88C661361FB481FF0096B776 /* HandleChangesManager.swift */,
88C661361FB481FF0096B776 /* HandleChangesMulticast.swift */,
882AD0C21F2225FF00FC20B7 /* QueriableRepository.swift */,
88D291DC1FB61C990007879F /* HandleUpdateDaemon.swift */,
88580C7C1F2213EF00C1CBAF /* Repository.swift */,
9CB26BF41F989B54005B9797 /* ActivityNotificationsController.swift */,
);
@ -4939,6 +4946,8 @@
isa = PBXGroup;
children = (
8881E4E31F6976C300AA6DF2 /* DaemonsController.swift */,
88D291DE1FB61DF20007879F /* DaemonsFactory.swift */,
88D291E01FB61E010007879F /* DaemonsFactoryImpl.swift */,
88580B251F2213AB00C1CBAF /* SocialMenuItemsProvider.swift */,
88580B2B1F2213AB00C1CBAF /* SocialPlus.swift */,
88580B261F2213AB00C1CBAF /* SocialPlusServiceProvider.swift */,
@ -5904,6 +5913,17 @@
path = Followers;
sourceTree = "<group>";
};
88F6F9971FB61A7A00802BA9 /* Publisher-Subscriber */ = {
isa = PBXGroup;
children = (
88C6612E1FB455B40096B776 /* CommentUpdateHint.swift */,
88C661341FB478690096B776 /* TopicUpdateHint.swift */,
88C661301FB46D4C0096B776 /* ReplyUpdateHint.swift */,
88C6612C1FB44F2E0096B776 /* Publisher-Subscriber.swift */,
);
path = "Publisher-Subscriber";
sourceTree = "<group>";
};
88F7A7871F2788E1005FEC5F /* Login */ = {
isa = PBXGroup;
children = (
@ -6829,7 +6849,7 @@
TargetAttributes = {
886B39C41F138F5D00BF1A8E = {
CreatedOnToolsVersion = 8.3.3;
ProvisioningStyle = Manual;
ProvisioningStyle = Automatic;
};
8EF0E5911F1FA5CC00E88ED6 = {
CreatedOnToolsVersion = 8.3.3;
@ -6838,7 +6858,7 @@
};
9CD805281F0D012F00B9B0AC = {
CreatedOnToolsVersion = 9.0;
ProvisioningStyle = Manual;
ProvisioningStyle = Automatic;
};
9CD805311F0D012F00B9B0AC = {
CreatedOnToolsVersion = 9.0;
@ -7270,7 +7290,7 @@
88580C501F2213AB00C1CBAF /* LoginInteractorInput.swift in Sources */,
8811C85E1F42FB6B00F7513D /* EmbeddedEditProfileInteractorInput.swift in Sources */,
9C6D35A41F31D1C1005893DB /* FeedModuleViewOutput.swift in Sources */,
88C661371FB481FF0096B776 /* HandleChangesManager.swift in Sources */,
88C661371FB481FF0096B776 /* HandleChangesMulticast.swift in Sources */,
88580BBC1F2213AB00C1CBAF /* String+Encoding.swift in Sources */,
88580C4D1F2213AB00C1CBAF /* CreatePostViewOutput.swift in Sources */,
8846CB491F30C98C00F11EC8 /* UserListModuleInput.swift in Sources */,
@ -7315,6 +7335,7 @@
88204FBB1F581F2A00E15F5A /* SuggestedUsersRequestExecutor.swift in Sources */,
88F385861F30D1080048B04C /* UserListInteractorInput.swift in Sources */,
88BC5C501F2B65D0001FBDD2 /* UserProfileInteractorInput.swift in Sources */,
88D291DD1FB61C990007879F /* HandleUpdateDaemon.swift in Sources */,
88F3857F1F30CD890048B04C /* UserListModuleOutput.swift in Sources */,
88580C4E1F2213AB00C1CBAF /* LoginConfigurator.swift in Sources */,
88B94F061F66D01E002392F9 /* UnfollowUserOperation.swift in Sources */,
@ -7332,6 +7353,7 @@
885E04021F6FBFB600D6EBD8 /* CommentCommand.swift in Sources */,
88B94EFA1F66B278002392F9 /* Daemon.swift in Sources */,
88E3AC0B1F55567F0016357F /* SettingsViewOutput.swift in Sources */,
88D291DF1FB61DF20007879F /* DaemonsFactory.swift in Sources */,
885994DF1F96586300947544 /* RemoveTopicCommand.swift in Sources */,
8864C8221F45BA6000ADCE13 /* SearchViewController.swift in Sources */,
8811C8881F431B9A00F7513D /* EditProfileViewInput.swift in Sources */,
@ -7603,6 +7625,7 @@
88C682551F7A4D82004BD291 /* FollowRequestsInteractorInput.swift in Sources */,
9CB26BF51F989B54005B9797 /* ActivityNotificationsController.swift in Sources */,
8864C8501F4709D300ADCE13 /* SearchTabInfo.swift in Sources */,
88F6F9921FB6066100802BA9 /* CacheRequestExecutorProvider.swift in Sources */,
883BFFCF1F71124000E1C4E3 /* CreateTopicImageCommand.swift in Sources */,
883234451F9F7E050008DAD2 /* LiveOperationProgress.m in Sources */,
88E3AC461F55B7570016357F /* ReportViewInput.swift in Sources */,
@ -7712,6 +7735,7 @@
88580C711F2213AB00C1CBAF /* TabMenuContainerViewInput.swift in Sources */,
8832345F1F9F7E050008DAD2 /* URL+OAuthSwift.swift in Sources */,
88580BB01F2213AB00C1CBAF /* TableDataDisplayManager.swift in Sources */,
88D291E11FB61E010007879F /* DaemonsFactoryImpl.swift in Sources */,
8892F25B1F7E618500A58D8B /* LinkedAccountView+Ext.swift in Sources */,
8875CCE71F331CB3001F2474 /* FollowingConfigurator.swift in Sources */,
88BC5C5B1F2B6628001FBDD2 /* ProfileSummaryView.swift in Sources */,
@ -8237,9 +8261,9 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = 9KBH5RKYEW;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 285W24646W;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
GCC_C_LANGUAGE_STANDARD = gnu99;
INFOPLIST_FILE = "EmbeddedSocial-Example/Info.plist";
@ -8247,8 +8271,8 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.microsoft.embeddedsocial-df";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "369c1331-48f6-48ab-908c-42ec1f7a638a";
PROVISIONING_PROFILE_SPECIFIER = "Microsoft Embedded Social Dogfood Distribution";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@ -8260,9 +8284,9 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = 9KBH5RKYEW;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 285W24646W;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
GCC_C_LANGUAGE_STANDARD = gnu99;
INFOPLIST_FILE = "EmbeddedSocial-Example/Info.plist";
@ -8270,8 +8294,8 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.microsoft.embeddedsocial-df";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "369c1331-48f6-48ab-908c-42ec1f7a638a";
PROVISIONING_PROFILE_SPECIFIER = "Microsoft Embedded Social Dogfood Distribution";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@ -8430,8 +8454,9 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 9KBH5RKYEW;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
@ -8474,8 +8499,9 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 9KBH5RKYEW;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";

Просмотреть файл

@ -5,7 +5,7 @@
import Foundation
struct CommentUpdateHint: Hint {
struct CommentUpdateHint: HandleUpdateHint {
let oldHandle: String
let newHandle: String
}

Просмотреть файл

@ -17,3 +17,8 @@ protocol Subscriber: class {
protocol Hint {
}
protocol HandleUpdateHint: Hint {
var oldHandle: String { get }
var newHandle: String { get }
}

Просмотреть файл

@ -5,7 +5,7 @@
import Foundation
struct ReplyUpdateHint: Hint {
struct ReplyUpdateHint: HandleUpdateHint {
let oldHandle: String
let newHandle: String
}

Просмотреть файл

@ -5,7 +5,7 @@
import Foundation
struct TopicUpdateHint: Hint {
struct TopicUpdateHint: HandleUpdateHint {
let oldHandle: String
let newHandle: String
}

Просмотреть файл

@ -49,4 +49,9 @@ extension CacheType {
return false
}
func isCached(_ command: OutgoingCommand) -> Bool {
let p = PredicateBuilder().predicate(for: command)
return firstOutgoing(ofType: OutgoingCommand.self, predicate: p, sortDescriptors: nil) != nil
}
}

Просмотреть файл

@ -32,6 +32,19 @@ class CommentCommand: OutgoingCommand {
}
func apply(to feed: inout CommentFetchResult) {
var comments = feed.comments
for (index, var comment) in comments.enumerated() {
if comment.commentHandle == self.comment.commentHandle {
apply(to: &comment)
comments[index] = comment
}
}
feed.comments = comments
}
override func encodeToJSON() -> Any {
return [
"comment": comment.encodeToJSON(),
@ -74,4 +87,8 @@ class CommentCommand: OutgoingCommand {
ReportCommentCommand.self
]
}
var isActionCommand: Bool {
return CommentCommand.commentActionCommandTypes.contains(where: { $0 == type(of: self) })
}
}

Просмотреть файл

@ -13,6 +13,12 @@ final class CreateCommentCommand: CommentCommand {
override func getRelatedHandle() -> String? {
return comment.topicHandle
}
override func apply(to feed: inout CommentFetchResult) {
var comments = feed.comments
comments.insert(comment, at: 0)
feed.comments = comments
}
}

Просмотреть файл

@ -18,4 +18,12 @@ final class RemoveCommentCommand: CommentCommand {
override func getRelatedHandle() -> String? {
return comment.topicHandle
}
override func apply(to feed: inout CommentFetchResult) {
var comments = feed.comments
if let index = comments.index(where: { $0.commentHandle == self.comment.commentHandle }) {
comments.remove(at: index)
}
feed.comments = comments
}
}

Просмотреть файл

@ -18,4 +18,23 @@ final class CreateReplyCommand: ReplyCommand {
override func setRelatedHandle(_ relatedHandle: String?) {
reply.commentHandle = relatedHandle
}
func apply(to feed: inout CommentFetchResult) {
var comments = feed.comments
for (index, comment) in comments.enumerated() {
if comment.commentHandle == reply.commentHandle {
comment.totalReplies += 1
comments[index] = comment
}
}
feed.comments = comments
}
override func apply(to feed: inout RepliesFetchResult) {
var replies = feed.replies
replies.insert(reply, at: 0)
feed.replies = replies
}
}

Просмотреть файл

@ -18,4 +18,12 @@ final class RemoveReplyCommand: ReplyCommand {
override func getRelatedHandle() -> String? {
return reply.commentHandle
}
override func apply(to feed: inout RepliesFetchResult) {
var replies = feed.replies
if let index = replies.index(where: { $0.replyHandle == self.reply.replyHandle }) {
replies.remove(at: index)
}
feed.replies = replies
}
}

Просмотреть файл

@ -28,6 +28,19 @@ class ReplyCommand: OutgoingCommand {
}
func apply(to feed: inout RepliesFetchResult) {
var replies = feed.replies
for (index, var reply) in replies.enumerated() {
if reply.replyHandle == self.reply.replyHandle {
apply(to: &reply)
replies[index] = reply
}
}
feed.replies = replies
}
override func encodeToJSON() -> Any {
return [
"reply": reply.encodeToJSON(),
@ -64,4 +77,8 @@ class ReplyCommand: OutgoingCommand {
ReportReplyCommand.self
]
}
var isActionCommand: Bool {
return ReplyCommand.replyActionCommandTypes.contains(where: { $0 == type(of: self) })
}
}

Просмотреть файл

@ -16,30 +16,30 @@ struct OutgoingCommandOperationsBuilder: OutgoingCommandOperationsBuilderType {
func operation(for command: OutgoingCommand) -> OutgoingCommandOperation? {
// user commands
if let command = command as? FollowCommand {
return FollowUserOperation(command: command, socialService: SocialService())
return FollowUserOperation(command: command, socialService: makeSocialService())
} else if let command = command as? UnfollowCommand {
return UnfollowUserOperation(command: command, socialService: SocialService())
return UnfollowUserOperation(command: command, socialService: makeSocialService())
} else if let command = command as? BlockCommand {
return BlockUserOperation(command: command, socialService: SocialService())
return BlockUserOperation(command: command, socialService: makeSocialService())
} else if let command = command as? UnblockCommand {
return UnblockUserOperation(command: command, socialService: SocialService())
return UnblockUserOperation(command: command, socialService: makeSocialService())
} else if let command = command as? CancelPendingCommand {
return CancelPendingUserOperation(command: command, socialService: SocialService())
return CancelPendingUserOperation(command: command, socialService: makeSocialService())
} else if let command = command as? AcceptPendingCommand {
return AcceptPendingUserOperation(command: command, socialService: SocialService())
return AcceptPendingUserOperation(command: command, socialService: makeSocialService())
} else if let command = command as? ReportUserCommand {
return ReportUserOperation(command: command, service: ReportingService())
}
// topic commands
else if let command = command as? LikeTopicCommand {
return LikeTopicOperation(command: command, likesService: LikesService())
return LikeTopicOperation(command: command, likesService: makeLikesService())
} else if let command = command as? UnlikeTopicCommand {
return UnlikeTopicOperation(command: command, likesService: LikesService())
return UnlikeTopicOperation(command: command, likesService: makeLikesService())
} else if let command = command as? PinTopicCommand {
return PinTopicOperation(command: command, likesService: LikesService())
return PinTopicOperation(command: command, likesService: makeLikesService())
} else if let command = command as? UnpinTopicCommand {
return UnpinTopicOperation(command: command, likesService: LikesService())
return UnpinTopicOperation(command: command, likesService: makeLikesService())
} else if let command = command as? CreateTopicCommand {
return CreateTopicOperation(command: command, topicsService: TopicService(imagesService: ImagesService()))
} else if let command = command as? RemoveTopicCommand {
@ -54,9 +54,9 @@ struct OutgoingCommandOperationsBuilder: OutgoingCommandOperationsBuilderType {
// reply commands
else if let command = command as? LikeReplyCommand {
return LikeReplyOperation(command: command, likesService: LikesService())
return LikeReplyOperation(command: command, likesService: makeLikesService())
} else if let command = command as? UnlikeReplyCommand {
return UnlikeReplyOperation(command: command, likesService: LikesService())
return UnlikeReplyOperation(command: command, likesService: makeLikesService())
} else if let command = command as? CreateReplyCommand {
return CreateReplyOperation(command: command, repliesService: RepliesService())
} else if let command = command as? RemoveReplyCommand {
@ -69,9 +69,9 @@ struct OutgoingCommandOperationsBuilder: OutgoingCommandOperationsBuilderType {
// comment commands
else if let command = command as? LikeCommentCommand {
return LikeCommentOperation(command: command, likesService: LikesService())
return LikeCommentOperation(command: command, likesService: makeLikesService())
} else if let command = command as? UnlikeCommentCommand {
return UnlikeCommentOperation(command: command, likesService: LikesService())
return UnlikeCommentOperation(command: command, likesService: makeLikesService())
} else if let command = command as? CreateCommentCommand {
return CreateCommentOperation(command: command, commentsService: CommentsService(imagesService: ImagesService()))
} else if let command = command as? RemoveCommentCommand {
@ -104,6 +104,14 @@ struct OutgoingCommandOperationsBuilder: OutgoingCommandOperationsBuilderType {
return nil
}
private func makeLikesService() -> LikesServiceProtocol {
return LikesService()
}
private func makeSocialService() -> SocialServiceType {
return SocialService()
}
func fetchCommandsOperation(cache: CacheType, predicate: NSPredicate) -> FetchOutgoingCommandsOperation {
return FetchOutgoingCommandsOperation(cache: cache, predicate: predicate)
}

Просмотреть файл

@ -191,6 +191,20 @@ extension PredicateBuilder: OutgoingCommandsPredicateBuilder {
func allNotificationCommands() -> NSPredicate {
return NSPredicate(format: "typeid = %@", UpdateNotificationsStatusCommand.typeIdentifier)
}
func allCommentCommands() -> NSPredicate {
let typeIDs = CommentCommand.allCommentCommandTypes.map { $0.typeIdentifier }
return NSPredicate(format: "typeid IN %@", typeIDs)
}
func allReplyCommands() -> NSPredicate {
let typeIDs = ReplyCommand.allReplyCommandTypes.map { $0.typeIdentifier }
return NSPredicate(format: "typeid IN %@", typeIDs)
}
func createReplyCommands() -> NSPredicate {
return NSPredicate(format: "typeid = %@", CreateReplyCommand.typeIdentifier)
}
}
extension PredicateBuilder: TopicsFeedProcessorPredicateBuilder {

Просмотреть файл

@ -5,9 +5,9 @@
import Foundation
class HandleChangesManager: Publisher {
class HandleChangesMulticast: Publisher {
static let shared = HandleChangesManager()
static let shared = HandleChangesMulticast()
private let multicast = MulticastDelegate<Subscriber>()

Просмотреть файл

@ -0,0 +1,32 @@
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
//
import Foundation
class HandleUpdateDaemon: Daemon, Subscriber {
private let multicast: HandleChangesMulticast
private let predicateBuilder = PredicateBuilder()
private let handleUpdater: HandleUpdater
init(handleChangesMulticast: HandleChangesMulticast = HandleChangesMulticast.shared,
handleUpdater: HandleUpdater = OutgoingCommandsHandleUpdater()) {
multicast = handleChangesMulticast
self.handleUpdater = handleUpdater
}
func start() {
multicast.subscribe(self)
}
func stop() { }
func update(_ hint: Hint) {
guard let hint = hint as? HandleUpdateHint else { return }
let p = predicateBuilder.predicate(handle: hint.oldHandle)
handleUpdater.updateHandle(to: hint.newHandle, predicate: p)
}
}

Просмотреть файл

@ -0,0 +1,124 @@
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
//
import Foundation
class CacheRequestExecutorProvider: CacheRequestExecutorProviderType {
static func makeUsersFeedExecutor(for service: BaseService) -> UsersFeedRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseUserCompactView.self,
responseType: UsersListResponse.self,
service: service,
responseProcessor: UsersListResponseProcessor(cache: service.cache))
}
static func makeMyFollowingExecutor(for service: BaseService) -> UsersFeedRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseUserCompactView.self,
responseType: UsersListResponse.self,
service: service,
responseProcessor: MyFollowingResponseProcessor(cache: service.cache))
}
static func makeMyBlockedUsersExecutor(for service: BaseService) -> UsersFeedRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseUserCompactView.self,
responseType: UsersListResponse.self,
service: service,
responseProcessor: MyBlockedUsersResponseProcessor(cache: service.cache))
}
static func makeTopicsFeedExecutor(for service: BaseService) -> TopicsFeedRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseTopicView.self,
responseType: FeedFetchResult.self,
service: service,
responseProcessor: TopicsFeedResponseProcessor(cache: service.cache))
}
static func makeMyRecentTopicsFeedExecutor(for service: BaseService) -> TopicsFeedRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseTopicView.self,
responseType: FeedFetchResult.self,
service: service,
responseProcessor: MyRecentTopicsFeedResponseProcessor(cache: service.cache))
}
static func makeMyActivityExecutor(for service: BaseService) -> MyActivityRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseActivityView.self,
responseType: PaginatedResponse<ActivityView>.self,
service: service,
responseProcessor: ActivityFeedProcessor())
}
static func makeSinglePostExecutor(for service: BaseService) -> SingleTopicRequestExecutor {
return makeCommonExecutor(requestType: TopicView.self,
responseType: Post.self,
service: service,
responseProcessor: SinglePostResponseProcessor(cache: service.cache))
}
static func makeSuggestedUsersExecutor(for service: BaseService) -> SuggestedUsersRequestExecutor {
let executor = SuggestedUsersRequestExecutorImpl()
bind(service: service, to: executor)
return executor
}
class func makeAtomicOutgoingCommandsExecutor(for service: BaseService) -> AtomicOutgoingCommandsExecutor {
let executor = AtomicOutgoingCommandsExecutorImpl()
executor.cache = service.cache
executor.errorHandler = service.errorHandler
return executor
}
static func makeMyFollowersExecutor(for service: BaseService) -> UsersFeedRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseUserCompactView.self,
responseType: UsersListResponse.self,
service: service,
responseProcessor: MyFollowersResponseProcessor(cache: service.cache))
}
static func makeMyPendingRequestsExecutor(for service: BaseService) -> UsersFeedRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseUserCompactView.self,
responseType: UsersListResponse.self,
service: service,
responseProcessor: PendingRequestsResponseProcessor(cache: service.cache))
}
static func makeSearchTopicsFeedExecutor(for service: BaseService) -> TopicsFeedRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseTopicView.self,
responseType: FeedFetchResult.self,
service: service,
responseProcessor: SearchTopicsFeedResponseProcessor(cache: service.cache))
}
static func makePopularUsersExecutor(for service: BaseService) -> PopularUsersRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseUserProfileView.self,
responseType: UsersListResponse.self,
service: service,
responseProcessor: PopularUsersResponseProcessor())
}
static func makeHashtagsExecutor(for service: BaseService) -> HashtagsRequestExecutor {
let executor = HashtagsRequestExecutorImpl()
executor.cache = service.cache
executor.errorHandler = service.errorHandler
return executor
}
private static func makeCommonExecutor<T: Cacheable, U>(
requestType: T.Type,
responseType: U.Type,
service: BaseService,
responseProcessor: ResponseProcessor<T, U>) -> IncomingCacheRequestExecutor<T, U> {
let executor = IncomingCacheRequestExecutorImpl<T, U>()
bind(service: service, to: executor)
executor.responseProcessor = responseProcessor
return executor
}
private static func bind<T, U>(service: BaseService, to executor: IncomingCacheRequestExecutor<T, U>) {
executor.cache = service.cache
executor.errorHandler = service.errorHandler
executor.networkTracker = service.networkStatusMulticast
}
}

Просмотреть файл

@ -15,9 +15,13 @@ class AtomicOutgoingCommandsExecutorImpl: OutgoingCacheRequestExecutor<Object, V
builder: RequestBuilder<Object>,
completion: @escaping (Result<Void>) -> Void) {
cacheAndComplete(command: command, completion: completion)
runCommand(command, with: builder)
}
func cacheAndComplete(command: OutgoingCommand, completion: @escaping (Result<Void>) -> Void) {
cacheCommand(command)
completion(.success())
runCommand(command, with: builder)
}
private func cacheCommand(_ command: OutgoingCommand) {
@ -59,5 +63,4 @@ class AtomicOutgoingCommandsExecutorImpl: OutgoingCacheRequestExecutor<Object, V
}
commandBeingExecuted = nil
}
}

Просмотреть файл

@ -47,120 +47,3 @@ protocol CacheRequestExecutorProviderType {
static func makeHashtagsExecutor(for service: BaseService) -> HashtagsRequestExecutor
}
struct CacheRequestExecutorProvider: CacheRequestExecutorProviderType {
static func makeUsersFeedExecutor(for service: BaseService) -> UsersFeedRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseUserCompactView.self,
responseType: UsersListResponse.self,
service: service,
responseProcessor: UsersListResponseProcessor(cache: service.cache))
}
static func makeMyFollowingExecutor(for service: BaseService) -> UsersFeedRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseUserCompactView.self,
responseType: UsersListResponse.self,
service: service,
responseProcessor: MyFollowingResponseProcessor(cache: service.cache))
}
static func makeMyBlockedUsersExecutor(for service: BaseService) -> UsersFeedRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseUserCompactView.self,
responseType: UsersListResponse.self,
service: service,
responseProcessor: MyBlockedUsersResponseProcessor(cache: service.cache))
}
static func makeTopicsFeedExecutor(for service: BaseService) -> TopicsFeedRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseTopicView.self,
responseType: FeedFetchResult.self,
service: service,
responseProcessor: TopicsFeedResponseProcessor(cache: service.cache))
}
static func makeMyRecentTopicsFeedExecutor(for service: BaseService) -> TopicsFeedRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseTopicView.self,
responseType: FeedFetchResult.self,
service: service,
responseProcessor: MyRecentTopicsFeedResponseProcessor(cache: service.cache))
}
static func makeMyActivityExecutor(for service: BaseService) -> MyActivityRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseActivityView.self,
responseType: PaginatedResponse<ActivityView>.self,
service: service,
responseProcessor: ActivityFeedProcessor())
}
static func makeSinglePostExecutor(for service: BaseService) -> SingleTopicRequestExecutor {
return makeCommonExecutor(requestType: TopicView.self,
responseType: Post.self,
service: service,
responseProcessor: SinglePostResponseProcessor(cache: service.cache))
}
static func makeSuggestedUsersExecutor(for service: BaseService) -> SuggestedUsersRequestExecutor {
let executor = SuggestedUsersRequestExecutorImpl()
bind(service: service, to: executor)
return executor
}
static func makeAtomicOutgoingCommandsExecutor(for service: BaseService) -> AtomicOutgoingCommandsExecutor {
let executor = AtomicOutgoingCommandsExecutorImpl()
executor.cache = service.cache
executor.errorHandler = service.errorHandler
return executor
}
static func makeMyFollowersExecutor(for service: BaseService) -> UsersFeedRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseUserCompactView.self,
responseType: UsersListResponse.self,
service: service,
responseProcessor: MyFollowersResponseProcessor(cache: service.cache))
}
static func makeMyPendingRequestsExecutor(for service: BaseService) -> UsersFeedRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseUserCompactView.self,
responseType: UsersListResponse.self,
service: service,
responseProcessor: PendingRequestsResponseProcessor(cache: service.cache))
}
static func makeSearchTopicsFeedExecutor(for service: BaseService) -> TopicsFeedRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseTopicView.self,
responseType: FeedFetchResult.self,
service: service,
responseProcessor: SearchTopicsFeedResponseProcessor(cache: service.cache))
}
static func makePopularUsersExecutor(for service: BaseService) -> PopularUsersRequestExecutor {
return makeCommonExecutor(requestType: FeedResponseUserProfileView.self,
responseType: UsersListResponse.self,
service: service,
responseProcessor: PopularUsersResponseProcessor())
}
static func makeHashtagsExecutor(for service: BaseService) -> HashtagsRequestExecutor {
let executor = HashtagsRequestExecutorImpl()
executor.cache = service.cache
executor.errorHandler = service.errorHandler
return executor
}
private static func makeCommonExecutor<T: Cacheable, U>(
requestType: T.Type,
responseType: U.Type,
service: BaseService,
responseProcessor: ResponseProcessor<T, U>) -> IncomingCacheRequestExecutor<T, U> {
let executor = IncomingCacheRequestExecutorImpl<T, U>()
bind(service: service, to: executor)
executor.responseProcessor = responseProcessor
return executor
}
private static func bind<T, U>(service: BaseService, to executor: IncomingCacheRequestExecutor<T, U>) {
executor.cache = service.cache
executor.errorHandler = service.errorHandler
executor.networkTracker = service.networkStatusMulticast
}
}

Просмотреть файл

@ -6,68 +6,60 @@
import Foundation
protocol CommentsProcessorType {
func proccess(_ fetchResult: inout CommentFetchResult)
func proccess(_ fetchResult: inout CommentFetchResult, topicHandle: String)
}
class CommentsProcessor: CommentsProcessorType {
let cache: CacheType
private let cache: CacheType
private let predicateBuilder = PredicateBuilder()
private var commands: [CommentCommand] = []
required init(cache: CacheType) {
self.cache = cache
}
func proccess(_ fetchResult: inout CommentFetchResult) {
let commands = fetchCreateDeleteCommentCommands() + fetchCommentActionCommands()
for command in fetchCreateDeleteReplyCommands() {
if let index = fetchResult.comments.index(where: { $0.commentHandle == command.reply.commentHandle }) {
if command is RemoveReplyCommand {
if fetchResult.comments[index].totalReplies > 0 {
fetchResult.comments[index].totalReplies -= 1
}
} else if command is CreateReplyCommand {
fetchResult.comments[index].totalReplies += 1
}
}
}
fetchCreateDeleteCommentCommands().filter( { $0.self is RemoveCommentCommand } ) .forEach { (commands) in
fetchResult.comments = fetchResult.comments.filter({ $0.commentHandle != commands.comment.commentHandle })
}
fetchResult.comments = fetchResult.comments.map({ (comment) -> Comment in
var comment = comment
let commandsToApply = commands.filter { $0.comment.commentHandle == comment.commentHandle }
for command in commandsToApply {
command.apply(to: &comment)
}
return comment
})
func proccess(_ feed: inout CommentFetchResult, topicHandle: String) {
commands = fetchAllCommentCommands()
applyCreateDeleteCommands(to: &feed, topicHandle: topicHandle)
applyCommentActionCommands(to: &feed)
applyReplyCommands(to: &feed)
}
private func fetchCommentActionCommands() -> [CommentCommand] {
let request = CacheFetchRequest(resultType: OutgoingCommand.self,
predicate: PredicateBuilder().commentActionCommands(),
sortDescriptors: [Cache.createdAtSortDescriptor])
private func fetchAllCommentCommands() -> [CommentCommand] {
let request = CacheFetchRequest(
resultType: OutgoingCommand.self,
predicate: predicateBuilder.allCommentCommands(),
sortDescriptors: [Cache.createdAtSortDescriptor]
)
return cache.fetchOutgoing(with: request) as? [CommentCommand] ?? []
}
private func fetchCreateDeleteCommentCommands() -> [CommentCommand] {
let request = CacheFetchRequest(resultType: OutgoingCommand.self,
predicate: PredicateBuilder().createDeleteCommentCommands(),
sortDescriptors: [Cache.createdAtSortDescriptor])
return cache.fetchOutgoing(with: request) as? [CommentCommand] ?? []
private func applyCreateDeleteCommands(to feed: inout CommentFetchResult, topicHandle: String) {
for command in commands where !command.isActionCommand && command.comment.topicHandle == topicHandle {
command.apply(to: &feed)
}
}
private func fetchCreateDeleteReplyCommands() -> [ReplyCommand] {
private func applyCommentActionCommands(to feed: inout CommentFetchResult) {
for command in commands where command.isActionCommand {
command.apply(to: &feed)
}
}
private func applyReplyCommands(to feed: inout CommentFetchResult) {
for case let command in fetchCreateReplyCommands() {
command.apply(to: &feed)
}
}
private func fetchCreateReplyCommands() -> [CreateReplyCommand] {
let request = CacheFetchRequest(resultType: OutgoingCommand.self,
predicate: PredicateBuilder().createDeleteReplyCommands(),
predicate: PredicateBuilder().createReplyCommands(),
sortDescriptors: [Cache.createdAtSortDescriptor])
return cache.fetchOutgoing(with: request) as? [ReplyCommand] ?? []
return cache.fetchOutgoing(with: request) as? [CreateReplyCommand] ?? []
}
}

Просмотреть файл

@ -10,21 +10,6 @@ typealias CommentFetchResultHandler = ((CommentFetchResult) -> Void)
typealias CommentHandler = ((Comment) -> Void)
typealias CommentPostResultHandler = ((Comment) -> Void)
enum CommentsServiceError: Error {
case failedToFetch(message: String)
case failedToLike(message: String)
case failedToUnLike(message: String)
var message: String {
switch self {
case .failedToFetch(let message),
.failedToLike(let message),
.failedToUnLike(let message):
return message
}
}
}
protocol CommentServiceProtocol {
func fetchComments(topicHandle: String, cursor: String?, limit: Int32?, cachedResult: @escaping CommentFetchResultHandler, resultHandler: @escaping CommentFetchResultHandler)
func comment(commentHandle: String, cachedResult: @escaping CommentHandler, success: @escaping CommentHandler, failure: @escaping Failure)
@ -40,7 +25,7 @@ class CommentsService: BaseService, CommentServiceProtocol {
private let predicateBuilder = PredicateBuilder()
private let changesPublisher: Publisher
init(imagesService: ImagesServiceType, changesPublisher: Publisher = HandleChangesManager.shared) {
init(imagesService: ImagesServiceType, changesPublisher: Publisher = HandleChangesMulticast.shared) {
self.changesPublisher = changesPublisher
self.imagesService = imagesService
super.init()
@ -71,7 +56,7 @@ class CommentsService: BaseService, CommentServiceProtocol {
if let commentView = result?.body {
strongSelf.cache.cacheIncoming(commentView, for: builder.URLString)
success(strongSelf.convert(commentView: commentView))
success(Comment(commentView: commentView))
} else if strongSelf.errorHandler.canHandle(error) {
strongSelf.errorHandler.handle(error)
failure(APIError(error: error))
@ -99,7 +84,7 @@ class CommentsService: BaseService, CommentServiceProtocol {
private func cachedIncomingComment(key: String) -> Comment? {
let p = predicateBuilder.predicate(typeID: key)
let cachedCommentView = cache.firstIncoming(ofType: CommentView.self, predicate: p, sortDescriptors: nil)
return cachedCommentView != nil ? convert(commentView: cachedCommentView!) : nil
return cachedCommentView != nil ? Comment(commentView: cachedCommentView!) : nil
}
func fetchComments(topicHandle: String,
@ -107,67 +92,65 @@ class CommentsService: BaseService, CommentServiceProtocol {
limit: Int32? = nil,
cachedResult: @escaping CommentFetchResultHandler,
resultHandler: @escaping CommentFetchResultHandler) {
let builder = CommentsAPI.topicCommentsGetTopicCommentsWithRequestBuilder(
topicHandle: topicHandle,
authorization: authorization,
cursor: cursor, limit: limit
cursor: cursor,
limit: limit
)
var result = CommentFetchResult()
fetchCachedFeed(topicHandle: topicHandle, url: builder.URLString, cachedResult: cachedResult)
let fetchOutgoingRequest = CacheFetchRequest(resultType: OutgoingCommand.self,
predicate: predicateBuilder.allCreateCommentCommands(for: topicHandle),
sortDescriptors: [Cache.createdAtSortDescriptor])
builder.execute { response, error in
let feed = self.onCommentsFetched(topicHandle: topicHandle,
cursor: cursor,
url: builder.URLString,
response: response?.body,
error: error)
resultHandler(feed)
}
}
private func fetchCachedFeed(topicHandle: String, url: String, cachedResult: @escaping CommentFetchResultHandler) {
let incomingFeed = cache.firstIncoming(ofType: FeedResponseCommentView.self,
predicate: predicateBuilder.predicate(handle: url),
sortDescriptors: nil)
let commands = cache.fetchOutgoing(with: fetchOutgoingRequest)
let incomingFeed = self.cache.firstIncoming(ofType: FeedResponseCommentView.self,
predicate: predicateBuilder.predicate(handle: builder.URLString),
sortDescriptors: nil)
var feed = CommentFetchResult()
feed.comments = incomingFeed?.data?.map(Comment.init) ?? []
processor.proccess(&feed, topicHandle: topicHandle)
cachedResult(feed)
}
func onCommentsFetched(topicHandle: String,
cursor: String?,
url: String,
response: FeedResponseCommentView?,
error: ErrorResponse?) -> CommentFetchResult {
let incomingComments = incomingFeed?.data?.map(self.convert(commentView:)) ?? []
let typeID = "fetch_commens-\(topicHandle)"
let outgoingComments = commands.flatMap { ($0 as? CreateCommentCommand)?.comment }
result.comments = outgoingComments + incomingComments
result.cursor = incomingFeed?.cursor
processor.proccess(&result)
cachedResult(result)
guard isNetworkReachable else {
result.error = CommentsServiceError.failedToFetch(message: L10n.Error.unknown)
resultHandler(result)
return
if cursor == nil {
cache.deleteIncoming(with: predicateBuilder.predicate(typeID: typeID))
}
builder.execute { [weak self, predicateBuilder] (response, error) in
result = CommentFetchResult()
guard let strongSelf = self else {
return
}
let typeID = "fetch_commens-\(topicHandle)"
if cursor == nil {
strongSelf.cache.deleteIncoming(with: predicateBuilder.predicate(typeID: typeID))
}
if let body = response?.body, let data = body.data {
body.handle = builder.URLString
strongSelf.cache.cacheIncoming(body, for: typeID)
result.comments = strongSelf.convert(data: data)
result.cursor = body.cursor
} else if strongSelf.errorHandler.canHandle(error) {
strongSelf.errorHandler.handle(error)
} else if let error = error {
result.error = CommentsServiceError.failedToFetch(message: error.localizedDescription)
}
resultHandler(result)
var feed = CommentFetchResult()
if let response = response, let data = response.data {
response.handle = url
cache.cacheIncoming(response, for: typeID)
feed.comments = data.map(Comment.init)
feed.cursor = response.cursor
} else if errorHandler.canHandle(error) {
errorHandler.handle(error)
} else if let error = error {
feed.error = APIError(error: error)
}
processor.proccess(&feed, topicHandle: topicHandle)
return feed
}
func postComment(comment: Comment,
@ -195,12 +178,15 @@ class CommentsService: BaseService, CommentServiceProtocol {
}
}
private func execute(command: CreateCommentCommand,
resultHandler: @escaping CommentPostResultHandler,
failure: @escaping Failure) {
func execute(command: CreateCommentCommand,
resultHandler: @escaping CommentPostResultHandler,
failure: @escaping Failure) {
cache.cacheOutgoing(command)
resultHandler(command.comment)
let isCached = cache.isCached(command)
if !isCached {
cache.cacheOutgoing(command)
resultHandler(command.comment)
}
CommentsAPI.topicCommentsPostComment(
topicHandle: command.comment.topicHandle,
@ -208,9 +194,17 @@ class CommentsService: BaseService, CommentServiceProtocol {
authorization: authorization
) { response, error in
if let newHandle = response?.commentHandle {
self.onCommentPosted(oldCommand: command, newHandle: newHandle)
if !isCached {
self.onCommentPosted(oldCommand: command, newHandle: newHandle)
} else {
command.comment.commentHandle = newHandle
resultHandler(command.comment)
}
} else if self.errorHandler.canHandle(error) {
self.errorHandler.handle(error)
failure(error ?? APIError.unknown)
} else if let error = error {
failure(error)
}
}
}
@ -225,9 +219,14 @@ class CommentsService: BaseService, CommentServiceProtocol {
}
func delete(comment: Comment, completion: @escaping (Result<Void>) -> Void) {
completion(.success())
let command = RemoveCommentCommand(comment: comment)
let isCached = cache.isCached(command)
if !isCached {
completion(.success())
}
let hasDeletedInverseCommand = cache.deleteInverseCommand(for: command)
guard !hasDeletedInverseCommand else {
@ -243,24 +242,23 @@ class CommentsService: BaseService, CommentServiceProtocol {
self.errorHandler.handle(error)
completion(.failure(APIError(error: error)))
} else if error == nil {
let p = self.predicateBuilder.predicate(for: command)
self.cache.deleteOutgoing(with: p)
if !isCached {
self.onCommentDeleted(command: command)
} else {
completion(.success())
}
}
}
}
private func convert(data: [CommentView]) -> [Comment] {
return data.map(Comment.init(commentView:))
}
private func convert(commentView: CommentView) -> Comment {
return Comment(commentView: commentView)
private func onCommentDeleted(command: RemoveCommentCommand) {
cache.deleteOutgoing(with: predicateBuilder.predicate(for: command))
}
}
struct CommentFetchResult {
var comments = [Comment]()
var error: CommentsServiceError?
var error: Error?
var cursor: String?
}

Просмотреть файл

@ -44,25 +44,35 @@ class ImagesService: BaseService, ImagesServiceType {
imageType: ImagesAPI.ImageType_imagesPostImage,
completion: @escaping (Result<String>) -> Void) {
cacheCommand(command)
completion(.success(command.photo.uid))
cacheCommandAndComplete(command, completion: completion)
uploadImageData(command.photo.image?.compressed(), imageType: imageType) { result in
if let handle = result.value {
let photo = Photo(uid: handle, image: command.photo.image)
self.imageCache.store(photo: photo)
self.deleteCommand(command)
} else if self.errorHandler.canHandle(result.error) {
self.errorHandler.handle(result.error)
}
uploadImageData(command.photo.image?.compressed(), imageType: imageType) { [weak self] result in
self?.onImageDataUploaded(command: command, result: result, completion: completion)
}
}
private func cacheCommand(_ command: ImageCommand) {
func cacheCommandAndComplete(_ command: ImageCommand, completion: @escaping (Result<String>) -> Void) {
cache.cacheOutgoing(command)
imageCache.store(photo: command.photo)
}
private func onImageDataUploaded(command: ImageCommand, result: Result<String>, completion: @escaping (Result<String>) -> Void) {
if let handle = result.value {
let photo = Photo(uid: handle, image: command.photo.image)
self.imageCache.store(photo: photo)
self.deleteCommand(command)
onImageDataUploadSuccess(newHandle: handle, completion: completion)
} else if self.errorHandler.canHandle(result.error) {
self.errorHandler.handle(result.error)
} else {
completion(result)
}
}
func onImageDataUploadSuccess(newHandle: String, completion: @escaping (Result<String>) -> Void) {
}
private func deleteCommand(_ command: ImageCommand) {
cache.deleteOutgoing(with: PredicateBuilder().predicate(for: command))
imageCache.remove(photo: command.photo)
@ -100,9 +110,9 @@ class ImagesService: BaseService, ImagesServiceType {
}
}
private func uploadImageData(_ data: Data?,
imageType: ImagesAPI.ImageType_imagesPostImage,
completion: @escaping (Result<String>) -> Void) {
func uploadImageData(_ data: Data?,
imageType: ImagesAPI.ImageType_imagesPostImage,
completion: @escaping (Result<String>) -> Void) {
uploadImageData(data, imageType: imageType, authorization: authorization, completion: completion)
}

Просмотреть файл

@ -56,11 +56,12 @@ class LikesService: BaseService, LikesServiceProtocol {
private var requestExecutor: UsersFeedRequestExecutor!
private var outgoingActionsExecutor: AtomicOutgoingCommandsExecutor!
init(ExecutorProvider provider: CacheRequestExecutorProviderType.Type = CacheRequestExecutorProvider.self) {
init(executorProvider provider: CacheRequestExecutorProviderType.Type = CacheRequestExecutorProvider.self) {
super.init()
requestExecutor = provider.makeUsersFeedExecutor(for: self)
outgoingActionsExecutor = provider.makeAtomicOutgoingCommandsExecutor(for: self)
}
func postLike(post: Post, completion: @escaping CompletionHandler) {
let builder = LikesAPI.topicLikesPostLikeWithRequestBuilder(topicHandle: post.topicHandle, authorization: authorization)
let command = LikeTopicCommand(topic: post)

Просмотреть файл

@ -7,47 +7,45 @@
import Foundation
protocol RepliesProcessorType {
func proccess(_ fetchResult: inout RepliesFetchResult)
func proccess(_ fetchResult: inout RepliesFetchResult, commentHandle: String)
}
class RepliesProcessor: RepliesProcessorType {
let cache: CacheType
private let cache: CacheType
private let predicateBuilder = PredicateBuilder()
private var commands: [ReplyCommand] = []
required init(cache: CacheType) {
init(cache: CacheType) {
self.cache = cache
}
func proccess(_ fetchResult: inout RepliesFetchResult) {
let commands = fetchCreateDeleteReplyCommands() + fetchReplyActionCommands()
fetchCreateDeleteReplyCommands().filter( { $0.self is RemoveReplyCommand } ) .forEach { (commands) in
fetchResult.replies = fetchResult.replies.filter({ $0.replyHandle != commands.reply.replyHandle })
func proccess(_ feed: inout RepliesFetchResult, commentHandle: String) {
commands = fetchAllReplyCommands()
applyCreateDeleteCommands(to: &feed, commentHandle: commentHandle)
applyReplyActionCommands(to: &feed)
}
private func fetchAllReplyCommands() -> [ReplyCommand] {
let request = CacheFetchRequest(
resultType: OutgoingCommand.self,
predicate: predicateBuilder.allReplyCommands(),
sortDescriptors: [Cache.createdAtSortDescriptor]
)
return cache.fetchOutgoing(with: request) as? [ReplyCommand] ?? []
}
private func applyCreateDeleteCommands(to feed: inout RepliesFetchResult, commentHandle: String) {
for command in commands where !command.isActionCommand && command.reply.commentHandle == commentHandle {
command.apply(to: &feed)
}
fetchResult.replies = fetchResult.replies.map({ (reply) -> Reply in
var reply = reply
let commandsToApply = commands.filter { $0.reply.replyHandle == reply.commentHandle }
for command in commandsToApply {
command.apply(to: &reply)
}
return reply
})
}
private func fetchReplyActionCommands() -> [ReplyCommand] {
let request = CacheFetchRequest(resultType: OutgoingCommand.self,
predicate: PredicateBuilder().replyActionCommands(),
sortDescriptors: [Cache.createdAtSortDescriptor])
return cache.fetchOutgoing(with: request) as? [ReplyCommand] ?? []
}
private func fetchCreateDeleteReplyCommands() -> [ReplyCommand] {
let request = CacheFetchRequest(resultType: OutgoingCommand.self,
predicate: PredicateBuilder().createDeleteReplyCommands(),
sortDescriptors: [Cache.createdAtSortDescriptor])
return cache.fetchOutgoing(with: request) as? [ReplyCommand] ?? []
private func applyReplyActionCommands(to feed: inout RepliesFetchResult) {
for command in commands where command.isActionCommand {
command.apply(to: &feed)
}
}
}

Просмотреть файл

@ -28,7 +28,7 @@ class RepliesService: BaseService, RepliesServiceProtcol {
private var processor: RepliesProcessorType!
private let changesPublisher: Publisher
init(changesPublisher: Publisher = HandleChangesManager.shared) {
init(changesPublisher: Publisher = HandleChangesMulticast.shared) {
self.changesPublisher = changesPublisher
super.init()
processor = RepliesProcessor(cache: cache)
@ -47,70 +47,59 @@ class RepliesService: BaseService, RepliesServiceProtcol {
limit: Int32(limit)
)
var result = RepliesFetchResult()
let fetchOutgoingRequest = CacheFetchRequest(resultType: OutgoingCommand.self,
predicate: PredicateBuilder().allCreateReplyCommands(),
sortDescriptors: [Cache.createdAtSortDescriptor])
let commands = cache.fetchOutgoing(with: fetchOutgoingRequest)
let outgoingReplies = commands.flatMap { ($0 as? CreateReplyCommand)?.reply }
let incomingFeed = self.cache.firstIncoming(ofType: FeedResponseReplyView.self,
predicate: PredicateBuilder().predicate(handle: builder.URLString),
sortDescriptors: nil)
let incomingReplies = incomingFeed?.data?.map(self.convert(replyView:)) ?? []
result.replies = outgoingReplies + incomingReplies
result.cursor = incomingFeed?.cursor
processor.proccess(&result)
cachedResult(result)
guard isNetworkReachable else {
result.error = APIError.unknown
resultHandler(result)
return
}
builder.execute { [weak self] (response, error) in
guard let strongSelf = self else {
return
}
let typeID = "fetch_replies-\(commentHandle)"
if cursor == nil {
strongSelf.cache.deleteIncoming(with: PredicateBuilder().predicate(typeID: typeID))
}
fetchCachedFeed(commentHandle: commentHandle, url: builder.URLString, cachedResult: cachedResult)
if let body = response?.body, let data = body.data {
body.handle = builder.URLString
strongSelf.cache.cacheIncoming(body, for: typeID)
result.replies = strongSelf.convert(data: data)
result.cursor = body.cursor
} else if strongSelf.errorHandler.canHandle(error) {
strongSelf.errorHandler.handle(error)
return
} else {
guard let unwrappedError = error else {
resultHandler(result)
return
}
if unwrappedError.statusCode >= Constants.HTTPStatusCodes.InternalServerError.rawValue {
resultHandler(result)
} else {
result.error = APIError(error: error)
}
}
resultHandler(result)
builder.execute { response, error in
let feed = self.onRepliesFetched(commentHandle: commentHandle,
cursor: cursor,
url: builder.URLString,
response: response?.body,
error: error)
resultHandler(feed)
}
}
func onRepliesFetched(commentHandle: String,
cursor: String?,
url: String,
response: FeedResponseReplyView?,
error: ErrorResponse?) -> RepliesFetchResult {
let typeID = "fetch_replies-\(commentHandle)"
if cursor == nil {
cache.deleteIncoming(with: PredicateBuilder().predicate(typeID: typeID))
}
var feed = RepliesFetchResult()
if let response = response, let data = response.data {
response.handle = url
cache.cacheIncoming(response, for: typeID)
feed.replies = data.map(Reply.init)
feed.cursor = response.cursor
} else if errorHandler.canHandle(error) {
errorHandler.handle(error)
} else if let error = error {
feed.error = APIError(error: error)
}
processor.proccess(&feed, commentHandle: commentHandle)
return feed
}
private func fetchCachedFeed(commentHandle: String, url: String, cachedResult: @escaping RepliesFetchResultHandler) {
let incomingFeed = cache.firstIncoming(ofType: FeedResponseReplyView.self,
predicate: PredicateBuilder().predicate(handle: url),
sortDescriptors: nil)
var feed = RepliesFetchResult()
feed.replies = incomingFeed?.data?.map(Reply.init) ?? []
processor.proccess(&feed, commentHandle: commentHandle)
cachedResult(feed)
}
func reply(replyHandle: String,
cachedResult: @escaping ReplyHandler,
success: @escaping ReplyHandler,
@ -166,9 +155,13 @@ class RepliesService: BaseService, RepliesServiceProtcol {
}
func postReply(reply: Reply, success: @escaping PostReplyResultHandler, failure: @escaping (APIError) -> (Void)) {
let replyCommand = CreateReplyCommand(reply: reply)
cache.cacheOutgoing(replyCommand)
success(PostReplyResponse(reply: reply))
let command = CreateReplyCommand(reply: reply)
let isCached = cache.isCached(command)
if !isCached {
cache.cacheOutgoing(command)
success(PostReplyResponse(reply: reply))
}
let request = PostReplyRequest()
request.text = reply.text
@ -181,8 +174,13 @@ class RepliesService: BaseService, RepliesServiceProtcol {
if self.errorHandler.canHandle(error) {
self.errorHandler.handle(error)
failure(APIError(error: error))
} else if let response = response {
self.onReplyPosted(oldCommand: replyCommand, response: response)
} else if let response = response, let newHandle = response.replyHandle {
if !isCached {
self.onReplyPosted(oldCommand: command, response: response)
} else {
command.reply.replyHandle = newHandle
success(PostReplyResponse(reply: reply))
}
}
}
}
@ -197,9 +195,15 @@ class RepliesService: BaseService, RepliesServiceProtcol {
changesPublisher.notify(ReplyUpdateHint(oldHandle: oldHandle, newHandle: newHandle))
}
func delete(reply: Reply, completion: @escaping ((Result<Void>) -> Void)) {
func delete(reply: Reply, completion: @escaping (Result<Void>) -> Void) {
let command = RemoveReplyCommand(reply: reply)
let isCached = cache.isCached(command)
if !isCached {
completion(.success())
}
let hasDeletedInverseCommand = cache.deleteInverseCommand(for: command)
guard !hasDeletedInverseCommand else {
@ -215,13 +219,19 @@ class RepliesService: BaseService, RepliesServiceProtcol {
self.errorHandler.handle(error)
completion(.failure(APIError(error: error)))
} else if error == nil {
let p = PredicateBuilder().predicate(for: command)
self.cache.deleteOutgoing(with: p)
completion(.success())
if !isCached {
self.onReplyDeleted(command: command)
} else {
completion(.success())
}
}
}
}
private func onReplyDeleted(command: RemoveReplyCommand) {
self.cache.deleteOutgoing(with: PredicateBuilder().predicate(for: command))
}
private func convert(data: [ReplyView]) -> [Reply] {
return data.map(Reply.init(replyView:))
}

Просмотреть файл

@ -48,7 +48,7 @@ class SocialService: BaseService, SocialServiceType {
fileprivate let executorProvider: CacheRequestExecutorProviderType.Type
init(ExecutorProvider provider: CacheRequestExecutorProviderType.Type = CacheRequestExecutorProvider.self) {
init(executorProvider provider: CacheRequestExecutorProviderType.Type = CacheRequestExecutorProvider.self) {
self.executorProvider = provider
super.init()
usersFeedExecutor = provider.makeUsersFeedExecutor(for: self)

Просмотреть файл

@ -96,7 +96,7 @@ class TopicService: BaseService, PostServiceProtocol {
init(imagesService: ImagesServiceType,
ExecutorProvider: CacheRequestExecutorProviderType.Type = CacheRequestExecutorProvider.self,
predicateBuilder: PredicateBuilder = PredicateBuilder(),
changesPublisher: Publisher = HandleChangesManager.shared) {
changesPublisher: Publisher = HandleChangesMulticast.shared) {
self.imagesService = imagesService
self.executorProvider = ExecutorProvider

Просмотреть файл

@ -6,31 +6,15 @@
import Foundation
final class DaemonsController: Daemon {
private let networkTracker: NetworkStatusMulticast
private let cache: CacheType
private lazy var outgoingCacheDaemon: OutgoingCommandsUploader = { [unowned self] in
let queue = OperationQueue()
queue.name = "OutgoingCommandsUploader-executionQueue"
queue.qualityOfService = .background
queue.maxConcurrentOperationCount = 1
let strategy = OutgoingCommandsUploadStrategy(cache: self.cache,
operationsBuilderType: OutgoingCommandOperationsBuilder(),
executionQueue: queue)
return OutgoingCommandsUploader(networkTracker: self.networkTracker,
uploadStrategy: strategy,
jsonDecoderType: Decoders.self)
}()
private lazy var daemons: [Daemon] = { [unowned self] in
return [self.outgoingCacheDaemon]
return [self.factory.makeOutgoingCacheDaemon(), self.factory.makeHandleUpdaterDaemon()]
}()
init(networkTracker: NetworkStatusMulticast, cache: CacheType) {
self.networkTracker = networkTracker
self.cache = cache
private let factory: DaemonsFactory
init(factory: DaemonsFactory) {
self.factory = factory
}
func start() {

Просмотреть файл

@ -0,0 +1,11 @@
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
//
import Foundation
protocol DaemonsFactory {
func makeOutgoingCacheDaemon() -> Daemon
func makeHandleUpdaterDaemon() -> Daemon
}

Просмотреть файл

@ -0,0 +1,36 @@
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
//
import Foundation
final class DaemonsFactoryImpl: DaemonsFactory {
private let cache: CacheType
private let networkTracker: NetworkTrackerType
init(cache: CacheType, networkTracker: NetworkTrackerType) {
self.cache = cache
self.networkTracker = networkTracker
}
func makeOutgoingCacheDaemon() -> Daemon {
let queue = OperationQueue()
queue.name = "OutgoingCommandsUploader-executionQueue"
queue.qualityOfService = .background
queue.maxConcurrentOperationCount = 1
let strategy = OutgoingCommandsUploadStrategy(cache: self.cache,
operationsBuilderType: OutgoingCommandOperationsBuilder(),
executionQueue: queue)
return OutgoingCommandsUploader(networkTracker: self.networkTracker,
uploadStrategy: strategy,
jsonDecoderType: Decoders.self)
}
func makeHandleUpdaterDaemon() -> Daemon {
return HandleUpdateDaemon()
}
}

Просмотреть файл

@ -68,7 +68,8 @@ final class SocialPlusServices: SocialPlusServicesType {
}
func getDaemonsController(cache: CacheType) -> Daemon {
return DaemonsController(networkTracker: networkTracker, cache: cache)
let factory = DaemonsFactoryImpl(cache: cache, networkTracker: networkTracker)
return DaemonsController(factory: factory)
}
func getStartupCommands(launchArgs: LaunchArguments) -> [Command] {

Просмотреть файл

@ -54,7 +54,7 @@ class CommentRepliesPresenter: CommentRepliesModuleInput, CommentRepliesViewOutp
init(pageSize: Int,
actionStrategy: AuthorizedActionStrategy,
handlePublisher: Publisher = HandleChangesManager.shared) {
handlePublisher: Publisher = HandleChangesMulticast.shared) {
self.pageSize = pageSize
self.actionStrategy = actionStrategy
handlePublisher.subscribe(self)

Просмотреть файл

@ -40,7 +40,7 @@ class FeedModuleInteractor: FeedModuleInteractorInput {
var searchService: SearchServiceType!
weak var userHolder: UserHolder? = SocialPlus.shared
init(handleChangesPublisher: Publisher = HandleChangesManager.shared) {
init(handleChangesPublisher: Publisher = HandleChangesMulticast.shared) {
handleChangesPublisher.subscribe(self)
}

Просмотреть файл

@ -8,7 +8,7 @@ import Foundation
protocol PostDetailInteractorOutput: class {
func didFetch(comments: [Comment], cursor: String?)
func didFetchMore(comments: [Comment], cursor: String?)
func didFail(error: CommentsServiceError)
func didFail(error: Error)
func commentDidPost(comment: Comment)
func commentPostFailed(error: Error)
}

Просмотреть файл

@ -35,7 +35,7 @@ class PostDetailPresenter: PostDetailViewOutput, PostDetailInteractorOutput, Pos
init(pageSize: Int,
actionStrategy: AuthorizedActionStrategy,
handleChangesPublisher: Publisher = HandleChangesManager.shared) {
handleChangesPublisher: Publisher = HandleChangesMulticast.shared) {
self.pageSize = pageSize
self.actionStrategy = actionStrategy
handleChangesPublisher.subscribe(self)
@ -94,7 +94,7 @@ class PostDetailPresenter: PostDetailViewOutput, PostDetailInteractorOutput, Pos
}
}
func didFail(error: CommentsServiceError) {
func didFail(error: Error) {
loadMoreCellViewModel.cellHeight = 0.1
loadMoreCellViewModel.stopLoading()
view.updateLoadingCell()