Update PromiseKit
This commit is contained in:
parent
f578ab1df2
commit
d6a6024f37
20
Podfile.lock
20
Podfile.lock
|
@ -43,16 +43,14 @@ 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 (6.5.2):
|
||||
- PromiseKit/CorePromise (= 6.5.2)
|
||||
- PromiseKit/Foundation (= 6.5.2)
|
||||
- PromiseKit/UIKit (= 6.5.2)
|
||||
- PromiseKit/CorePromise (6.5.2)
|
||||
- PromiseKit/Foundation (6.5.2):
|
||||
- PromiseKit/CorePromise
|
||||
- PromiseKit/QuartzCore (4.5.2):
|
||||
- PromiseKit/CorePromise
|
||||
- PromiseKit/UIKit (4.5.2):
|
||||
- PromiseKit/UIKit (6.5.2):
|
||||
- PromiseKit/CorePromise
|
||||
- PureLayout (3.0.2)
|
||||
- Reachability (3.2)
|
||||
|
@ -85,7 +83,7 @@ PODS:
|
|||
- GRKOpenSSLFramework
|
||||
- libPhoneNumber-iOS
|
||||
- Mantle
|
||||
- PromiseKit (~> 4.0)
|
||||
- PromiseKit (~> 6.0)
|
||||
- Reachability
|
||||
- SAMKeychain
|
||||
- SignalCoreKit
|
||||
|
@ -101,7 +99,7 @@ PODS:
|
|||
- GRKOpenSSLFramework
|
||||
- libPhoneNumber-iOS
|
||||
- Mantle
|
||||
- PromiseKit (~> 4.0)
|
||||
- PromiseKit (~> 6.0)
|
||||
- Reachability
|
||||
- SAMKeychain
|
||||
- SignalCoreKit
|
||||
|
|
|
@ -41,7 +41,7 @@ public class MessageFetcherJob: NSObject {
|
|||
guard signalService.isCensorshipCircumventionActive else {
|
||||
Logger.debug("delegating message fetching to SocketManager since we're using normal transport.")
|
||||
TSSocketManager.shared.requestSocketOpen()
|
||||
return Promise(value: ())
|
||||
return Promise.value(())
|
||||
}
|
||||
|
||||
Logger.info("fetching messages via REST.")
|
||||
|
@ -63,7 +63,7 @@ public class MessageFetcherJob: NSObject {
|
|||
return self.run()
|
||||
} else {
|
||||
// All finished
|
||||
return Promise(value: ())
|
||||
return Promise.value(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ public class MessageFetcherJob: NSObject {
|
|||
@objc
|
||||
@discardableResult
|
||||
public func run() -> AnyPromise {
|
||||
return AnyPromise(run())
|
||||
return AnyPromise(run() as Promise)
|
||||
}
|
||||
|
||||
// use in DEBUG or wherever you can't receive push notifications to poll for messages.
|
||||
|
@ -176,25 +176,25 @@ public class MessageFetcherJob: NSObject {
|
|||
}
|
||||
|
||||
private func fetchUndeliveredMessages() -> Promise<(envelopes: [SSKProtoEnvelope], more: Bool)> {
|
||||
return Promise { fulfill, reject in
|
||||
return Promise { resolver in
|
||||
let request = OWSRequestFactory.getMessagesRequest()
|
||||
self.networkManager.makeRequest(
|
||||
request,
|
||||
success: { (_: URLSessionDataTask?, responseObject: Any?) -> Void in
|
||||
guard let (envelopes, more) = self.parseMessagesResponse(responseObject: responseObject) else {
|
||||
Logger.error("response object had unexpected content")
|
||||
return reject(OWSErrorMakeUnableToProcessServerResponseError())
|
||||
return resolver.reject(OWSErrorMakeUnableToProcessServerResponseError())
|
||||
}
|
||||
|
||||
fulfill((envelopes: envelopes, more: more))
|
||||
resolver.fulfill((envelopes: envelopes, more: more))
|
||||
},
|
||||
failure: { (_: URLSessionDataTask?, error: Error?) in
|
||||
guard let error = error else {
|
||||
Logger.error("error was surpringly nil. sheesh rough day.")
|
||||
return reject(OWSErrorMakeUnableToProcessServerResponseError())
|
||||
return resolver.reject(OWSErrorMakeUnableToProcessServerResponseError())
|
||||
}
|
||||
|
||||
reject(error)
|
||||
resolver.reject(error)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ import SignalMessaging
|
|||
},
|
||||
failure: { error in
|
||||
Logger.error("failed with error: \(error) retrying in \(retryDelay)s.")
|
||||
after(interval: retryDelay).then {
|
||||
after(seconds: retryDelay).done {
|
||||
self.run(retryDelay: retryDelay * 2)
|
||||
}.retainUntilComplete()
|
||||
})
|
||||
|
|
|
@ -33,14 +33,9 @@ class SyncPushTokensJob: NSObject {
|
|||
func run() -> Promise<Void> {
|
||||
Logger.info("Starting.")
|
||||
|
||||
let runPromise: Promise<Void> = DispatchQueue.main.promise {
|
||||
// HACK: no-op dispatch to work around a bug in PromiseKit/Swift which won't compile
|
||||
// when dispatching complex Promise types. We should eventually be able to delete the
|
||||
// following two lines, skipping this no-op dispatch.
|
||||
return
|
||||
}.then {
|
||||
let runPromise = firstly {
|
||||
return self.pushRegistrationManager.requestPushTokens()
|
||||
}.then { (pushToken: String, voipToken: String) in
|
||||
}.then { (pushToken: String, voipToken: String) -> Promise<Void> in
|
||||
Logger.info("finished: requesting push tokens")
|
||||
var shouldUploadTokens = false
|
||||
|
||||
|
@ -59,18 +54,17 @@ class SyncPushTokensJob: NSObject {
|
|||
|
||||
guard shouldUploadTokens else {
|
||||
Logger.info("No reason to upload pushToken: \(pushToken), voipToken: \(voipToken)")
|
||||
return Promise(value: ())
|
||||
return Promise.value(())
|
||||
}
|
||||
|
||||
Logger.warn("uploading tokens to account servers. pushToken: \(pushToken), voipToken: \(voipToken)")
|
||||
return self.accountManager.updatePushTokens(pushToken: pushToken, voipToken: voipToken).then {
|
||||
Logger.info("successfully updated push tokens on server")
|
||||
return self.recordPushTokensLocally(pushToken: pushToken, voipToken: voipToken)
|
||||
return firstly {
|
||||
self.accountManager.updatePushTokens(pushToken: pushToken, voipToken: voipToken)
|
||||
}.done { _ in
|
||||
self.recordPushTokensLocally(pushToken: pushToken, voipToken: voipToken)
|
||||
}
|
||||
}.then {
|
||||
}.done {
|
||||
Logger.info("completed successfully.")
|
||||
}.catch { error in
|
||||
Logger.error("Failed with error: \(error).")
|
||||
}
|
||||
|
||||
runPromise.retainUntilComplete()
|
||||
|
@ -80,17 +74,21 @@ class SyncPushTokensJob: NSObject {
|
|||
|
||||
// MARK: - objc wrappers, since objc can't use swift parameterized types
|
||||
|
||||
@objc class func run(accountManager: AccountManager, preferences: OWSPreferences) -> AnyPromise {
|
||||
@objc
|
||||
class func run(accountManager: AccountManager, preferences: OWSPreferences) -> AnyPromise {
|
||||
let promise: Promise<Void> = self.run(accountManager: accountManager, preferences: preferences)
|
||||
return AnyPromise(promise)
|
||||
}
|
||||
|
||||
@objc func run() -> AnyPromise {
|
||||
@objc
|
||||
func run() -> AnyPromise {
|
||||
let promise: Promise<Void> = self.run()
|
||||
return AnyPromise(promise)
|
||||
}
|
||||
|
||||
private func recordPushTokensLocally(pushToken: String, voipToken: String) -> Promise<Void> {
|
||||
// MARK:
|
||||
|
||||
private func recordPushTokensLocally(pushToken: String, voipToken: String) {
|
||||
Logger.warn("Recording push tokens locally. pushToken: \(pushToken), voipToken: \(voipToken)")
|
||||
|
||||
var didTokensChange = false
|
||||
|
@ -110,7 +108,5 @@ class SyncPushTokensJob: NSObject {
|
|||
if (didTokensChange) {
|
||||
NotificationCenter.default.postNotificationNameAsync(SyncPushTokensJob.PushTokensDidChange, object: nil)
|
||||
}
|
||||
|
||||
return Promise(value: ())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
@ -12,8 +12,8 @@ public extension MessageSender {
|
|||
* Wrap message sending in a Promise for easier callback chaining.
|
||||
*/
|
||||
public func sendPromise(message: TSOutgoingMessage) -> Promise<Void> {
|
||||
let promise: Promise<Void> = Promise { fulfill, reject in
|
||||
self.enqueue(message, success: fulfill, failure: reject)
|
||||
let promise: Promise<Void> = Promise { resolver in
|
||||
self.enqueue(message, success: resolver.fulfill, failure: resolver.reject)
|
||||
}
|
||||
|
||||
// Ensure sends complete before they're GC'd.
|
||||
|
|
|
@ -43,7 +43,7 @@ public class AccountManager: NSObject {
|
|||
|
||||
@objc func register(verificationCode: String,
|
||||
pin: String?) -> AnyPromise {
|
||||
return AnyPromise(register(verificationCode: verificationCode, pin: pin))
|
||||
return AnyPromise(register(verificationCode: verificationCode, pin: pin) as Promise<Void>)
|
||||
}
|
||||
|
||||
func register(verificationCode: String,
|
||||
|
@ -71,7 +71,7 @@ public class AccountManager: NSObject {
|
|||
default:
|
||||
throw error
|
||||
}
|
||||
}.then {
|
||||
}.done {
|
||||
self.completeRegistration()
|
||||
}
|
||||
|
||||
|
@ -82,11 +82,11 @@ public class AccountManager: NSObject {
|
|||
|
||||
private func registerForTextSecure(verificationCode: String,
|
||||
pin: String?) -> Promise<Void> {
|
||||
return Promise { fulfill, reject in
|
||||
return Promise { resolver in
|
||||
tsAccountManager.verifyAccount(withCode: verificationCode,
|
||||
pin: pin,
|
||||
success: fulfill,
|
||||
failure: reject)
|
||||
success: resolver.fulfill,
|
||||
failure: resolver.reject)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,38 +105,39 @@ public class AccountManager: NSObject {
|
|||
// MARK: Message Delivery
|
||||
|
||||
func updatePushTokens(pushToken: String, voipToken: String) -> Promise<Void> {
|
||||
return Promise { fulfill, reject in
|
||||
return Promise { resolver in
|
||||
tsAccountManager.registerForPushNotifications(pushToken: pushToken,
|
||||
voipToken: voipToken,
|
||||
success: fulfill,
|
||||
failure: reject)
|
||||
success: resolver.fulfill,
|
||||
failure: resolver.reject)
|
||||
}
|
||||
}
|
||||
|
||||
func enableManualMessageFetching() -> Promise<Void> {
|
||||
return tsAccountManager.setIsManualMessageFetchEnabled(true).asPromise().asVoid()
|
||||
let anyPromise = tsAccountManager.setIsManualMessageFetchEnabled(true)
|
||||
return Promise(anyPromise).asVoid()
|
||||
}
|
||||
|
||||
// MARK: Turn Server
|
||||
|
||||
func getTurnServerInfo() -> Promise<TurnServerInfo> {
|
||||
return Promise { fulfill, reject in
|
||||
return Promise { resolver in
|
||||
self.networkManager.makeRequest(OWSRequestFactory.turnServerInfoRequest(),
|
||||
success: { (_: URLSessionDataTask, responseObject: Any?) in
|
||||
guard responseObject != nil else {
|
||||
return reject(OWSErrorMakeUnableToProcessServerResponseError())
|
||||
return resolver.reject(OWSErrorMakeUnableToProcessServerResponseError())
|
||||
}
|
||||
|
||||
if let responseDictionary = responseObject as? [String: AnyObject] {
|
||||
if let turnServerInfo = TurnServerInfo(attributes: responseDictionary) {
|
||||
return fulfill(turnServerInfo)
|
||||
return resolver.fulfill(turnServerInfo)
|
||||
}
|
||||
Logger.error("unexpected server response:\(responseDictionary)")
|
||||
}
|
||||
return reject(OWSErrorMakeUnableToProcessServerResponseError())
|
||||
return resolver.reject(OWSErrorMakeUnableToProcessServerResponseError())
|
||||
},
|
||||
failure: { (_: URLSessionDataTask, error: Error) in
|
||||
return reject(error)
|
||||
return resolver.reject(error)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,11 +41,11 @@ class DebugUICalling: DebugUIPage {
|
|||
}
|
||||
let callMessage = OWSOutgoingCallMessage(thread: thread, hangupMessage: hangupMessage)
|
||||
|
||||
strongSelf.messageSender.sendPromise(message: callMessage).then {
|
||||
strongSelf.messageSender.sendPromise(message: callMessage).done {
|
||||
Logger.debug("Successfully sent hangup call message to \(thread.contactIdentifier())")
|
||||
}.catch { error in
|
||||
Logger.error("failed to send hangup call message to \(thread.contactIdentifier()) with error: \(error)")
|
||||
}
|
||||
}.retainUntilComplete()
|
||||
},
|
||||
OWSTableItem(title: "Send 'busy' for old call") { [weak self] in
|
||||
guard let strongSelf = self else { return }
|
||||
|
@ -62,11 +62,11 @@ class DebugUICalling: DebugUIPage {
|
|||
|
||||
let callMessage = OWSOutgoingCallMessage(thread: thread, busyMessage: busyMessage)
|
||||
|
||||
strongSelf.messageSender.sendPromise(message: callMessage).then {
|
||||
strongSelf.messageSender.sendPromise(message: callMessage).done {
|
||||
Logger.debug("Successfully sent busy call message to \(thread.contactIdentifier())")
|
||||
}.catch { error in
|
||||
Logger.error("failed to send busy call message to \(thread.contactIdentifier()) with error: \(error)")
|
||||
}
|
||||
}.retainUntilComplete()
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -42,11 +42,11 @@ class DebugUIProfile: DebugUIPage {
|
|||
guard let strongSelf = self else { return }
|
||||
|
||||
let message = OWSProfileKeyMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: aThread)
|
||||
strongSelf.messageSender.sendPromise(message: message).then {
|
||||
strongSelf.messageSender.sendPromise(message: message).done {
|
||||
Logger.info("Successfully sent profile key message to thread: \(String(describing: aThread))")
|
||||
}.catch { _ in
|
||||
owsFailDebug("Failed to send profile key message to thread: \(String(describing: aThread))")
|
||||
}
|
||||
}.catch { _ in
|
||||
owsFailDebug("Failed to send profile key message to thread: \(String(describing: aThread))")
|
||||
}.retainUntilComplete()
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -249,7 +249,7 @@ class GifPickerCell: UICollectionViewCell {
|
|||
return Promise(error: GiphyError.assertionError(description: "renditionForSending was unexpectedly nil"))
|
||||
}
|
||||
|
||||
let (promise, fulfill, reject) = Promise<GiphyAsset>.pending()
|
||||
let (promise, resolver) = Promise<GiphyAsset>.pending()
|
||||
|
||||
// We don't retain a handle on the asset request, since there will only ever
|
||||
// be one selected asset, and we never want to cancel it.
|
||||
|
@ -257,13 +257,13 @@ class GifPickerCell: UICollectionViewCell {
|
|||
.sharedInstance.requestAsset(rendition: renditionForSending,
|
||||
priority: .high,
|
||||
success: { _, asset in
|
||||
fulfill(asset)
|
||||
resolver.fulfill(asset)
|
||||
},
|
||||
failure: { _ in
|
||||
// TODO GiphyDownloader API should pass through a useful failing error
|
||||
// so we can pass it through here
|
||||
Logger.error("request failed")
|
||||
reject(GiphyError.fetchFailure)
|
||||
resolver.reject(GiphyError.fetchFailure)
|
||||
})
|
||||
|
||||
return promise
|
||||
|
|
|
@ -6,6 +6,7 @@ import Foundation
|
|||
import SignalServiceKit
|
||||
import Reachability
|
||||
import SignalMessaging
|
||||
import PromiseKit
|
||||
|
||||
@objc
|
||||
protocol GifPickerViewControllerDelegate: class {
|
||||
|
@ -361,7 +362,9 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
|
|||
|
||||
public func getFileForCell(_ cell: GifPickerCell) {
|
||||
GiphyDownloader.sharedInstance.cancelAllRequests()
|
||||
cell.requestRenditionForSending().then { [weak self] (asset: GiphyAsset) -> Void in
|
||||
firstly {
|
||||
cell.requestRenditionForSending()
|
||||
}.done { [weak self] (asset: GiphyAsset) in
|
||||
guard let strongSelf = self else {
|
||||
Logger.info("ignoring send, since VC was dismissed before fetching finished.")
|
||||
return
|
||||
|
@ -397,7 +400,6 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
|
|||
|
||||
strongSelf.present(alert, animated: true, completion: nil)
|
||||
}.retainUntilComplete()
|
||||
|
||||
}
|
||||
|
||||
public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
|
||||
|
|
|
@ -939,7 +939,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
|
|||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
OWSLogInfo(@"beggining refreshing.");
|
||||
[SignalApp.sharedApp.messageFetcherJob run].always(^{
|
||||
[SignalApp.sharedApp.messageFetcherJob run].ensure(^{
|
||||
OWSLogInfo(@"ending refreshing.");
|
||||
[refreshControl endRefreshing];
|
||||
});
|
||||
|
|
|
@ -442,7 +442,7 @@ protocol CallAudioServiceDelegate: class {
|
|||
// Since a call notification is more urgent than a message notifaction, we
|
||||
// vibrate twice, like a pulse, to differentiate from a normal notification vibration.
|
||||
vibrate()
|
||||
DispatchQueue.default.asyncAfter(deadline: DispatchTime.now() + pulseDuration) {
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + pulseDuration) {
|
||||
self.vibrate()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,19 +104,16 @@ private class SignalCallData: NSObject {
|
|||
public let call: SignalCall
|
||||
|
||||
// Used to coordinate promises across delegate methods
|
||||
let fulfillCallConnectedPromise: (() -> Void)
|
||||
let rejectCallConnectedPromise: ((Error) -> Void)
|
||||
let callConnectedPromise: Promise<Void>
|
||||
let callConnectedResolver: Resolver<Void>
|
||||
|
||||
// Used to ensure any received ICE messages wait until the peer connection client is set up.
|
||||
let fulfillPeerConnectionClientPromise: (() -> Void)
|
||||
let rejectPeerConnectionClientPromise: ((Error) -> Void)
|
||||
let peerConnectionClientPromise: Promise<Void>
|
||||
let peerConnectionClientResolver: Resolver<Void>
|
||||
|
||||
// Used to ensure CallOffer was sent before sending any ICE updates.
|
||||
let fulfillReadyToSendIceUpdatesPromise: (() -> Void)
|
||||
let rejectReadyToSendIceUpdatesPromise: ((Error) -> Void)
|
||||
let readyToSendIceUpdatesPromise: Promise<Void>
|
||||
let readyToSendIceUpdatesResolver: Resolver<Void>
|
||||
|
||||
weak var localCaptureSession: AVCaptureSession? {
|
||||
didSet {
|
||||
|
@ -153,26 +150,17 @@ private class SignalCallData: NSObject {
|
|||
required init(call: SignalCall) {
|
||||
self.call = call
|
||||
|
||||
let (callConnectedPromise, fulfillCallConnectedPromise, rejectCallConnectedPromise) = Promise<Void>.pending()
|
||||
let (callConnectedPromise, callConnectedResolver) = Promise<Void>.pending()
|
||||
self.callConnectedPromise = callConnectedPromise
|
||||
self.fulfillCallConnectedPromise = {
|
||||
fulfillCallConnectedPromise(())
|
||||
}
|
||||
self.rejectCallConnectedPromise = rejectCallConnectedPromise
|
||||
self.callConnectedResolver = callConnectedResolver
|
||||
|
||||
let (peerConnectionClientPromise, fulfillPeerConnectionClientPromise, rejectPeerConnectionClientPromise) = Promise<Void>.pending()
|
||||
let (peerConnectionClientPromise, peerConnectionClientResolver) = Promise<Void>.pending()
|
||||
self.peerConnectionClientPromise = peerConnectionClientPromise
|
||||
self.fulfillPeerConnectionClientPromise = {
|
||||
fulfillPeerConnectionClientPromise(())
|
||||
}
|
||||
self.rejectPeerConnectionClientPromise = rejectPeerConnectionClientPromise
|
||||
self.peerConnectionClientResolver = peerConnectionClientResolver
|
||||
|
||||
let (readyToSendIceUpdatesPromise, fulfillReadyToSendIceUpdatesPromise, rejectReadyToSendIceUpdatesPromise) = Promise<Void>.pending()
|
||||
let (readyToSendIceUpdatesPromise, readyToSendIceUpdatesResolver) = Promise<Void>.pending()
|
||||
self.readyToSendIceUpdatesPromise = readyToSendIceUpdatesPromise
|
||||
self.fulfillReadyToSendIceUpdatesPromise = {
|
||||
fulfillReadyToSendIceUpdatesPromise(())
|
||||
}
|
||||
self.rejectReadyToSendIceUpdatesPromise = rejectReadyToSendIceUpdatesPromise
|
||||
self.readyToSendIceUpdatesResolver = readyToSendIceUpdatesResolver
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
@ -192,15 +180,15 @@ private class SignalCallData: NSObject {
|
|||
|
||||
// In case we're still waiting on this promise somewhere, we need to reject it to avoid a memory leak.
|
||||
// There is no harm in rejecting a previously fulfilled promise.
|
||||
rejectCallConnectedPromise(CallError.obsoleteCall(description: "Terminating call"))
|
||||
self.callConnectedResolver.reject(CallError.obsoleteCall(description: "Terminating call"))
|
||||
|
||||
// In case we're still waiting on the peer connection setup somewhere, we need to reject it to avoid a memory leak.
|
||||
// There is no harm in rejecting a previously fulfilled promise.
|
||||
rejectPeerConnectionClientPromise(CallError.obsoleteCall(description: "Terminating call"))
|
||||
self.peerConnectionClientResolver.reject(CallError.obsoleteCall(description: "Terminating call"))
|
||||
|
||||
// In case we're still waiting on this promise somewhere, we need to reject it to avoid a memory leak.
|
||||
// There is no harm in rejecting a previously fulfilled promise.
|
||||
rejectReadyToSendIceUpdatesPromise(CallError.obsoleteCall(description: "Terminating call"))
|
||||
self.readyToSendIceUpdatesResolver.reject(CallError.obsoleteCall(description: "Terminating call"))
|
||||
|
||||
peerConnectionClient?.terminate()
|
||||
Logger.debug("setting peerConnectionClient")
|
||||
|
@ -395,7 +383,7 @@ private class SignalCallData: NSObject {
|
|||
let peerConnectionClient = PeerConnectionClient(iceServers: iceServers, delegate: self, callDirection: .outgoing, useTurnOnly: useTurnOnly)
|
||||
Logger.debug("setting peerConnectionClient for call: \(call.identifiersForLogs)")
|
||||
callData.peerConnectionClient = peerConnectionClient
|
||||
callData.fulfillPeerConnectionClientPromise()
|
||||
callData.peerConnectionClientResolver.fulfill(())
|
||||
|
||||
return peerConnectionClient.createOffer()
|
||||
}.then { (sessionDescription: HardenedRTCSessionDescription) -> Promise<Void> in
|
||||
|
@ -409,10 +397,12 @@ private class SignalCallData: NSObject {
|
|||
|
||||
Logger.info("session description for outgoing call: \(call.identifiersForLogs), sdp: \(sessionDescription.logSafeDescription).")
|
||||
|
||||
return peerConnectionClient.setLocalSessionDescription(sessionDescription).then {
|
||||
return firstly {
|
||||
peerConnectionClient.setLocalSessionDescription(sessionDescription)
|
||||
}.then { _ -> Promise<Void> in
|
||||
do {
|
||||
let offerBuilder = SSKProtoCallMessageOffer.builder(id: call.signalingId,
|
||||
sessionDescription: sessionDescription.sdp)
|
||||
sessionDescription: sessionDescription.sdp)
|
||||
let callMessage = OWSOutgoingCallMessage(thread: call.thread, offerMessage: try offerBuilder.build())
|
||||
return self.messageSender.sendPromise(message: callMessage)
|
||||
} catch {
|
||||
|
@ -420,7 +410,7 @@ private class SignalCallData: NSObject {
|
|||
throw CallError.fatalError(description: "Couldn't build proto")
|
||||
}
|
||||
}
|
||||
}.then {
|
||||
}.then { () -> Promise<Void> in
|
||||
guard self.call == call else {
|
||||
throw CallError.obsoleteCall(description: "obsolete call")
|
||||
}
|
||||
|
@ -430,7 +420,7 @@ private class SignalCallData: NSObject {
|
|||
self.readyToSendIceUpdates(call: call)
|
||||
|
||||
// Don't let the outgoing call ring forever. We don't support inbound ringing forever anyway.
|
||||
let timeout: Promise<Void> = after(interval: connectingTimeoutSeconds).then { () -> Void in
|
||||
let timeout: Promise<Void> = after(seconds: connectingTimeoutSeconds).done {
|
||||
// This code will always be called, whether or not the call has timed out.
|
||||
// However, if the call has already connected, the `race` promise will have already been
|
||||
// fulfilled. Rejecting an already fulfilled promise is a no-op.
|
||||
|
@ -438,11 +428,13 @@ private class SignalCallData: NSObject {
|
|||
}
|
||||
|
||||
return race(timeout, callData.callConnectedPromise)
|
||||
}.then {
|
||||
}.done {
|
||||
Logger.info(self.call == call
|
||||
? "outgoing call connected: \(call.identifiersForLogs)."
|
||||
: "obsolete outgoing call connected: \(call.identifiersForLogs).")
|
||||
}.catch { error in
|
||||
}
|
||||
|
||||
promise.catch { error in
|
||||
Logger.error("placing call \(call.identifiersForLogs) failed with error: \(error)")
|
||||
|
||||
if let callError = error as? CallError {
|
||||
|
@ -456,8 +448,8 @@ private class SignalCallData: NSObject {
|
|||
let externalError = CallError.externalError(underlyingError: error)
|
||||
self.handleFailedCall(failedCall: call, error: externalError)
|
||||
}
|
||||
}
|
||||
promise.retainUntilComplete()
|
||||
}.retainUntilComplete()
|
||||
|
||||
return promise
|
||||
}
|
||||
|
||||
|
@ -473,7 +465,7 @@ private class SignalCallData: NSObject {
|
|||
return
|
||||
}
|
||||
|
||||
callData.fulfillReadyToSendIceUpdatesPromise()
|
||||
callData.readyToSendIceUpdatesResolver.fulfill(())
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -500,7 +492,10 @@ private class SignalCallData: NSObject {
|
|||
}
|
||||
|
||||
let sessionDescription = RTCSessionDescription(type: .answer, sdp: sessionDescription)
|
||||
let setDescriptionPromise = peerConnectionClient.setRemoteSessionDescription(sessionDescription).then {
|
||||
|
||||
firstly {
|
||||
peerConnectionClient.setRemoteSessionDescription(sessionDescription)
|
||||
}.done {
|
||||
Logger.debug("successfully set remote description")
|
||||
}.catch { error in
|
||||
if let callError = error as? CallError {
|
||||
|
@ -511,8 +506,7 @@ private class SignalCallData: NSObject {
|
|||
let externalError = CallError.externalError(underlyingError: error)
|
||||
self.handleFailedCall(failedCall: call, error: externalError)
|
||||
}
|
||||
}
|
||||
setDescriptionPromise.retainUntilComplete()
|
||||
}.retainUntilComplete()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -683,8 +677,8 @@ private class SignalCallData: NSObject {
|
|||
strongSelf.handleFailedCall(failedCall: newCall, error: timeout)
|
||||
})
|
||||
|
||||
let incomingCallPromise = firstly {
|
||||
return getIceServers()
|
||||
firstly {
|
||||
getIceServers()
|
||||
}.then { (iceServers: [RTCIceServer]) -> Promise<HardenedRTCSessionDescription> in
|
||||
// FIXME for first time call recipients I think we'll see mic/camera permission requests here,
|
||||
// even though, from the users perspective, no incoming call is yet visible.
|
||||
|
@ -702,14 +696,14 @@ private class SignalCallData: NSObject {
|
|||
Logger.debug("setting peerConnectionClient for: \(newCall.identifiersForLogs)")
|
||||
let peerConnectionClient = PeerConnectionClient(iceServers: iceServers, delegate: self, callDirection: .incoming, useTurnOnly: useTurnOnly)
|
||||
callData.peerConnectionClient = peerConnectionClient
|
||||
callData.fulfillPeerConnectionClientPromise()
|
||||
callData.peerConnectionClientResolver.fulfill(())
|
||||
|
||||
let offerSessionDescription = RTCSessionDescription(type: .offer, sdp: callerSessionDescription)
|
||||
let constraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil)
|
||||
|
||||
// Find a sessionDescription compatible with my constraints and the remote sessionDescription
|
||||
return peerConnectionClient.negotiateSessionDescription(remoteDescription: offerSessionDescription, constraints: constraints)
|
||||
}.then { (negotiatedSessionDescription: HardenedRTCSessionDescription) in
|
||||
}.then { (negotiatedSessionDescription: HardenedRTCSessionDescription) -> Promise<Void> in
|
||||
guard self.call == newCall else {
|
||||
throw CallError.obsoleteCall(description: "negotiateSessionDescription() response for obsolete call")
|
||||
}
|
||||
|
@ -726,7 +720,7 @@ private class SignalCallData: NSObject {
|
|||
owsFailDebug("Couldn't build proto")
|
||||
throw CallError.fatalError(description: "Couldn't build proto")
|
||||
}
|
||||
}.then {
|
||||
}.then { () -> Promise<Void> in
|
||||
guard self.call == newCall else {
|
||||
throw CallError.obsoleteCall(description: "sendPromise(message: ) response for obsolete call")
|
||||
}
|
||||
|
@ -736,7 +730,7 @@ private class SignalCallData: NSObject {
|
|||
// a more intuitive ordering.
|
||||
self.readyToSendIceUpdates(call: newCall)
|
||||
|
||||
let timeout: Promise<Void> = after(interval: connectingTimeoutSeconds).then { () -> Void in
|
||||
let timeout: Promise<Void> = after(seconds: connectingTimeoutSeconds).done {
|
||||
// rejecting a promise by throwing is safely a no-op if the promise has already been fulfilled
|
||||
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorTimeoutWhileConnectingIncoming(), file: #file, function: #function, line: #line)
|
||||
throw CallError.timeout(description: "timed out waiting for call to connect")
|
||||
|
@ -744,11 +738,11 @@ private class SignalCallData: NSObject {
|
|||
|
||||
// This will be fulfilled (potentially) by the RTCDataChannel delegate method
|
||||
return race(callData.callConnectedPromise, timeout)
|
||||
}.then {
|
||||
}.done {
|
||||
Logger.info(self.call == newCall
|
||||
? "incoming call connected: \(newCall.identifiersForLogs)."
|
||||
: "obsolete incoming call connected: \(newCall.identifiersForLogs).")
|
||||
}.catch { error in
|
||||
}.recover { error in
|
||||
guard self.call == newCall else {
|
||||
Logger.debug("ignoring error: \(error) for obsolete call: \(newCall.identifiersForLogs).")
|
||||
return
|
||||
|
@ -761,13 +755,12 @@ private class SignalCallData: NSObject {
|
|||
let externalError = CallError.externalError(underlyingError: error)
|
||||
self.handleFailedCall(failedCall: newCall, error: externalError)
|
||||
}
|
||||
}.always {
|
||||
}.ensure {
|
||||
Logger.debug("ending background task awaiting inbound call connection")
|
||||
|
||||
assert(backgroundTask != nil)
|
||||
backgroundTask = nil
|
||||
}
|
||||
incomingCallPromise.retainUntilComplete()
|
||||
}.retainUntilComplete()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -782,7 +775,7 @@ private class SignalCallData: NSObject {
|
|||
return
|
||||
}
|
||||
|
||||
callData.peerConnectionClientPromise.then { () -> Void in
|
||||
callData.peerConnectionClientPromise.done {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard let call = self.call else {
|
||||
|
@ -829,7 +822,7 @@ private class SignalCallData: NSObject {
|
|||
|
||||
// Wait until we've sent the CallOffer before sending any ice updates for the call to ensure
|
||||
// intuitive message ordering for other clients.
|
||||
callData.readyToSendIceUpdatesPromise.then { () -> Void in
|
||||
callData.readyToSendIceUpdatesPromise.done {
|
||||
guard call == self.call else {
|
||||
self.handleFailedCurrentCall(error: .obsoleteCall(description: "current call changed since we became ready to send ice updates"))
|
||||
return
|
||||
|
@ -1072,7 +1065,7 @@ private class SignalCallData: NSObject {
|
|||
Logger.info("handleConnectedCall: \(callData.call.identifiersForLogs).")
|
||||
|
||||
// cancel connection timeout
|
||||
callData.fulfillCallConnectedPromise()
|
||||
callData.callConnectedResolver.fulfill(())
|
||||
|
||||
callData.call.state = .connected
|
||||
|
||||
|
@ -1197,13 +1190,14 @@ private class SignalCallData: NSObject {
|
|||
do {
|
||||
let hangupBuilder = SSKProtoCallMessageHangup.builder(id: call.signalingId)
|
||||
let callMessage = OWSOutgoingCallMessage(thread: call.thread, hangupMessage: try hangupBuilder.build())
|
||||
let sendPromise = self.messageSender.sendPromise(message: callMessage).then {
|
||||
firstly {
|
||||
self.messageSender.sendPromise(message: callMessage)
|
||||
}.done {
|
||||
Logger.debug("successfully sent hangup call message to \(call.thread.contactIdentifier())")
|
||||
}.catch { error in
|
||||
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorHandleLocalHungupCall(), file: #file, function: #function, line: #line)
|
||||
Logger.error("failed to send hangup call message to \(call.thread.contactIdentifier()) with error: \(error)")
|
||||
}
|
||||
sendPromise.retainUntilComplete()
|
||||
}.catch { error in
|
||||
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorHandleLocalHungupCall(), file: #file, function: #function, line: #line)
|
||||
Logger.error("failed to send hangup call message to \(call.thread.contactIdentifier()) with error: \(error)")
|
||||
}.retainUntilComplete()
|
||||
|
||||
terminateCall()
|
||||
} catch {
|
||||
|
@ -1518,8 +1512,8 @@ private class SignalCallData: NSObject {
|
|||
AssertIsOnMainThread()
|
||||
|
||||
return firstly {
|
||||
return accountManager.getTurnServerInfo()
|
||||
}.then { turnServerInfo -> [RTCIceServer] in
|
||||
accountManager.getTurnServerInfo()
|
||||
}.map { turnServerInfo -> [RTCIceServer] in
|
||||
Logger.debug("got turn server urls: \(turnServerInfo.urls)")
|
||||
|
||||
return turnServerInfo.urls.map { url in
|
||||
|
@ -1532,11 +1526,11 @@ private class SignalCallData: NSObject {
|
|||
return RTCIceServer(urlStrings: [url])
|
||||
}
|
||||
} + [CallService.fallbackIceServer]
|
||||
}.recover { error -> [RTCIceServer] in
|
||||
}.recover { (error: Error) -> Guarantee<[RTCIceServer]> in
|
||||
Logger.error("fetching ICE servers failed with error: \(error)")
|
||||
Logger.warn("using fallback ICE Servers")
|
||||
|
||||
return [CallService.fallbackIceServer]
|
||||
return Guarantee.value([CallService.fallbackIceServer])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,11 +34,7 @@ class NonCallKitCallUIAdaptee: NSObject, CallUIAdaptee {
|
|||
// make sure we don't terminate audio session during call
|
||||
OWSAudioSession.shared.startAudioActivity(call.audioActivity)
|
||||
|
||||
self.callService.handleOutgoingCall(call).then {
|
||||
Logger.debug("handleOutgoingCall succeeded")
|
||||
}.catch { error in
|
||||
Logger.error("handleOutgoingCall failed with error: \(error)")
|
||||
}.retainUntilComplete()
|
||||
self.callService.handleOutgoingCall(call).retainUntilComplete()
|
||||
|
||||
return call
|
||||
}
|
||||
|
|
|
@ -498,42 +498,42 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
|
|||
public func createOffer() -> Promise<HardenedRTCSessionDescription> {
|
||||
AssertIsOnMainThread()
|
||||
let proxyCopy = self.proxy
|
||||
let (promise, fulfill, reject) = Promise<HardenedRTCSessionDescription>.pending()
|
||||
let (promise, resolver) = Promise<HardenedRTCSessionDescription>.pending()
|
||||
let completion: ((RTCSessionDescription?, Error?) -> Void) = { (sdp, error) in
|
||||
guard let strongSelf = proxyCopy.get() else {
|
||||
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
resolver.reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
return
|
||||
}
|
||||
strongSelf.assertOnSignalingQueue()
|
||||
guard strongSelf.peerConnection != nil else {
|
||||
Logger.debug("Ignoring obsolete event in terminated client")
|
||||
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
resolver.reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
return
|
||||
}
|
||||
if let error = error {
|
||||
reject(error)
|
||||
resolver.reject(error)
|
||||
return
|
||||
}
|
||||
|
||||
guard let sessionDescription = sdp else {
|
||||
Logger.error("No session description was obtained, even though there was no error reported.")
|
||||
let error = OWSErrorMakeUnableToProcessServerResponseError()
|
||||
reject(error)
|
||||
resolver.reject(error)
|
||||
return
|
||||
}
|
||||
|
||||
fulfill(HardenedRTCSessionDescription(rtcSessionDescription: sessionDescription))
|
||||
resolver.fulfill(HardenedRTCSessionDescription(rtcSessionDescription: sessionDescription))
|
||||
}
|
||||
|
||||
PeerConnectionClient.signalingQueue.async {
|
||||
guard let strongSelf = proxyCopy.get() else {
|
||||
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
resolver.reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
return
|
||||
}
|
||||
strongSelf.assertOnSignalingQueue()
|
||||
guard let peerConnection = strongSelf.peerConnection else {
|
||||
Logger.debug("Ignoring obsolete event in terminated client")
|
||||
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
resolver.reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -549,26 +549,26 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
|
|||
|
||||
public func setLocalSessionDescriptionInternal(_ sessionDescription: HardenedRTCSessionDescription) -> Promise<Void> {
|
||||
let proxyCopy = self.proxy
|
||||
let (promise, fulfill, reject) = Promise<Void>.pending()
|
||||
let (promise, resolver) = Promise<Void>.pending()
|
||||
PeerConnectionClient.signalingQueue.async {
|
||||
guard let strongSelf = proxyCopy.get() else {
|
||||
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
resolver.reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
return
|
||||
}
|
||||
strongSelf.assertOnSignalingQueue()
|
||||
|
||||
guard let peerConnection = strongSelf.peerConnection else {
|
||||
Logger.debug("Ignoring obsolete event in terminated client")
|
||||
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
resolver.reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
return
|
||||
}
|
||||
|
||||
Logger.verbose("setting local session description: \(sessionDescription)")
|
||||
peerConnection.setLocalDescription(sessionDescription.rtcSessionDescription, completionHandler: { (error) in
|
||||
if let error = error {
|
||||
reject(error)
|
||||
resolver.reject(error)
|
||||
} else {
|
||||
fulfill(())
|
||||
resolver.fulfill(())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -578,16 +578,16 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
|
|||
public func setLocalSessionDescription(_ sessionDescription: HardenedRTCSessionDescription) -> Promise<Void> {
|
||||
AssertIsOnMainThread()
|
||||
let proxyCopy = self.proxy
|
||||
let (promise, fulfill, reject) = Promise<Void>.pending()
|
||||
let (promise, resolver) = Promise<Void>.pending()
|
||||
PeerConnectionClient.signalingQueue.async {
|
||||
guard let strongSelf = proxyCopy.get() else {
|
||||
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
resolver.reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
return
|
||||
}
|
||||
strongSelf.assertOnSignalingQueue()
|
||||
guard let peerConnection = strongSelf.peerConnection else {
|
||||
Logger.debug("Ignoring obsolete event in terminated client")
|
||||
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
resolver.reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -595,10 +595,10 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
|
|||
peerConnection.setLocalDescription(sessionDescription.rtcSessionDescription,
|
||||
completionHandler: { error in
|
||||
if let error = error {
|
||||
reject(error)
|
||||
resolver.reject(error)
|
||||
return
|
||||
}
|
||||
fulfill(())
|
||||
resolver.fulfill(())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -609,37 +609,37 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
|
|||
AssertIsOnMainThread()
|
||||
let proxyCopy = self.proxy
|
||||
return setRemoteSessionDescription(remoteDescription)
|
||||
.then(on: PeerConnectionClient.signalingQueue) {
|
||||
.then(on: PeerConnectionClient.signalingQueue) { _ -> Promise<HardenedRTCSessionDescription> in
|
||||
guard let strongSelf = proxyCopy.get() else {
|
||||
return Promise(error: NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
}
|
||||
return strongSelf.negotiateAnswerSessionDescription(constraints: constraints)
|
||||
}
|
||||
return strongSelf.negotiateAnswerSessionDescription(constraints: constraints)
|
||||
}
|
||||
}
|
||||
|
||||
public func setRemoteSessionDescription(_ sessionDescription: RTCSessionDescription) -> Promise<Void> {
|
||||
AssertIsOnMainThread()
|
||||
let proxyCopy = self.proxy
|
||||
let (promise, fulfill, reject) = Promise<Void>.pending()
|
||||
let (promise, resolver) = Promise<Void>.pending()
|
||||
PeerConnectionClient.signalingQueue.async {
|
||||
guard let strongSelf = proxyCopy.get() else {
|
||||
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
resolver.reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
return
|
||||
}
|
||||
strongSelf.assertOnSignalingQueue()
|
||||
guard let peerConnection = strongSelf.peerConnection else {
|
||||
Logger.debug("Ignoring obsolete event in terminated client")
|
||||
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
resolver.reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
return
|
||||
}
|
||||
Logger.verbose("setting remote description: \(sessionDescription)")
|
||||
peerConnection.setRemoteDescription(sessionDescription,
|
||||
completionHandler: { error in
|
||||
if let error = error {
|
||||
reject(error)
|
||||
resolver.reject(error)
|
||||
return
|
||||
}
|
||||
fulfill(())
|
||||
resolver.fulfill(())
|
||||
})
|
||||
}
|
||||
return promise
|
||||
|
@ -648,50 +648,50 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
|
|||
private func negotiateAnswerSessionDescription(constraints: RTCMediaConstraints) -> Promise<HardenedRTCSessionDescription> {
|
||||
assertOnSignalingQueue()
|
||||
let proxyCopy = self.proxy
|
||||
let (promise, fulfill, reject) = Promise<HardenedRTCSessionDescription>.pending()
|
||||
let (promise, resolver) = Promise<HardenedRTCSessionDescription>.pending()
|
||||
let completion: ((RTCSessionDescription?, Error?) -> Void) = { (sdp, error) in
|
||||
guard let strongSelf = proxyCopy.get() else {
|
||||
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
resolver.reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
return
|
||||
}
|
||||
strongSelf.assertOnSignalingQueue()
|
||||
guard strongSelf.peerConnection != nil else {
|
||||
Logger.debug("Ignoring obsolete event in terminated client")
|
||||
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
resolver.reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
return
|
||||
}
|
||||
if let error = error {
|
||||
reject(error)
|
||||
resolver.reject(error)
|
||||
return
|
||||
}
|
||||
|
||||
guard let sessionDescription = sdp else {
|
||||
Logger.error("unexpected empty session description, even though no error was reported.")
|
||||
let error = OWSErrorMakeUnableToProcessServerResponseError()
|
||||
reject(error)
|
||||
resolver.reject(error)
|
||||
return
|
||||
}
|
||||
|
||||
let hardenedSessionDescription = HardenedRTCSessionDescription(rtcSessionDescription: sessionDescription)
|
||||
|
||||
strongSelf.setLocalSessionDescriptionInternal(hardenedSessionDescription)
|
||||
.then(on: PeerConnectionClient.signalingQueue) { _ in
|
||||
fulfill(hardenedSessionDescription)
|
||||
.done(on: PeerConnectionClient.signalingQueue) {
|
||||
resolver.fulfill(hardenedSessionDescription)
|
||||
}.catch { error in
|
||||
reject(error)
|
||||
resolver.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
PeerConnectionClient.signalingQueue.async {
|
||||
guard let strongSelf = proxyCopy.get() else {
|
||||
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
resolver.reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
return
|
||||
}
|
||||
strongSelf.assertOnSignalingQueue()
|
||||
|
||||
guard let peerConnection = strongSelf.peerConnection else {
|
||||
Logger.debug("Ignoring obsolete event in terminated client")
|
||||
reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
resolver.reject(NSError(domain: "Obsolete client", code: 0, userInfo: nil))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -275,8 +275,7 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate {
|
|||
// We can't wait for long before fulfilling the CXAction, else CallKit will show a "Failed Call". We don't
|
||||
// actually need to wait for the outcome of the handleOutgoingCall promise, because it handles any errors by
|
||||
// manually failing the call.
|
||||
let callPromise = self.callService.handleOutgoingCall(call)
|
||||
callPromise.retainUntilComplete()
|
||||
self.callService.handleOutgoingCall(call).retainUntilComplete()
|
||||
|
||||
action.fulfill()
|
||||
self.provider.reportOutgoingCall(with: call.localId, startedConnectingAt: nil)
|
||||
|
|
|
@ -36,28 +36,29 @@ public enum PushRegistrationError: Error {
|
|||
}
|
||||
|
||||
private var userNotificationSettingsPromise: Promise<Void>?
|
||||
private var fulfillUserNotificationSettingsPromise: (() -> Void)?
|
||||
private var userNotificationSettingsResolver: Resolver<Void>?
|
||||
|
||||
private var vanillaTokenPromise: Promise<Data>?
|
||||
private var fulfillVanillaTokenPromise: ((Data) -> Void)?
|
||||
private var rejectVanillaTokenPromise: ((Error) -> Void)?
|
||||
private var vanillaTokenResolver: Resolver<Data>?
|
||||
|
||||
private var voipRegistry: PKPushRegistry?
|
||||
private var voipTokenPromise: Promise<Data>?
|
||||
private var fulfillVoipTokenPromise: ((Data) -> Void)?
|
||||
private var voipTokenResolver: Resolver<Data>?
|
||||
|
||||
// MARK: Public interface
|
||||
|
||||
public func requestPushTokens() -> Promise<(pushToken: String, voipToken: String)> {
|
||||
Logger.info("")
|
||||
|
||||
return self.registerUserNotificationSettings().then {
|
||||
return firstly {
|
||||
self.registerUserNotificationSettings()
|
||||
}.then { () -> Promise<(pushToken: String, voipToken: String)> in
|
||||
guard !Platform.isSimulator else {
|
||||
throw PushRegistrationError.pushNotSupported(description: "Push not supported on simulators")
|
||||
}
|
||||
|
||||
return self.registerForVanillaPushToken().then { vanillaPushToken in
|
||||
self.registerForVoipPushToken().then { voipPushToken in
|
||||
return self.registerForVanillaPushToken().then { vanillaPushToken -> Promise<(pushToken: String, voipToken: String)> in
|
||||
self.registerForVoipPushToken().map { voipPushToken in
|
||||
(pushToken: vanillaPushToken, voipToken: voipPushToken)
|
||||
}
|
||||
}
|
||||
|
@ -71,12 +72,12 @@ public enum PushRegistrationError: Error {
|
|||
// we register user notification settings.
|
||||
@objc
|
||||
public func didRegisterUserNotificationSettings() {
|
||||
guard let fulfillUserNotificationSettingsPromise = self.fulfillUserNotificationSettingsPromise else {
|
||||
guard let userNotificationSettingsResolver = self.userNotificationSettingsResolver else {
|
||||
owsFailDebug("promise completion in \(#function) unexpectedly nil")
|
||||
return
|
||||
}
|
||||
|
||||
fulfillUserNotificationSettingsPromise()
|
||||
userNotificationSettingsResolver.fulfill(())
|
||||
}
|
||||
|
||||
// MARK: Vanilla push token
|
||||
|
@ -84,23 +85,23 @@ public enum PushRegistrationError: Error {
|
|||
// Vanilla push token is obtained from the system via AppDelegate
|
||||
@objc
|
||||
public func didReceiveVanillaPushToken(_ tokenData: Data) {
|
||||
guard let fulfillVanillaTokenPromise = self.fulfillVanillaTokenPromise else {
|
||||
guard let vanillaTokenResolver = self.vanillaTokenResolver else {
|
||||
owsFailDebug("promise completion in \(#function) unexpectedly nil")
|
||||
return
|
||||
}
|
||||
|
||||
fulfillVanillaTokenPromise(tokenData)
|
||||
vanillaTokenResolver.fulfill(tokenData)
|
||||
}
|
||||
|
||||
// Vanilla push token is obtained from the system via AppDelegate
|
||||
@objc
|
||||
public func didFailToReceiveVanillaPushToken(error: Error) {
|
||||
guard let rejectVanillaTokenPromise = self.rejectVanillaTokenPromise else {
|
||||
guard let vanillaTokenResolver = self.vanillaTokenResolver else {
|
||||
owsFailDebug("promise completion in \(#function) unexpectedly nil")
|
||||
return
|
||||
}
|
||||
|
||||
rejectVanillaTokenPromise(error)
|
||||
vanillaTokenResolver.reject(error)
|
||||
}
|
||||
|
||||
// MARK: PKPushRegistryDelegate - voIP Push Token
|
||||
|
@ -115,12 +116,12 @@ public enum PushRegistrationError: Error {
|
|||
Logger.info("")
|
||||
assert(type == .voIP)
|
||||
assert(credentials.type == .voIP)
|
||||
guard let fulfillVoipTokenPromise = self.fulfillVoipTokenPromise else {
|
||||
guard let voipTokenResolver = self.voipTokenResolver else {
|
||||
owsFailDebug("fulfillVoipTokenPromise was unexpectedly nil")
|
||||
return
|
||||
}
|
||||
|
||||
fulfillVoipTokenPromise(credentials.token)
|
||||
voipTokenResolver.fulfill(credentials.token)
|
||||
}
|
||||
|
||||
public func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {
|
||||
|
@ -143,11 +144,9 @@ public enum PushRegistrationError: Error {
|
|||
return promise
|
||||
}
|
||||
|
||||
let (promise, fulfill, _) = Promise<Void>.pending()
|
||||
let (promise, resolver) = Promise<Void>.pending()
|
||||
self.userNotificationSettingsPromise = promise
|
||||
self.fulfillUserNotificationSettingsPromise = {
|
||||
fulfill(())
|
||||
}
|
||||
self.userNotificationSettingsResolver = resolver
|
||||
|
||||
Logger.info("registering user notification settings")
|
||||
|
||||
|
@ -189,18 +188,18 @@ public enum PushRegistrationError: Error {
|
|||
let promise = vanillaTokenPromise!
|
||||
assert(promise.isPending)
|
||||
Logger.info("alreay pending promise for vanilla push token")
|
||||
return promise.then { $0.hexEncodedString }
|
||||
return promise.map { $0.hexEncodedString }
|
||||
}
|
||||
|
||||
// No pending vanilla token yet. Create a new promise
|
||||
let (promise, fulfill, reject) = Promise<Data>.pending()
|
||||
let (promise, resolver) = Promise<Data>.pending()
|
||||
self.vanillaTokenPromise = promise
|
||||
self.fulfillVanillaTokenPromise = fulfill
|
||||
self.rejectVanillaTokenPromise = reject
|
||||
self.vanillaTokenResolver = resolver
|
||||
|
||||
UIApplication.shared.registerForRemoteNotifications()
|
||||
|
||||
let kTimeout: TimeInterval = 10
|
||||
let timeout: Promise<Data> = after(seconds: kTimeout).then { throw PushRegistrationError.timeout }
|
||||
let timeout: Promise<Data> = after(seconds: kTimeout).map { throw PushRegistrationError.timeout }
|
||||
let promiseWithTimeout: Promise<Data> = race(promise, timeout)
|
||||
|
||||
return promiseWithTimeout.recover { error -> Promise<Data> in
|
||||
|
@ -219,7 +218,7 @@ public enum PushRegistrationError: Error {
|
|||
default:
|
||||
throw error
|
||||
}
|
||||
}.then { (pushTokenData: Data) -> String in
|
||||
}.map { (pushTokenData: Data) -> String in
|
||||
if self.isSusceptibleToFailedPushRegistration {
|
||||
// Sentinal in case this bug is fixed.
|
||||
owsFailDebug("Device was unexpectedly able to complete push registration even though it was susceptible to failure.")
|
||||
|
@ -227,7 +226,7 @@ public enum PushRegistrationError: Error {
|
|||
|
||||
Logger.info("successfully registered for vanilla push notifications")
|
||||
return pushTokenData.hexEncodedString
|
||||
}.always {
|
||||
}.ensure {
|
||||
self.vanillaTokenPromise = nil
|
||||
}
|
||||
}
|
||||
|
@ -239,13 +238,13 @@ public enum PushRegistrationError: Error {
|
|||
guard self.voipTokenPromise == nil else {
|
||||
let promise = self.voipTokenPromise!
|
||||
assert(promise.isPending)
|
||||
return promise.then { $0.hexEncodedString }
|
||||
return promise.map { $0.hexEncodedString }
|
||||
}
|
||||
|
||||
// No pending voip token yet. Create a new promise
|
||||
let (promise, fulfill, reject) = Promise<Data>.pending()
|
||||
let (promise, resolver) = Promise<Data>.pending()
|
||||
self.voipTokenPromise = promise
|
||||
self.fulfillVoipTokenPromise = fulfill
|
||||
self.voipTokenResolver = resolver
|
||||
|
||||
if self.voipRegistry == nil {
|
||||
// We don't create the voip registry in init, because it immediately requests the voip token,
|
||||
|
@ -258,8 +257,8 @@ public enum PushRegistrationError: Error {
|
|||
|
||||
guard let voipRegistry = self.voipRegistry else {
|
||||
owsFailDebug("failed to initialize voipRegistry")
|
||||
reject(PushRegistrationError.assertionError(description: "failed to initialize voipRegistry"))
|
||||
return promise.then { _ in
|
||||
resolver.reject(PushRegistrationError.assertionError(description: "failed to initialize voipRegistry"))
|
||||
return promise.map { _ in
|
||||
// coerce expected type of returned promise - we don't really care about the value,
|
||||
// since this promise has been rejected. In practice this shouldn't happen
|
||||
String()
|
||||
|
@ -270,13 +269,13 @@ public enum PushRegistrationError: Error {
|
|||
// rather than waiting for the delegate method to be called.
|
||||
if let voipTokenData = voipRegistry.pushToken(for: .voIP) {
|
||||
Logger.info("using pre-registered voIP token")
|
||||
fulfill(voipTokenData)
|
||||
resolver.fulfill(voipTokenData)
|
||||
}
|
||||
|
||||
return promise.then { (voipTokenData: Data) -> String in
|
||||
return promise.map { (voipTokenData: Data) -> String in
|
||||
Logger.info("successfully registered for voip push notifications")
|
||||
return voipTokenData.hexEncodedString
|
||||
}.always {
|
||||
}.ensure {
|
||||
self.voipTokenPromise = nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ class AppUpdateNag: NSObject {
|
|||
|
||||
firstly {
|
||||
self.versionService.fetchLatestVersion(lookupURL: lookupURL)
|
||||
}.then { appStoreRecord -> Void in
|
||||
}.done { appStoreRecord in
|
||||
guard appStoreRecord.version.compare(currentVersion, options: .numeric) == ComparisonResult.orderedDescending else {
|
||||
Logger.debug("remote version: \(appStoreRecord) is not newer than currentVersion: \(currentVersion)")
|
||||
return
|
||||
|
@ -201,12 +201,12 @@ class AppStoreVersionService: NSObject {
|
|||
func fetchLatestVersion(lookupURL: URL) -> Promise<AppStoreRecord> {
|
||||
Logger.debug("lookupURL:\(lookupURL)")
|
||||
|
||||
let (promise, fulfill, reject) = Promise<AppStoreRecord>.pending()
|
||||
let (promise, resolver) = Promise<AppStoreRecord>.pending()
|
||||
|
||||
let task = URLSession.ephemeral.dataTask(with: lookupURL) { (data, _, error) in
|
||||
guard let data = data else {
|
||||
Logger.warn("data was unexpectedly nil")
|
||||
reject(OWSErrorMakeUnableToProcessServerResponseError())
|
||||
resolver.reject(OWSErrorMakeUnableToProcessServerResponseError())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -215,13 +215,13 @@ class AppStoreVersionService: NSObject {
|
|||
let resultSet = try decoder.decode(AppStoreLookupResultSet.self, from: data)
|
||||
guard let appStoreRecord = resultSet.results.first else {
|
||||
Logger.warn("record was unexpectedly nil")
|
||||
reject(OWSErrorMakeUnableToProcessServerResponseError())
|
||||
resolver.reject(OWSErrorMakeUnableToProcessServerResponseError())
|
||||
return
|
||||
}
|
||||
|
||||
fulfill(appStoreRecord)
|
||||
resolver.fulfill(appStoreRecord)
|
||||
} catch {
|
||||
reject(error)
|
||||
resolver.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ class AccountManagerTest: SignalBaseTest {
|
|||
|
||||
firstly {
|
||||
accountManager.register(verificationCode: "", pin: "")
|
||||
}.then {
|
||||
}.done {
|
||||
XCTFail("Should fail")
|
||||
}.catch { error in
|
||||
let nserror = error as NSError
|
||||
|
@ -77,7 +77,7 @@ class AccountManagerTest: SignalBaseTest {
|
|||
} else {
|
||||
XCTFail("Unexpected error: \(error)")
|
||||
}
|
||||
}
|
||||
}.retainUntilComplete()
|
||||
|
||||
self.waitForExpectations(timeout: 1.0, handler: nil)
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ class AccountManagerTest: SignalBaseTest {
|
|||
|
||||
firstly {
|
||||
accountManager.register(verificationCode: "123456", pin: "")
|
||||
}.then {
|
||||
}.done {
|
||||
XCTFail("Should fail")
|
||||
}.catch { error in
|
||||
if error is VerificationFailedError {
|
||||
|
@ -97,7 +97,7 @@ class AccountManagerTest: SignalBaseTest {
|
|||
} else {
|
||||
XCTFail("Unexpected error: \(error)")
|
||||
}
|
||||
}
|
||||
}.retainUntilComplete()
|
||||
|
||||
self.waitForExpectations(timeout: 1.0, handler: nil)
|
||||
}
|
||||
|
@ -111,11 +111,11 @@ class AccountManagerTest: SignalBaseTest {
|
|||
|
||||
firstly {
|
||||
accountManager.register(verificationCode: "123456", pin: "")
|
||||
}.then {
|
||||
}.done {
|
||||
expectation.fulfill()
|
||||
}.catch { error in
|
||||
XCTFail("Unexpected error: \(error)")
|
||||
}
|
||||
}.retainUntilComplete()
|
||||
|
||||
self.waitForExpectations(timeout: 1.0, handler: nil)
|
||||
}
|
||||
|
@ -125,11 +125,13 @@ class AccountManagerTest: SignalBaseTest {
|
|||
|
||||
let expectation = self.expectation(description: "should fail")
|
||||
|
||||
accountManager.updatePushTokens(pushToken: PushNotificationRequestResult.FailTSOnly.rawValue, voipToken: "whatever").then {
|
||||
firstly {
|
||||
accountManager.updatePushTokens(pushToken: PushNotificationRequestResult.FailTSOnly.rawValue, voipToken: "whatever")
|
||||
}.done {
|
||||
XCTFail("Expected to fail.")
|
||||
}.catch { _ in
|
||||
expectation.fulfill()
|
||||
}
|
||||
}.retainUntilComplete()
|
||||
|
||||
self.waitForExpectations(timeout: 1.0, handler: nil)
|
||||
}
|
||||
|
|
|
@ -954,7 +954,7 @@ public class SignalAttachment: NSObject {
|
|||
guard let url = dataSource.dataUrl() else {
|
||||
let attachment = SignalAttachment(dataSource: DataSourceValue.emptyDataSource(), dataUTI: dataUTI)
|
||||
attachment.error = .missingData
|
||||
return (Promise(value: attachment), nil)
|
||||
return (Promise.value(attachment), nil)
|
||||
}
|
||||
|
||||
let asset = AVAsset(url: url)
|
||||
|
@ -962,7 +962,7 @@ public class SignalAttachment: NSObject {
|
|||
guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetMediumQuality) else {
|
||||
let attachment = SignalAttachment(dataSource: DataSourceValue.emptyDataSource(), dataUTI: dataUTI)
|
||||
attachment.error = .couldNotConvertToMpeg4
|
||||
return (Promise(value: attachment), nil)
|
||||
return (Promise.value(attachment), nil)
|
||||
}
|
||||
|
||||
exportSession.shouldOptimizeForNetworkUse = true
|
||||
|
@ -972,7 +972,7 @@ public class SignalAttachment: NSObject {
|
|||
let exportURL = videoTempPath.appendingPathComponent(UUID().uuidString).appendingPathExtension("mp4")
|
||||
exportSession.outputURL = exportURL
|
||||
|
||||
let (promise, fulfill, _) = Promise<SignalAttachment>.pending()
|
||||
let (promise, resolver) = Promise<SignalAttachment>.pending()
|
||||
|
||||
Logger.debug("starting video export")
|
||||
exportSession.exportAsynchronously {
|
||||
|
@ -985,14 +985,14 @@ public class SignalAttachment: NSObject {
|
|||
owsFailDebug("Failed to build data source for exported video URL")
|
||||
let attachment = SignalAttachment(dataSource: DataSourceValue.emptyDataSource(), dataUTI: dataUTI)
|
||||
attachment.error = .couldNotConvertToMpeg4
|
||||
fulfill(attachment)
|
||||
resolver.fulfill(attachment)
|
||||
return
|
||||
}
|
||||
|
||||
dataSource.sourceFilename = mp4Filename
|
||||
|
||||
let attachment = SignalAttachment(dataSource: dataSource, dataUTI: kUTTypeMPEG4 as String)
|
||||
fulfill(attachment)
|
||||
resolver.fulfill(attachment)
|
||||
}
|
||||
|
||||
return (promise, exportSession)
|
||||
|
|
|
@ -59,7 +59,7 @@ public class OWS106EnsureProfileComplete: OWSDatabaseMigration {
|
|||
return
|
||||
}
|
||||
|
||||
self.ensureProfileComplete().then { _ -> Void in
|
||||
self.ensureProfileComplete().done {
|
||||
Logger.info("complete. Canceling timer and saving.")
|
||||
self.completionHandler(true)
|
||||
}.catch { error in
|
||||
|
@ -83,33 +83,33 @@ public class OWS106EnsureProfileComplete: OWSDatabaseMigration {
|
|||
func ensureProfileComplete() -> Promise<Void> {
|
||||
guard let localRecipientId = TSAccountManager.localNumber() else {
|
||||
// local app doesn't think we're registered, so nothing to worry about.
|
||||
return Promise(value: ())
|
||||
return Promise.value(())
|
||||
}
|
||||
|
||||
let (promise, fulfill, reject) = Promise<Void>.pending()
|
||||
|
||||
ProfileFetcherJob().getProfile(recipientId: localRecipientId).then { _ -> Void in
|
||||
return firstly {
|
||||
ProfileFetcherJob().getProfile(recipientId: localRecipientId)
|
||||
}.done { _ in
|
||||
Logger.info("verified recipient profile is in good shape: \(localRecipientId)")
|
||||
|
||||
fulfill(())
|
||||
}.catch { error in
|
||||
}.recover { error -> Promise<Void> in
|
||||
switch error {
|
||||
case SignalServiceProfile.ValidationError.invalidIdentityKey(let description):
|
||||
Logger.warn("detected incomplete profile for \(localRecipientId) error: \(description)")
|
||||
|
||||
let (promise, resolver) = Promise<Void>.pending()
|
||||
// This is the error condition we're looking for. Update prekeys to properly set the identity key, completing registration.
|
||||
TSPreKeyManager.createPreKeys(success: {
|
||||
Logger.info("successfully uploaded pre-keys. Profile should be fixed.")
|
||||
fulfill(())
|
||||
resolver.fulfill(())
|
||||
},
|
||||
failure: { _ in
|
||||
reject(OWSErrorWithCodeDescription(.signalServiceFailure, "\(self.logTag) Unknown error"))
|
||||
resolver.reject(OWSErrorWithCodeDescription(.signalServiceFailure, "\(self.logTag) Unknown error"))
|
||||
})
|
||||
default:
|
||||
reject(error)
|
||||
}
|
||||
}.retainUntilComplete()
|
||||
|
||||
return promise
|
||||
return promise
|
||||
default:
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ public class ProfileFetcherJob: NSObject {
|
|||
}
|
||||
|
||||
public func updateProfile(recipientId: String, remainingRetries: Int = 3) {
|
||||
self.getProfile(recipientId: recipientId).then { profile in
|
||||
self.getProfile(recipientId: recipientId).map { profile in
|
||||
self.updateProfile(signalServiceProfile: profile)
|
||||
}.catch { error in
|
||||
switch error {
|
||||
|
@ -138,20 +138,20 @@ public class ProfileFetcherJob: NSObject {
|
|||
let socketType: OWSWebSocketType = unidentifiedAccess == nil ? .default : .UD
|
||||
if socketManager.canMakeRequests(of: socketType) {
|
||||
let request = OWSRequestFactory.getProfileRequest(recipientId: recipientId, unidentifiedAccess: unidentifiedAccess)
|
||||
let (promise, fulfill, reject) = Promise<SignalServiceProfile>.pending()
|
||||
let (promise, resolver) = Promise<SignalServiceProfile>.pending()
|
||||
|
||||
self.socketManager.make(request,
|
||||
webSocketType: socketType,
|
||||
success: { (responseObject: Any?) -> Void in
|
||||
do {
|
||||
let profile = try SignalServiceProfile(recipientId: recipientId, responseObject: responseObject)
|
||||
fulfill(profile)
|
||||
resolver.fulfill(profile)
|
||||
} catch {
|
||||
reject(error)
|
||||
resolver.reject(error)
|
||||
}
|
||||
},
|
||||
failure: { (_: NSInteger, _:Data?, error: Error) in
|
||||
reject(error)
|
||||
resolver.reject(error)
|
||||
})
|
||||
return promise
|
||||
} else {
|
||||
|
|
|
@ -53,11 +53,7 @@ An Objective-C library for communicating with the Signal messaging service.
|
|||
s.dependency 'SignalCoreKit'
|
||||
s.dependency 'SignalMetadataKit'
|
||||
|
||||
# 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"
|
||||
s.dependency 'PromiseKit', "~> 6.0"
|
||||
|
||||
s.test_spec 'Tests' do |test_spec|
|
||||
test_spec.source_files = 'SignalServiceKit/tests/**/*.{h,m,swift}'
|
||||
|
|
|
@ -30,20 +30,20 @@ public class CreatePreKeysOperation: OWSOperation {
|
|||
let signedPreKeyRecord: SignedPreKeyRecord = self.primaryStorage.generateRandomSignedRecord()
|
||||
let preKeyRecords: [PreKeyRecord] = self.primaryStorage.generatePreKeyRecords()
|
||||
|
||||
firstly {
|
||||
self.primaryStorage.storeSignedPreKey(signedPreKeyRecord.id, signedPreKeyRecord: signedPreKeyRecord)
|
||||
self.primaryStorage.storePreKeyRecords(preKeyRecords)
|
||||
self.primaryStorage.storeSignedPreKey(signedPreKeyRecord.id, signedPreKeyRecord: signedPreKeyRecord)
|
||||
self.primaryStorage.storePreKeyRecords(preKeyRecords)
|
||||
|
||||
return self.accountServiceClient.setPreKeys(identityKey: identityKey, signedPreKeyRecord: signedPreKeyRecord, preKeyRecords: preKeyRecords)
|
||||
}.then { () -> Void in
|
||||
signedPreKeyRecord.markAsAcceptedByService()
|
||||
self.primaryStorage.storeSignedPreKey(signedPreKeyRecord.id, signedPreKeyRecord: signedPreKeyRecord)
|
||||
self.primaryStorage.setCurrentSignedPrekeyId(signedPreKeyRecord.id)
|
||||
}.then { () -> Void in
|
||||
Logger.debug("done")
|
||||
self.reportSuccess()
|
||||
}.catch { error in
|
||||
self.reportError(error)
|
||||
}.retainUntilComplete()
|
||||
firstly {
|
||||
self.accountServiceClient.setPreKeys(identityKey: identityKey, signedPreKeyRecord: signedPreKeyRecord, preKeyRecords: preKeyRecords)
|
||||
}.done {
|
||||
signedPreKeyRecord.markAsAcceptedByService()
|
||||
self.primaryStorage.storeSignedPreKey(signedPreKeyRecord.id, signedPreKeyRecord: signedPreKeyRecord)
|
||||
self.primaryStorage.setCurrentSignedPrekeyId(signedPreKeyRecord.id)
|
||||
|
||||
Logger.debug("done")
|
||||
self.reportSuccess()
|
||||
}.catch { error in
|
||||
self.reportError(error)
|
||||
}.retainUntilComplete()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ public class RefreshPreKeysOperation: OWSOperation {
|
|||
Logger.debug("preKeysCount: \(preKeysCount)")
|
||||
guard preKeysCount < kEphemeralPreKeysMinimumCount || self.primaryStorage.currentSignedPrekeyId() == nil else {
|
||||
Logger.debug("Available keys sufficient: \(preKeysCount)")
|
||||
return Promise(value: ())
|
||||
return Promise.value(())
|
||||
}
|
||||
|
||||
let identityKey: Data = self.identityKeyManager.identityKeyPair()!.publicKey
|
||||
|
@ -52,7 +52,9 @@ public class RefreshPreKeysOperation: OWSOperation {
|
|||
self.primaryStorage.storeSignedPreKey(signedPreKeyRecord.id, signedPreKeyRecord: signedPreKeyRecord)
|
||||
self.primaryStorage.storePreKeyRecords(preKeyRecords)
|
||||
|
||||
return self.accountServiceClient.setPreKeys(identityKey: identityKey, signedPreKeyRecord: signedPreKeyRecord, preKeyRecords: preKeyRecords).then { () -> Void in
|
||||
return firstly {
|
||||
self.accountServiceClient.setPreKeys(identityKey: identityKey, signedPreKeyRecord: signedPreKeyRecord, preKeyRecords: preKeyRecords)
|
||||
}.done {
|
||||
signedPreKeyRecord.markAsAcceptedByService()
|
||||
self.primaryStorage.storeSignedPreKey(signedPreKeyRecord.id, signedPreKeyRecord: signedPreKeyRecord)
|
||||
self.primaryStorage.setCurrentSignedPrekeyId(signedPreKeyRecord.id)
|
||||
|
@ -60,7 +62,7 @@ public class RefreshPreKeysOperation: OWSOperation {
|
|||
TSPreKeyManager.clearPreKeyUpdateFailureCount()
|
||||
TSPreKeyManager.clearSignedPreKeyRecords()
|
||||
}
|
||||
}.then { () -> Void in
|
||||
}.done {
|
||||
Logger.debug("done")
|
||||
self.reportSuccess()
|
||||
}.catch { error in
|
||||
|
|
|
@ -29,10 +29,10 @@ public class RotateSignedPreKeyOperation: OWSOperation {
|
|||
|
||||
let signedPreKeyRecord: SignedPreKeyRecord = self.primaryStorage.generateRandomSignedRecord()
|
||||
|
||||
self.primaryStorage.storeSignedPreKey(signedPreKeyRecord.id, signedPreKeyRecord: signedPreKeyRecord)
|
||||
firstly {
|
||||
self.primaryStorage.storeSignedPreKey(signedPreKeyRecord.id, signedPreKeyRecord: signedPreKeyRecord)
|
||||
return self.accountServiceClient.setSignedPreKey(signedPreKeyRecord)
|
||||
}.then(on: DispatchQueue.global()) { () -> Void in
|
||||
}.done(on: DispatchQueue.global()) {
|
||||
Logger.info("Successfully uploaded signed PreKey")
|
||||
signedPreKeyRecord.markAsAcceptedByService()
|
||||
self.primaryStorage.storeSignedPreKey(signedPreKeyRecord.id, signedPreKeyRecord: signedPreKeyRecord)
|
||||
|
@ -40,7 +40,7 @@ public class RotateSignedPreKeyOperation: OWSOperation {
|
|||
|
||||
TSPreKeyManager.clearPreKeyUpdateFailureCount()
|
||||
TSPreKeyManager.clearSignedPreKeyRecords()
|
||||
}.then { () -> Void in
|
||||
|
||||
Logger.debug("done")
|
||||
self.reportSuccess()
|
||||
}.catch { error in
|
||||
|
|
|
@ -129,7 +129,7 @@ NSString *const kOutgoingReadReceiptManagerCollection = @"kOutgoingReadReceiptMa
|
|||
}
|
||||
|
||||
AnyPromise *completionPromise = PMKJoin(sendPromises);
|
||||
completionPromise.always(^() {
|
||||
completionPromise.ensure(^() {
|
||||
// Wait N seconds before conducting another pass.
|
||||
// This allows time for a batch to accumulate.
|
||||
//
|
||||
|
|
|
@ -232,39 +232,44 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
|
|||
@objc
|
||||
public func ensureSenderCertificateObjC(success:@escaping (SMKSenderCertificate) -> Void,
|
||||
failure:@escaping (Error) -> Void) {
|
||||
ensureSenderCertificate()
|
||||
.then(execute: { certificate in
|
||||
success(certificate)
|
||||
})
|
||||
.catch(execute: { (error) in
|
||||
failure(error)
|
||||
}).retainUntilComplete()
|
||||
firstly {
|
||||
ensureSenderCertificate()
|
||||
}.map { certificate in
|
||||
success(certificate)
|
||||
}.catch { error in
|
||||
failure(error)
|
||||
}.retainUntilComplete()
|
||||
}
|
||||
|
||||
public func ensureSenderCertificate() -> Promise<SMKSenderCertificate> {
|
||||
// If there is a valid cached sender certificate, use that.
|
||||
if let certificate = senderCertificate() {
|
||||
return Promise(value: certificate)
|
||||
return Promise.value(certificate)
|
||||
}
|
||||
|
||||
// Try to obtain a new sender certificate.
|
||||
return requestSenderCertificate().then { (certificateData, certificate) in
|
||||
return firstly {
|
||||
requestSenderCertificate()
|
||||
}.map { (certificateData: Data, certificate: SMKSenderCertificate) in
|
||||
|
||||
// Cache the current sender certificate.
|
||||
self.setSenderCertificate(certificateData)
|
||||
|
||||
return Promise(value: certificate)
|
||||
return certificate
|
||||
}
|
||||
}
|
||||
|
||||
private func requestSenderCertificate() -> Promise<(Data, SMKSenderCertificate)> {
|
||||
return SignalServiceRestClient().requestUDSenderCertificate().then { (certificateData) -> Promise<(Data, SMKSenderCertificate)> in
|
||||
private func requestSenderCertificate() -> Promise<(certificateData: Data, certificate: SMKSenderCertificate)> {
|
||||
return firstly {
|
||||
SignalServiceRestClient().requestUDSenderCertificate()
|
||||
}.map { certificateData -> (certificateData: Data, certificate: SMKSenderCertificate) in
|
||||
let certificate = try SMKSenderCertificate.parse(data: certificateData)
|
||||
|
||||
guard self.isValidCertificate(certificate) else {
|
||||
throw OWSUDError.invalidData(description: "Invalid sender certificate returned by server")
|
||||
}
|
||||
|
||||
return Promise(value: (certificateData, certificate) )
|
||||
return (certificateData: certificateData, certificate: certificate)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,18 +27,19 @@ extension NetworkManagerError {
|
|||
}
|
||||
|
||||
extension TSNetworkManager {
|
||||
|
||||
public func makePromise(request: TSRequest) -> Promise<(task: URLSessionDataTask, responseObject: Any?)> {
|
||||
let (promise, fulfill, reject) = Promise<(task: URLSessionDataTask, responseObject: Any?)>.pending()
|
||||
let (promise, resolver) = Promise<(task: URLSessionDataTask, responseObject: Any?)>.pending()
|
||||
|
||||
self.makeRequest(request,
|
||||
success: { task, responseObject in
|
||||
fulfill((task: task, responseObject: responseObject))
|
||||
resolver.fulfill((task: task, responseObject: responseObject))
|
||||
},
|
||||
failure: { task, error in
|
||||
let nmError = NetworkManagerError.taskError(task: task, underlyingError: error)
|
||||
let nsError: NSError = nmError as NSError
|
||||
nsError.isRetryable = (error as NSError).isRetryable
|
||||
reject(nsError)
|
||||
resolver.reject(nsError)
|
||||
})
|
||||
|
||||
return promise
|
||||
|
|
|
@ -38,13 +38,15 @@ public class SignalServiceRestClient: NSObject, SignalServiceClient {
|
|||
Logger.debug("")
|
||||
|
||||
let request = OWSRequestFactory.availablePreKeysCountRequest()
|
||||
return networkManager.makePromise(request: request).then { (_, responseObject) -> Int in
|
||||
return firstly {
|
||||
networkManager.makePromise(request: request)
|
||||
}.map { _, responseObject in
|
||||
Logger.debug("got response")
|
||||
guard let params = ParamParser(responseObject: responseObject) else {
|
||||
throw self.unexpectedServerResponseError()
|
||||
}
|
||||
|
||||
let count: Int = try! params.required(key: "count")
|
||||
let count: Int = try params.required(key: "count")
|
||||
|
||||
return count
|
||||
}
|
||||
|
@ -66,20 +68,15 @@ public class SignalServiceRestClient: NSObject, SignalServiceClient {
|
|||
|
||||
public func requestUDSenderCertificate() -> Promise<Data> {
|
||||
let request = OWSRequestFactory.udSenderCertificateRequest()
|
||||
return self.networkManager.makePromise(request: request)
|
||||
.then(execute: { (_, responseObject) -> Data in
|
||||
let certificateData = try self.parseUDSenderCertificateResponse(responseObject: responseObject)
|
||||
return firstly {
|
||||
self.networkManager.makePromise(request: request)
|
||||
}.map { _, responseObject in
|
||||
guard let parser = ParamParser(responseObject: responseObject) else {
|
||||
throw OWSUDError.invalidData(description: "Invalid sender certificate response")
|
||||
}
|
||||
|
||||
return certificateData
|
||||
})
|
||||
}
|
||||
|
||||
private func parseUDSenderCertificateResponse(responseObject: Any?) throws -> Data {
|
||||
guard let parser = ParamParser(responseObject: responseObject) else {
|
||||
throw OWSUDError.invalidData(description: "Invalid sender certificate response")
|
||||
return try parser.requiredBase64EncodedData(key: "certificate")
|
||||
}
|
||||
|
||||
return try parser.requiredBase64EncodedData(key: "certificate")
|
||||
}
|
||||
|
||||
@objc
|
||||
|
@ -89,19 +86,15 @@ public class SignalServiceRestClient: NSObject, SignalServiceClient {
|
|||
|
||||
public func updateAccountAttributes() -> Promise<Void> {
|
||||
let request = OWSRequestFactory.updateAttributesRequest()
|
||||
let promise: Promise<Void> = networkManager.makePromise(request: request)
|
||||
.then(execute: { (_, _) in
|
||||
Logger.info("updated account attributes on server")
|
||||
}).catch(execute: { (error) in
|
||||
Logger.error("failed to update account attributes on server with error: \(error)")
|
||||
})
|
||||
return promise
|
||||
return networkManager.makePromise(request: request).asVoid()
|
||||
}
|
||||
|
||||
public func retrieveProfile(recipientId: RecipientIdentifier, unidentifiedAccess: SSKUnidentifiedAccess?) -> Promise<SignalServiceProfile> {
|
||||
let request = OWSRequestFactory.getProfileRequest(recipientId: recipientId, unidentifiedAccess: unidentifiedAccess)
|
||||
return networkManager.makePromise(request: request).then { (_: URLSessionDataTask, responseObject: Any?) in
|
||||
return try SignalServiceProfile(recipientId: recipientId, responseObject: responseObject)
|
||||
return firstly {
|
||||
networkManager.makePromise(request: request)
|
||||
}.map { _, responseObject in
|
||||
try SignalServiceProfile(recipientId: recipientId, responseObject: responseObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,10 +12,22 @@ public extension AnyPromise {
|
|||
*/
|
||||
@objc
|
||||
func retainUntilComplete() {
|
||||
// Unfortunately, there is (currently) no way to surpress the
|
||||
// compiler warning: "Variable 'retainCycle' was written to, but never read"
|
||||
var retainCycle: AnyPromise? = self
|
||||
self.always {
|
||||
_ = self.ensure {
|
||||
assert(retainCycle != nil)
|
||||
retainCycle = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension PMKFinalizer {
|
||||
/**
|
||||
* Sometimes there isn't a straight forward candidate to retain a promise, in that case we tell the
|
||||
* promise to self retain, until it completes to avoid the risk it's GC'd before completion.
|
||||
*/
|
||||
func retainUntilComplete() {
|
||||
var retainCycle: PMKFinalizer? = self
|
||||
_ = self.finally {
|
||||
assert(retainCycle != nil)
|
||||
retainCycle = nil
|
||||
}
|
||||
|
@ -28,10 +40,22 @@ public extension Promise {
|
|||
* promise to self retain, until it completes to avoid the risk it's GC'd before completion.
|
||||
*/
|
||||
func retainUntilComplete() {
|
||||
// Unfortunately, there is (currently) no way to surpress the
|
||||
// compiler warning: "Variable 'retainCycle' was written to, but never read"
|
||||
var retainCycle: Promise<T>? = self
|
||||
self.always {
|
||||
_ = self.ensure {
|
||||
assert(retainCycle != nil)
|
||||
retainCycle = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Guarantee {
|
||||
/**
|
||||
* Sometimes there isn't a straight forward candidate to retain a promise, in that case we tell the
|
||||
* promise to self retain, until it completes to avoid the risk it's GC'd before completion.
|
||||
*/
|
||||
func retainUntilComplete() {
|
||||
var retainCycle: Guarantee<T>? = self
|
||||
_ = self.done { _ in
|
||||
assert(retainCycle != nil)
|
||||
retainCycle = nil
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
|
|||
self.loadViewController = loadViewController
|
||||
|
||||
// Don't display load screen immediately, in hopes that we can avoid it altogether.
|
||||
after(seconds: 0.5).then { [weak self] () -> Void in
|
||||
after(seconds: 0.5).done { [weak self] in
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard let strongSelf = self else { return }
|
||||
|
@ -532,7 +532,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
|
|||
private func buildAttachmentAndPresentConversationPicker() {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
self.buildAttachment().then { [weak self] attachment -> Void in
|
||||
self.buildAttachment().map { [weak self] attachment in
|
||||
AssertIsOnMainThread()
|
||||
guard let strongSelf = self else { return }
|
||||
|
||||
|
@ -544,7 +544,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
|
|||
conversationPicker.attachment = attachment
|
||||
strongSelf.showPrimaryViewController(conversationPicker)
|
||||
Logger.info("showing picker with attachment: \(attachment)")
|
||||
}.catch {[weak self] error in
|
||||
}.catch { [weak self] error in
|
||||
AssertIsOnMainThread()
|
||||
guard let strongSelf = self else { return }
|
||||
|
||||
|
@ -690,7 +690,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
|
|||
}
|
||||
Logger.debug("matched utiType: \(srcUtiType)")
|
||||
|
||||
let (promise, fulfill, reject) = Promise<(itemUrl: URL, utiType: String)>.pending()
|
||||
let (promise, resolver) = Promise<(itemUrl: URL, utiType: String)>.pending()
|
||||
|
||||
var customFileName: String?
|
||||
var isConvertibleToTextMessage = false
|
||||
|
@ -702,13 +702,13 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
|
|||
guard let strongSelf = self else { return }
|
||||
|
||||
guard error == nil else {
|
||||
reject(error!)
|
||||
resolver.reject(error!)
|
||||
return
|
||||
}
|
||||
|
||||
guard let value = value else {
|
||||
let missingProviderError = ShareViewControllerError.assertionError(description: "missing item provider")
|
||||
reject(missingProviderError)
|
||||
resolver.reject(missingProviderError)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -726,7 +726,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
|
|||
} else {
|
||||
Logger.error("could not parse vcard.")
|
||||
let writeError = ShareViewControllerError.assertionError(description: "Could not parse vcard data.")
|
||||
reject(writeError)
|
||||
resolver.reject(writeError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -734,21 +734,21 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
|
|||
let customFileExtension = MIMETypeUtil.fileExtension(forUTIType: srcUtiType)
|
||||
guard let tempFilePath = OWSFileSystem.writeData(toTemporaryFile: data, fileExtension: customFileExtension) else {
|
||||
let writeError = ShareViewControllerError.assertionError(description: "Error writing item data: \(String(describing: error))")
|
||||
reject(writeError)
|
||||
resolver.reject(writeError)
|
||||
return
|
||||
}
|
||||
let fileUrl = URL(fileURLWithPath: tempFilePath)
|
||||
fulfill((itemUrl: fileUrl, utiType: srcUtiType))
|
||||
resolver.fulfill((itemUrl: fileUrl, utiType: srcUtiType))
|
||||
} else if let string = value as? String {
|
||||
Logger.debug("string provider: \(string)")
|
||||
guard let data = string.filterStringForDisplay().data(using: String.Encoding.utf8) else {
|
||||
let writeError = ShareViewControllerError.assertionError(description: "Error writing item data: \(String(describing: error))")
|
||||
reject(writeError)
|
||||
resolver.reject(writeError)
|
||||
return
|
||||
}
|
||||
guard let tempFilePath = OWSFileSystem.writeData(toTemporaryFile: data, fileExtension: "txt") else {
|
||||
let writeError = ShareViewControllerError.assertionError(description: "Error writing item data: \(String(describing: error))")
|
||||
reject(writeError)
|
||||
resolver.reject(writeError)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -757,18 +757,18 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
|
|||
isConvertibleToTextMessage = !itemProvider.registeredTypeIdentifiers.contains(kUTTypeFileURL as String)
|
||||
|
||||
if UTTypeConformsTo(srcUtiType as CFString, kUTTypeText) {
|
||||
fulfill((itemUrl: fileUrl, utiType: srcUtiType))
|
||||
resolver.fulfill((itemUrl: fileUrl, utiType: srcUtiType))
|
||||
} else {
|
||||
fulfill((itemUrl: fileUrl, utiType: kUTTypeText as String))
|
||||
resolver.fulfill((itemUrl: fileUrl, utiType: kUTTypeText as String))
|
||||
}
|
||||
} else if let url = value as? URL {
|
||||
// If the share itself is a URL (e.g. a link from Safari), try to send this as a text message.
|
||||
isConvertibleToTextMessage = (itemProvider.registeredTypeIdentifiers.contains(kUTTypeURL as String) &&
|
||||
!itemProvider.registeredTypeIdentifiers.contains(kUTTypeFileURL as String))
|
||||
if isConvertibleToTextMessage {
|
||||
fulfill((itemUrl: url, utiType: kUTTypeURL as String))
|
||||
resolver.fulfill((itemUrl: url, utiType: kUTTypeURL as String))
|
||||
} else {
|
||||
fulfill((itemUrl: url, utiType: srcUtiType))
|
||||
resolver.fulfill((itemUrl: url, utiType: srcUtiType))
|
||||
}
|
||||
} else if let image = value as? UIImage {
|
||||
if let data = UIImagePNGRepresentation(image) {
|
||||
|
@ -776,18 +776,18 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
|
|||
do {
|
||||
let url = NSURL.fileURL(withPath: tempFilePath)
|
||||
try data.write(to: url)
|
||||
fulfill((url, srcUtiType))
|
||||
resolver.fulfill((url, srcUtiType))
|
||||
} catch {
|
||||
reject(ShareViewControllerError.assertionError(description: "couldn't write UIImage: \(String(describing: error))"))
|
||||
resolver.reject(ShareViewControllerError.assertionError(description: "couldn't write UIImage: \(String(describing: error))"))
|
||||
}
|
||||
} else {
|
||||
reject(ShareViewControllerError.assertionError(description: "couldn't convert UIImage to PNG: \(String(describing: error))"))
|
||||
resolver.reject(ShareViewControllerError.assertionError(description: "couldn't convert UIImage to PNG: \(String(describing: error))"))
|
||||
}
|
||||
} else {
|
||||
// It's unavoidable that we may sometimes receives data types that we
|
||||
// don't know how to handle.
|
||||
let unexpectedTypeError = ShareViewControllerError.assertionError(description: "unexpected value: \(String(describing: value))")
|
||||
reject(unexpectedTypeError)
|
||||
resolver.reject(unexpectedTypeError)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -861,7 +861,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
|
|||
Logger.info("isConvertibleToTextMessage")
|
||||
attachment.isConvertibleToTextMessage = isConvertibleToTextMessage
|
||||
}
|
||||
return Promise(value: attachment)
|
||||
return Promise.value(attachment)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue