diff --git a/Cartfile b/Cartfile index 480add23b..ed5c5ee9a 100644 --- a/Cartfile +++ b/Cartfile @@ -1,7 +1,2 @@ -# Avoid PromiseKit 5 for now. -# From the maintainer: -# > PromiseKit 5 has been released, but is not yet fully documented, -# > so we advise sticking with version 4 for the time being. -github "mxcl/PromiseKit" ~> 4.0 github "TheLevelUp/ZXingObjC" diff --git a/Cartfile.resolved b/Cartfile.resolved index c2bc915b5..a22907f04 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1 @@ github "TheLevelUp/ZXingObjC" "3.2.2" -github "mxcl/PromiseKit" "4.5.0" diff --git a/Carthage b/Carthage index cf52d8e96..b316e4e26 160000 --- a/Carthage +++ b/Carthage @@ -1 +1 @@ -Subproject commit cf52d8e963e990d2a386c17266ec1e2d7810f317 +Subproject commit b316e4e26362bb86605cd3743d513ea8a57e66bf diff --git a/Podfile.lock b/Podfile.lock index bcbb057b7..2cce25b58 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -36,6 +36,17 @@ PODS: - Mantle (2.1.0): - Mantle/extobjc (= 2.1.0) - Mantle/extobjc (2.1.0) + - PromiseKit (4.5.2): + - PromiseKit/Foundation (= 4.5.2) + - PromiseKit/QuartzCore (= 4.5.2) + - PromiseKit/UIKit (= 4.5.2) + - PromiseKit/CorePromise (4.5.2) + - PromiseKit/Foundation (4.5.2): + - PromiseKit/CorePromise + - PromiseKit/QuartzCore (4.5.2): + - PromiseKit/CorePromise + - PromiseKit/UIKit (4.5.2): + - PromiseKit/CorePromise - ProtocolBuffers (1.9.11) - PureLayout (3.0.2) - Reachability (3.2) @@ -48,10 +59,10 @@ PODS: - GRKOpenSSLFramework - libPhoneNumber-iOS - Mantle + - PromiseKit (~> 4.0) - Reachability - SAMKeychain - SocketRocket - - TwistedOakCollapsingFutures - YapDatabase/SQLCipher - SocketRocket (0.5.1) - SQLCipher (3.4.2): @@ -60,9 +71,6 @@ PODS: - SQLCipher/standard (3.4.2): - SQLCipher/common - SSZipArchive (2.1.2) - - TwistedOakCollapsingFutures (1.0.0): - - UnionFind (~> 1.0) - - UnionFind (1.0.1) - YapDatabase/SQLCipher (3.0.2): - YapDatabase/SQLCipher/Core (= 3.0.2) - YapDatabase/SQLCipher/Extensions (= 3.0.2) @@ -152,13 +160,12 @@ SPEC REPOS: - JSQSystemSoundPlayer - libPhoneNumber-iOS - Mantle + - PromiseKit - ProtocolBuffers - PureLayout - Reachability - SAMKeychain - SSZipArchive - - TwistedOakCollapsingFutures - - UnionFind - YYImage EXTERNAL SOURCES: @@ -224,16 +231,15 @@ SPEC CHECKSUMS: JSQSystemSoundPlayer: c5850e77a4363ffd374cd851154b9af93264ed8d libPhoneNumber-iOS: e444379ac18bbfbdefad571da735b2cd7e096caa Mantle: 2fa750afa478cd625a94230fbf1c13462f29395b + PromiseKit: 743e497a5f505a470d3bbbf4ce0663c1268af0a4 ProtocolBuffers: d509225eb2ea43d9582a59e94348fcf86e2abd65 PureLayout: 4d550abe49a94f24c2808b9b95db9131685fe4cd Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c - SignalServiceKit: 3a65a39b6671c290e6258db78002527e085842ad + SignalServiceKit: eaaf6404e61fc05c71f91246327e2d487e0e1833 SocketRocket: dbb1554b8fc288ef8ef370d6285aeca7361be31e SQLCipher: f9fcf29b2e59ced7defc2a2bdd0ebe79b40d4990 SSZipArchive: d4009d2ce5520a421f231fd97028cc0e2667eed8 - TwistedOakCollapsingFutures: f359b90f203e9ab13dfb92c9ff41842a7fe1cd0c - UnionFind: c33be5adb12983981d6e827ea94fc7f9e370f52d YapDatabase: 299a32de9d350d37a9ac5b0532609d87d5d2a5de YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54 diff --git a/Pods b/Pods index 0a0ef07e7..2d600357b 160000 --- a/Pods +++ b/Pods @@ -1 +1 @@ -Subproject commit 0a0ef07e7afa563b97ea03d5978b78b72a50ecf6 +Subproject commit 2d600357bc067932e27c99ada1822df1e85449b7 diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 8e1735d36..54e2f694e 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -113,7 +113,6 @@ 346129C71FD2072E00532771 /* NSString+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = 346129C01FD2072C00532771 /* NSString+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 346129C81FD2072E00532771 /* NSAttributedString+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 346129C11FD2072D00532771 /* NSAttributedString+OWS.m */; }; 346129C91FD2072E00532771 /* NSString+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 346129C21FD2072D00532771 /* NSString+OWS.m */; }; - 346129CB1FD2072E00532771 /* Promise+retainUntilComplete.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346129C41FD2072D00532771 /* Promise+retainUntilComplete.swift */; }; 346129CC1FD2072E00532771 /* NSAttributedString+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = 346129C51FD2072D00532771 /* NSAttributedString+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 346129D01FD207F300532771 /* OWSAlerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346129CF1FD207F200532771 /* OWSAlerts.swift */; }; 346129D21FD2085A00532771 /* CommonStrings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346129D11FD2085A00532771 /* CommonStrings.swift */; }; @@ -735,7 +734,6 @@ 346129C01FD2072C00532771 /* NSString+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+OWS.h"; sourceTree = ""; }; 346129C11FD2072D00532771 /* NSAttributedString+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSAttributedString+OWS.m"; sourceTree = ""; }; 346129C21FD2072D00532771 /* NSString+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+OWS.m"; sourceTree = ""; }; - 346129C41FD2072D00532771 /* Promise+retainUntilComplete.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Promise+retainUntilComplete.swift"; sourceTree = ""; }; 346129C51FD2072D00532771 /* NSAttributedString+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSAttributedString+OWS.h"; sourceTree = ""; }; 346129CF1FD207F200532771 /* OWSAlerts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSAlerts.swift; sourceTree = ""; }; 346129D11FD2085A00532771 /* CommonStrings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommonStrings.swift; sourceTree = ""; }; @@ -1507,7 +1505,6 @@ 346129C11FD2072D00532771 /* NSAttributedString+OWS.m */, 346129C01FD2072C00532771 /* NSString+OWS.h */, 346129C21FD2072D00532771 /* NSString+OWS.m */, - 346129C41FD2072D00532771 /* Promise+retainUntilComplete.swift */, 452D1AF220810B6F00A67F7F /* String+OWS.swift */, 34C82E4F20F8E1F000E9688D /* Theme.h */, 34C82E5020F8E1F100E9688D /* Theme.m */, @@ -2892,7 +2889,6 @@ files = ( ); inputPaths = ( - "$(SRCROOT)/Carthage/Build/iOS/PromiseKit.framework", "$(SRCROOT)/Carthage/Build/iOS/WebRTC.framework", "$(SRCROOT)/Carthage/Build/iOS/ZXingObjC.framework", ); @@ -2909,7 +2905,6 @@ files = ( ); inputPaths = ( - "$(SRCROOT)/Carthage/Build/iOS/PromiseKit.framework", "$(SRCROOT)/Carthage/Build/iOS/WebRTC.framework", "$(SRCROOT)/Carthage/Build/iOS/ZXingObjC.framework", ); @@ -2951,6 +2946,7 @@ "${BUILT_PRODUCTS_DIR}/JSQMessagesViewController/JSQMessagesViewController.framework", "${BUILT_PRODUCTS_DIR}/JSQSystemSoundPlayer/JSQSystemSoundPlayer.framework", "${BUILT_PRODUCTS_DIR}/Mantle/Mantle.framework", + "${BUILT_PRODUCTS_DIR}/PromiseKit/PromiseKit.framework", "${BUILT_PRODUCTS_DIR}/ProtocolBuffers/ProtocolBuffers.framework", "${BUILT_PRODUCTS_DIR}/PureLayout/PureLayout.framework", "${BUILT_PRODUCTS_DIR}/Reachability/Reachability.framework", @@ -2959,8 +2955,6 @@ "${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework", "${BUILT_PRODUCTS_DIR}/SignalServiceKit/SignalServiceKit.framework", "${BUILT_PRODUCTS_DIR}/SocketRocket/SocketRocket.framework", - "${BUILT_PRODUCTS_DIR}/TwistedOakCollapsingFutures/TwistedOakCollapsingFutures.framework", - "${BUILT_PRODUCTS_DIR}/UnionFind/UnionFind.framework", "${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework", "${BUILT_PRODUCTS_DIR}/YapDatabase/YapDatabase.framework", "${BUILT_PRODUCTS_DIR}/libPhoneNumber-iOS/libPhoneNumber_iOS.framework", @@ -2977,6 +2971,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/JSQMessagesViewController.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/JSQSystemSoundPlayer.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Mantle.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PromiseKit.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ProtocolBuffers.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PureLayout.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reachability.framework", @@ -2985,8 +2980,6 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SSZipArchive.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SignalServiceKit.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SocketRocket.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TwistedOakCollapsingFutures.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/UnionFind.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYImage.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YapDatabase.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libPhoneNumber_iOS.framework", @@ -3030,6 +3023,7 @@ "${BUILT_PRODUCTS_DIR}/JSQMessagesViewController/JSQMessagesViewController.framework", "${BUILT_PRODUCTS_DIR}/JSQSystemSoundPlayer/JSQSystemSoundPlayer.framework", "${BUILT_PRODUCTS_DIR}/Mantle/Mantle.framework", + "${BUILT_PRODUCTS_DIR}/PromiseKit/PromiseKit.framework", "${BUILT_PRODUCTS_DIR}/ProtocolBuffers/ProtocolBuffers.framework", "${BUILT_PRODUCTS_DIR}/PureLayout/PureLayout.framework", "${BUILT_PRODUCTS_DIR}/Reachability/Reachability.framework", @@ -3037,8 +3031,6 @@ "${BUILT_PRODUCTS_DIR}/SQLCipher/SQLCipher.framework", "${BUILT_PRODUCTS_DIR}/SignalServiceKit/SignalServiceKit.framework", "${BUILT_PRODUCTS_DIR}/SocketRocket/SocketRocket.framework", - "${BUILT_PRODUCTS_DIR}/TwistedOakCollapsingFutures/TwistedOakCollapsingFutures.framework", - "${BUILT_PRODUCTS_DIR}/UnionFind/UnionFind.framework", "${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework", "${BUILT_PRODUCTS_DIR}/YapDatabase/YapDatabase.framework", "${BUILT_PRODUCTS_DIR}/libPhoneNumber-iOS/libPhoneNumber_iOS.framework", @@ -3054,6 +3046,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/JSQMessagesViewController.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/JSQSystemSoundPlayer.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Mantle.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PromiseKit.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ProtocolBuffers.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PureLayout.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reachability.framework", @@ -3061,8 +3054,6 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SQLCipher.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SignalServiceKit.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SocketRocket.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TwistedOakCollapsingFutures.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/UnionFind.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYImage.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YapDatabase.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libPhoneNumber_iOS.framework", @@ -3175,7 +3166,6 @@ 346129D51FD20ADC00532771 /* UIViewController+OWS.m in Sources */, 451F8A431FD714FE005CB9DA /* AvatarImageView.swift in Sources */, 346129C91FD2072E00532771 /* NSString+OWS.m in Sources */, - 346129CB1FD2072E00532771 /* Promise+retainUntilComplete.swift in Sources */, 347850691FD9B78A007B8332 /* AppSetup.m in Sources */, 346129FF1FD5F31400532771 /* OWS103EnableVideoCalling.m in Sources */, 346129E31FD5C0BE00532771 /* VersionMigrations.m in Sources */, diff --git a/Signal/src/network/GiphyDownloader.swift b/Signal/src/network/GiphyDownloader.swift index 5c423a67c..9461a89a4 100644 --- a/Signal/src/network/GiphyDownloader.swift +++ b/Signal/src/network/GiphyDownloader.swift @@ -617,7 +617,7 @@ extension URLSessionTask { request.httpShouldUsePipelining = true let rangeHeaderValue = "bytes=\(assetSegment.segmentStart)-\(assetSegment.segmentStart + assetSegment.segmentLength - 1)" request.addValue(rangeHeaderValue, forHTTPHeaderField: "Range") - let task = giphyDownloadSession.dataTask(with: request) + let task: URLSessionDataTask = giphyDownloadSession.dataTask(with: request) task.assetRequest = assetRequest task.assetSegment = assetSegment assetSegment.task = task diff --git a/SignalServiceKit.podspec b/SignalServiceKit.podspec index 461ebc290..b4035c791 100644 --- a/SignalServiceKit.podspec +++ b/SignalServiceKit.podspec @@ -48,6 +48,12 @@ An Objective-C library for communicating with the Signal messaging service. s.dependency 'libPhoneNumber-iOS' s.dependency 'GRKOpenSSLFramework' s.dependency 'SAMKeychain' - s.dependency 'TwistedOakCollapsingFutures' s.dependency 'Reachability' + + # Avoid PromiseKit 5/6 for now. + # From the maintainer: + # > PromiseKit 5 has been released, but is not yet fully documented, + # > so we advise sticking with version 4 for the time being. + s.dependency 'PromiseKit', "~> 4.0" + end diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 24240aba8..1d71c6139 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -46,7 +46,8 @@ #import #import #import -#import +#import +#import NS_ASSUME_NONNULL_BEGIN @@ -576,28 +577,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; }); } -// For group sends, we're using chained futures to make the code more readable. -- (TOCFuture *)sendMessageFuture:(TSOutgoingMessage *)message - recipient:(SignalRecipient *)recipient - thread:(TSThread *)thread -{ - TOCFutureSource *futureSource = [[TOCFutureSource alloc] init]; - - [self sendMessageToService:message - recipient:recipient - thread:thread - attempts:OWSMessageSenderRetryAttempts - useWebsocketIfAvailable:YES - success:^{ - [futureSource trySetResult:@1]; - } - failure:^(NSError *error) { - [futureSource trySetFailure:error]; - }]; - - return futureSource.future; -} - - (void)groupSend:(NSArray *)recipients message:(TSOutgoingMessage *)message thread:(TSThread *)thread @@ -606,7 +585,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; { [self saveGroupMessage:message inThread:thread]; - NSMutableArray *futures = [NSMutableArray array]; + NSMutableArray *sendPromises = [NSMutableArray array]; + NSMutableArray *sendErrors = [NSMutableArray array]; for (SignalRecipient *recipient in recipients) { NSString *recipientId = recipient.recipientId; @@ -617,52 +597,66 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } // ...otherwise we send. - [futures addObject:[self sendMessageFuture:message recipient:recipient thread:thread]]; + + // For group sends, we're using chained promises to make the code more readable. + AnyPromise *sendPromise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { + [self sendMessageToService:message + recipient:recipient + thread:thread + attempts:OWSMessageSenderRetryAttempts + useWebsocketIfAvailable:YES + success:^{ + resolve(@(1)); + } + failure:^(NSError *error) { + @synchronized(sendErrors) { + [sendErrors addObject:error]; + } + resolve(error); + }]; + }]; + [sendPromises addObject:sendPromise]; } - TOCFuture *completionFuture = futures.toc_thenAll; - - [completionFuture thenDo:^(id value) { + // We use PMKJoin(), not PMKWhen(), because we don't want the + // completion promise to execute until _all_ send promises + // have either succeeded or failed. PMKWhen() executes as + // soon as any of its input promises fail. + AnyPromise *sendCompletionPromise = PMKJoin(sendPromises); + sendCompletionPromise.then(^(id value) { successHandler(); - }]; - - [completionFuture catchDo:^(id failure) { - // failure from toc_thenAll yields an array of failed Futures, rather than the future's failure. + }); + sendCompletionPromise.catch(^(id failure) { NSError *firstRetryableError = nil; NSError *firstNonRetryableError = nil; - if ([failure isKindOfClass:[NSArray class]]) { - NSArray *groupSendFutures = (NSArray *)failure; - for (TOCFuture *groupSendFuture in groupSendFutures) { - if (groupSendFuture.hasFailed) { - id failureResult = groupSendFuture.forceGetFailure; - if ([failureResult isKindOfClass:[NSError class]]) { - NSError *error = failureResult; + NSArray *sendErrorsCopy; + @synchronized(sendErrors) { + sendErrorsCopy = [sendErrors copy]; + } - // Some errors should be ignored when sending messages - // to groups. See discussion on - // NSError (OWSMessageSender) category. - if ([error shouldBeIgnoredForGroups]) { - continue; - } + for (NSError *error in sendErrorsCopy) { + // Some errors should be ignored when sending messages + // to groups. See discussion on + // NSError (OWSMessageSender) category. + if ([error shouldBeIgnoredForGroups]) { + continue; + } - // Some errors should never be retried, in order to avoid - // hitting rate limits, for example. Unfortunately, since - // group send retry is all-or-nothing, we need to fail - // immediately even if some of the other recipients had - // retryable errors. - if ([error isFatal]) { - failureHandler(error); - return; - } + // Some errors should never be retried, in order to avoid + // hitting rate limits, for example. Unfortunately, since + // group send retry is all-or-nothing, we need to fail + // immediately even if some of the other recipients had + // retryable errors. + if ([error isFatal]) { + failureHandler(error); + return; + } - if ([error isRetryable] && !firstRetryableError) { - firstRetryableError = error; - } else if (![error isRetryable] && !firstNonRetryableError) { - firstNonRetryableError = error; - } - } - } + if ([error isRetryable] && !firstRetryableError) { + firstRetryableError = error; + } else if (![error isRetryable] && !firstNonRetryableError) { + firstNonRetryableError = error; } } @@ -686,7 +680,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; successHandler(); } } - }]; + }); + [sendCompletionPromise retainUntilComplete]; } - (void)unregisteredRecipient:(SignalRecipient *)recipient diff --git a/SignalMessaging/categories/Collection+OWS.swift b/SignalServiceKit/src/Util/Collection+OWS.swift similarity index 100% rename from SignalMessaging/categories/Collection+OWS.swift rename to SignalServiceKit/src/Util/Collection+OWS.swift diff --git a/SignalMessaging/categories/NSAttributedString+OWS.h b/SignalServiceKit/src/Util/NSAttributedString+OWS.h similarity index 100% rename from SignalMessaging/categories/NSAttributedString+OWS.h rename to SignalServiceKit/src/Util/NSAttributedString+OWS.h diff --git a/SignalMessaging/categories/NSAttributedString+OWS.m b/SignalServiceKit/src/Util/NSAttributedString+OWS.m similarity index 100% rename from SignalMessaging/categories/NSAttributedString+OWS.m rename to SignalServiceKit/src/Util/NSAttributedString+OWS.m diff --git a/SignalMessaging/categories/NSString+OWS.h b/SignalServiceKit/src/Util/NSString+OWS.h similarity index 100% rename from SignalMessaging/categories/NSString+OWS.h rename to SignalServiceKit/src/Util/NSString+OWS.h diff --git a/SignalMessaging/categories/NSString+OWS.m b/SignalServiceKit/src/Util/NSString+OWS.m similarity index 100% rename from SignalMessaging/categories/NSString+OWS.m rename to SignalServiceKit/src/Util/NSString+OWS.m diff --git a/SignalMessaging/categories/Promise+retainUntilComplete.swift b/SignalServiceKit/src/Util/Promise+retainUntilComplete.swift similarity index 100% rename from SignalMessaging/categories/Promise+retainUntilComplete.swift rename to SignalServiceKit/src/Util/Promise+retainUntilComplete.swift diff --git a/SignalMessaging/categories/String+OWS.swift b/SignalServiceKit/src/Util/String+OWS.swift similarity index 100% rename from SignalMessaging/categories/String+OWS.swift rename to SignalServiceKit/src/Util/String+OWS.swift diff --git a/SignalMessaging/categories/Theme.h b/SignalServiceKit/src/Util/Theme.h similarity index 100% rename from SignalMessaging/categories/Theme.h rename to SignalServiceKit/src/Util/Theme.h diff --git a/SignalMessaging/categories/Theme.m b/SignalServiceKit/src/Util/Theme.m similarity index 100% rename from SignalMessaging/categories/Theme.m rename to SignalServiceKit/src/Util/Theme.m diff --git a/SignalMessaging/categories/UIColor+OWS.h b/SignalServiceKit/src/Util/UIColor+OWS.h similarity index 100% rename from SignalMessaging/categories/UIColor+OWS.h rename to SignalServiceKit/src/Util/UIColor+OWS.h diff --git a/SignalMessaging/categories/UIColor+OWS.m b/SignalServiceKit/src/Util/UIColor+OWS.m similarity index 100% rename from SignalMessaging/categories/UIColor+OWS.m rename to SignalServiceKit/src/Util/UIColor+OWS.m diff --git a/SignalMessaging/categories/UIDevice+featureSupport.swift b/SignalServiceKit/src/Util/UIDevice+featureSupport.swift similarity index 100% rename from SignalMessaging/categories/UIDevice+featureSupport.swift rename to SignalServiceKit/src/Util/UIDevice+featureSupport.swift diff --git a/SignalMessaging/categories/UIFont+OWS.h b/SignalServiceKit/src/Util/UIFont+OWS.h similarity index 100% rename from SignalMessaging/categories/UIFont+OWS.h rename to SignalServiceKit/src/Util/UIFont+OWS.h diff --git a/SignalMessaging/categories/UIFont+OWS.m b/SignalServiceKit/src/Util/UIFont+OWS.m similarity index 100% rename from SignalMessaging/categories/UIFont+OWS.m rename to SignalServiceKit/src/Util/UIFont+OWS.m diff --git a/SignalMessaging/categories/UIView+OWS.h b/SignalServiceKit/src/Util/UIView+OWS.h similarity index 100% rename from SignalMessaging/categories/UIView+OWS.h rename to SignalServiceKit/src/Util/UIView+OWS.h diff --git a/SignalMessaging/categories/UIView+OWS.m b/SignalServiceKit/src/Util/UIView+OWS.m similarity index 100% rename from SignalMessaging/categories/UIView+OWS.m rename to SignalServiceKit/src/Util/UIView+OWS.m diff --git a/SignalMessaging/categories/UIViewController+OWS.h b/SignalServiceKit/src/Util/UIViewController+OWS.h similarity index 100% rename from SignalMessaging/categories/UIViewController+OWS.h rename to SignalServiceKit/src/Util/UIViewController+OWS.h diff --git a/SignalMessaging/categories/UIViewController+OWS.m b/SignalServiceKit/src/Util/UIViewController+OWS.m similarity index 100% rename from SignalMessaging/categories/UIViewController+OWS.m rename to SignalServiceKit/src/Util/UIViewController+OWS.m