Skip to content

Commit

Permalink
[Config] Follow-up to 'RCNRealtime' port (#14303)
Browse files Browse the repository at this point in the history
  • Loading branch information
ncooke3 authored Jan 4, 2025
1 parent bf24629 commit b79b6d8
Showing 1 changed file with 36 additions and 26 deletions.
62 changes: 36 additions & 26 deletions FirebaseRemoteConfig/SwiftNew/ConfigRealtime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {
}
if status == .success {
if Int(self.configFetch.templateVersionNumber) ?? 0 >= targetVersion {
// Only notify listeners if there is a change.
if let update = update, !update.updatedKeys.isEmpty {
self.realtimeLockQueue.async { [weak self] in
guard let self else { return }
Expand All @@ -443,15 +444,19 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {

@objc(scheduleFetch:targetVersion:) public
func scheduleFetch(remainingAttempts: Int, targetVersion: Int) {
// Needs fetch to occur between 0 - 3 seconds. Randomize to not cause DDoS
// alerts in backend.
let delay = TimeInterval.random(in: 0 ... 3) // Random delay between 0 and 3 seconds
realtimeLockQueue.asyncAfter(deadline: .now() + delay) {
self.fetchLatestConfig(remainingAttempts: remainingAttempts, targetVersion: targetVersion)
}
}

/// Perform fetch and handle developers callbacks.
@objc(autoFetch:targetVersion:) public
func autoFetch(attempts: Int, targetVersion: Int) {
realtimeLockQueue.async {
realtimeLockQueue.async { [weak self] in
guard let self else { return }
guard attempts > 0 else {
let error = NSError(domain: RemoteConfigUpdateErrorDomain,
code: RemoteConfigUpdateError.notFetched.rawValue,
Expand All @@ -468,13 +473,13 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {

// MARK: - URLSessionDataDelegate

/// Delegate to asynchronously handle every new notification that comes over
/// the wire. Auto-fetches and runs callback for each new notification.
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask,
didReceive data: Data) {
if !session.isEqual(self.session) {
return
}

let strData = String(data: data, encoding: .utf8) ?? ""
// If response data contains the API enablement link, return the entire
// message to the user in the form of a error.
if strData.contains(serverForbiddenStatusCode) {
let error = NSError(domain: RemoteConfigUpdateErrorDomain,
code: RemoteConfigUpdateError.streamError.rawValue,
Expand All @@ -496,7 +501,13 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {
evaluateStreamResponse(response)
}
} catch {
propagateErrors(error)
let wrappedError = NSError(domain: RemoteConfigUpdateErrorDomain,
code: RemoteConfigUpdateError.messageInvalid.rawValue,
userInfo: [
NSLocalizedDescriptionKey: "Unable to parse ConfigUpdate. \(strData)",
NSUnderlyingErrorKey: error,
])
propagateErrors(wrappedError)
return
}
}
Expand Down Expand Up @@ -534,13 +545,10 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {
statusCode == fetchResponseHTTPStatusCodeGatewayTimeout
}

/// Delegate to handle initial reply from the server.
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask,
didReceive response: URLResponse,
completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
if !session.isEqual(self.session) {
completionHandler(.cancel) // Cancel if not current session
return
}
isRequestInProgress = false
if let httpResponse = response as? HTTPURLResponse {
let statusCode = httpResponse.statusCode
Expand All @@ -555,8 +563,6 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {

if isStatusCodeRetryable(statusCode) {
retryHTTPConnection()
completionHandler(.cancel) // cancel the failing task

} else {
let error = NSError(
domain: RemoteConfigUpdateErrorDomain,
Expand All @@ -568,16 +574,17 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {
)
RCLog.error("I-RCN000021", "Cannot establish connection. Error: \(error)")
propagateErrors(error)
completionHandler(.cancel) // cancel the failing task
}
} else {
// On success, reset retry parameters.
remainingRetryCount = maxRetries
settings.realtimeRetryCount = 0
completionHandler(.allow)
}
completionHandler(.allow)
}
}

/// Delegate to handle data task completion.
public func urlSession(_ session: URLSession, task: URLSessionTask,
didCompleteWithError error: Error?) {
if !session.isEqual(self.session) {
Expand All @@ -591,10 +598,8 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {
retryHTTPConnection()
}

/// Delegate to handle session invalidation.
public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
if !session.isEqual(self.session) {
return
}
if !isRequestInProgress {
if let _ = error {
settings.updateRealtimeExponentialBackoffTime()
Expand All @@ -608,13 +613,15 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {

@objc public
func beginRealtimeStream() {
realtimeLockQueue.async {
realtimeLockQueue.async { [weak self] in
guard let self else { return }
guard self.settings.realtimeBackoffInterval() <= 0.0 else {
self.retryHTTPConnection()
return
}
if self.canMakeConnection() {
guard self.settings.realtimeBackoffInterval() <= 0.0 else {
self.retryHTTPConnection()
return
}
self.createRequestBody { requestBody in
self.createRequestBody { [weak self] requestBody in
guard let self else { return }
var request = self.request
request.httpBody = requestBody
self.isRequestInProgress = true
Expand All @@ -627,7 +634,8 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {

@objc public
func pauseRealtimeStream() {
realtimeLockQueue.async {
realtimeLockQueue.async { [weak self] in
guard let self else { return }
if let task = self.dataTask {
task.cancel()
self.dataTask = nil
Expand All @@ -639,7 +647,8 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {
@objc public func addConfigUpdateListener(_ listener: @Sendable @escaping (RemoteConfigUpdate?,
Error?) -> Void)
-> ConfigUpdateListenerRegistration {
realtimeLockQueue.async {
realtimeLockQueue.async { [weak self] in
guard let self else { return }
let temp = self.listeners.mutableCopy() as! NSMutableOrderedSet
temp.add(listener)
self.listeners = temp
Expand All @@ -650,7 +659,8 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {

@objc public func removeConfigUpdateListener(_ listener: @escaping (RemoteConfigUpdate?, Error?)
-> Void) {
realtimeLockQueue.async {
realtimeLockQueue.async { [weak self] in
guard let self else { return }
let temp: NSMutableOrderedSet = self.listeners.mutableCopy() as! NSMutableOrderedSet
temp.remove(listener)
self.listeners = temp
Expand Down

0 comments on commit b79b6d8

Please sign in to comment.