@@ -417,6 +417,7 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {
417417 }
418418 if status == . success {
419419 if Int ( self . configFetch. templateVersionNumber) ?? 0 >= targetVersion {
420+ // Only notify listeners if there is a change.
420421 if let update = update, !update. updatedKeys. isEmpty {
421422 self . realtimeLockQueue. async { [ weak self] in
422423 guard let self else { return }
@@ -443,15 +444,19 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {
443444
444445 @objc ( scheduleFetch: targetVersion: ) public
445446 func scheduleFetch( remainingAttempts: Int , targetVersion: Int ) {
447+ // Needs fetch to occur between 0 - 3 seconds. Randomize to not cause DDoS
448+ // alerts in backend.
446449 let delay = TimeInterval . random ( in: 0 ... 3 ) // Random delay between 0 and 3 seconds
447450 realtimeLockQueue. asyncAfter ( deadline: . now( ) + delay) {
448451 self . fetchLatestConfig ( remainingAttempts: remainingAttempts, targetVersion: targetVersion)
449452 }
450453 }
451454
455+ /// Perform fetch and handle developers callbacks.
452456 @objc ( autoFetch: targetVersion: ) public
453457 func autoFetch( attempts: Int , targetVersion: Int ) {
454- realtimeLockQueue. async {
458+ realtimeLockQueue. async { [ weak self] in
459+ guard let self else { return }
455460 guard attempts > 0 else {
456461 let error = NSError ( domain: RemoteConfigUpdateErrorDomain,
457462 code: RemoteConfigUpdateError . notFetched. rawValue,
@@ -468,13 +473,13 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {
468473
469474 // MARK: - URLSessionDataDelegate
470475
476+ /// Delegate to asynchronously handle every new notification that comes over
477+ /// the wire. Auto-fetches and runs callback for each new notification.
471478 public func urlSession( _ session: URLSession , dataTask: URLSessionDataTask ,
472479 didReceive data: Data ) {
473- if !session. isEqual ( self . session) {
474- return
475- }
476-
477480 let strData = String ( data: data, encoding: . utf8) ?? " "
481+ // If response data contains the API enablement link, return the entire
482+ // message to the user in the form of a error.
478483 if strData. contains ( serverForbiddenStatusCode) {
479484 let error = NSError ( domain: RemoteConfigUpdateErrorDomain,
480485 code: RemoteConfigUpdateError . streamError. rawValue,
@@ -496,7 +501,13 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {
496501 evaluateStreamResponse ( response)
497502 }
498503 } catch {
499- propagateErrors ( error)
504+ let wrappedError = NSError ( domain: RemoteConfigUpdateErrorDomain,
505+ code: RemoteConfigUpdateError . messageInvalid. rawValue,
506+ userInfo: [
507+ NSLocalizedDescriptionKey: " Unable to parse ConfigUpdate. \( strData) " ,
508+ NSUnderlyingErrorKey: error,
509+ ] )
510+ propagateErrors ( wrappedError)
500511 return
501512 }
502513 }
@@ -534,13 +545,10 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {
534545 statusCode == fetchResponseHTTPStatusCodeGatewayTimeout
535546 }
536547
548+ /// Delegate to handle initial reply from the server.
537549 public func urlSession( _ session: URLSession , dataTask: URLSessionDataTask ,
538550 didReceive response: URLResponse ,
539551 completionHandler: @escaping ( URLSession . ResponseDisposition ) -> Void ) {
540- if !session. isEqual ( self . session) {
541- completionHandler ( . cancel) // Cancel if not current session
542- return
543- }
544552 isRequestInProgress = false
545553 if let httpResponse = response as? HTTPURLResponse {
546554 let statusCode = httpResponse. statusCode
@@ -555,8 +563,6 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {
555563
556564 if isStatusCodeRetryable ( statusCode) {
557565 retryHTTPConnection ( )
558- completionHandler ( . cancel) // cancel the failing task
559-
560566 } else {
561567 let error = NSError (
562568 domain: RemoteConfigUpdateErrorDomain,
@@ -568,16 +574,17 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {
568574 )
569575 RCLog . error ( " I-RCN000021 " , " Cannot establish connection. Error: \( error) " )
570576 propagateErrors ( error)
571- completionHandler ( . cancel) // cancel the failing task
572577 }
573578 } else {
579+ // On success, reset retry parameters.
574580 remainingRetryCount = maxRetries
575581 settings. realtimeRetryCount = 0
576- completionHandler ( . allow)
577582 }
583+ completionHandler ( . allow)
578584 }
579585 }
580586
587+ /// Delegate to handle data task completion.
581588 public func urlSession( _ session: URLSession , task: URLSessionTask ,
582589 didCompleteWithError error: Error ? ) {
583590 if !session. isEqual ( self . session) {
@@ -591,10 +598,8 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {
591598 retryHTTPConnection ( )
592599 }
593600
601+ /// Delegate to handle session invalidation.
594602 public func urlSession( _ session: URLSession , didBecomeInvalidWithError error: Error ? ) {
595- if !session. isEqual ( self . session) {
596- return
597- }
598603 if !isRequestInProgress {
599604 if let _ = error {
600605 settings. updateRealtimeExponentialBackoffTime ( )
@@ -608,13 +613,15 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {
608613
609614 @objc public
610615 func beginRealtimeStream( ) {
611- realtimeLockQueue. async {
616+ realtimeLockQueue. async { [ weak self] in
617+ guard let self else { return }
618+ guard self . settings. realtimeBackoffInterval ( ) <= 0.0 else {
619+ self . retryHTTPConnection ( )
620+ return
621+ }
612622 if self . canMakeConnection ( ) {
613- guard self . settings. realtimeBackoffInterval ( ) <= 0.0 else {
614- self . retryHTTPConnection ( )
615- return
616- }
617- self . createRequestBody { requestBody in
623+ self . createRequestBody { [ weak self] requestBody in
624+ guard let self else { return }
618625 var request = self . request
619626 request. httpBody = requestBody
620627 self . isRequestInProgress = true
@@ -627,7 +634,8 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {
627634
628635 @objc public
629636 func pauseRealtimeStream( ) {
630- realtimeLockQueue. async {
637+ realtimeLockQueue. async { [ weak self] in
638+ guard let self else { return }
631639 if let task = self . dataTask {
632640 task. cancel ( )
633641 self . dataTask = nil
@@ -639,7 +647,8 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {
639647 @objc public func addConfigUpdateListener( _ listener: @Sendable @escaping ( RemoteConfigUpdate ? ,
640648 Error ? ) -> Void )
641649 -> ConfigUpdateListenerRegistration {
642- realtimeLockQueue. async {
650+ realtimeLockQueue. async { [ weak self] in
651+ guard let self else { return }
643652 let temp = self . listeners. mutableCopy ( ) as! NSMutableOrderedSet
644653 temp. add ( listener)
645654 self . listeners = temp
@@ -650,7 +659,8 @@ class ConfigRealtime: NSObject, URLSessionDataDelegate {
650659
651660 @objc public func removeConfigUpdateListener( _ listener: @escaping ( RemoteConfigUpdate ? , Error ? )
652661 -> Void ) {
653- realtimeLockQueue. async {
662+ realtimeLockQueue. async { [ weak self] in
663+ guard let self else { return }
654664 let temp : NSMutableOrderedSet = self . listeners. mutableCopy ( ) as! NSMutableOrderedSet
655665 temp. remove ( listener)
656666 self . listeners = temp
0 commit comments