Skip to content

Commit d0c0c98

Browse files
committed
api fixes
1 parent e173fcb commit d0c0c98

File tree

5 files changed

+152
-39
lines changed

5 files changed

+152
-39
lines changed

FirebaseRemoteConfig/SwiftNew/RemoteConfig.swift

Lines changed: 116 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ public class RemoteConfig: NSObject, NSFastEnumeration {
148148

149149
@objc public var settings: ConfigSettings
150150

151-
private let configFetch: ConfigFetch
151+
let configFetch: ConfigFetch
152152

153153
private let configExperiment: ConfigExperiment
154154

@@ -201,8 +201,9 @@ public class RemoteConfig: NSObject, NSFastEnumeration {
201201
// Use the provider to generate and return instances of FIRRemoteConfig for this specific app and
202202
// namespace. This will ensure the app is configured before Remote Config can return an instance.
203203
@objc(remoteConfigWithFIRNamespace:app:)
204-
public static func remoteConfig(withFIRNamespace firebaseNamespace: String,
205-
app: FirebaseApp) -> RemoteConfig {
204+
public static func remoteConfig(withFIRNamespace firebaseNamespace: String = RemoteConfigConstants
205+
.NamespaceGoogleMobilePlatform,
206+
app: FirebaseApp) -> RemoteConfig {
206207
let provider = ComponentType<RemoteConfigInterop>
207208
.instance(
208209
for: RemoteConfigInterop.self,
@@ -366,10 +367,23 @@ public class RemoteConfig: NSObject, NSFastEnumeration {
366367
}
367368
}
368369

370+
/// Ensures initialization is complete and clients can begin querying for Remote Config values.
371+
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
372+
public func ensureInitialized() async throws {
373+
return try await withCheckedThrowingContinuation { continuation in
374+
self.ensureInitialized { error in
375+
if let error {
376+
continuation.resume(throwing: error)
377+
} else {
378+
continuation.resume()
379+
}
380+
}
381+
}
382+
}
383+
369384
/// Ensures initialization is complete and clients can begin querying for Remote Config values.
370385
/// - Parameter completionHandler: Initialization complete callback with error parameter.
371-
@objc public func ensureInitialized(withCompletionHandler completionHandler: @escaping (Error?)
372-
-> Void) {
386+
@objc public func ensureInitialized(completionHandler: @escaping (Error?) -> Void) {
373387
DispatchQueue.global(qos: .utility).async { [weak self] in
374388
guard let self = self else { return }
375389
let initializationSuccess = self.configContent.initializationSuccessful()
@@ -402,6 +416,27 @@ public class RemoteConfig: NSObject, NSFastEnumeration {
402416

403417
// MARK: fetch
404418

419+
/// Fetches Remote Config data with a callback. Call `activate()` to make fetched data
420+
/// available to your app.
421+
///
422+
/// Note: This method uses a Firebase Installations token to identify the app instance, and once
423+
/// it's called, it periodically sends data to the Firebase backend. (see
424+
/// `Installations.authToken(completion:)`).
425+
/// To stop the periodic sync, call `Installations.delete(completion:)`
426+
/// and avoid calling this method again.
427+
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
428+
public func fetch() async throws -> RemoteConfigFetchStatus {
429+
return try await withUnsafeThrowingContinuation() { continuation in
430+
self.fetch { status, error in
431+
if let error {
432+
continuation.resume(throwing: error)
433+
} else {
434+
continuation.resume(returning: status)
435+
}
436+
}
437+
}
438+
}
439+
405440
/// Fetches Remote Config data with a callback. Call `activate()` to make fetched data
406441
/// available to your app.
407442
///
@@ -419,6 +454,32 @@ public class RemoteConfig: NSObject, NSFastEnumeration {
419454
}
420455
}
421456

457+
/// Fetches Remote Config data and sets a duration that specifies how long config data lasts.
458+
/// Call `activateWithCompletion:` to make fetched data available to your app.
459+
///
460+
/// - Parameter expirationDuration Override the (default or optionally set `minimumFetchInterval`
461+
/// property in RemoteConfigSettings) `minimumFetchInterval` for only the current request, in
462+
/// seconds. Setting a value of 0 seconds will force a fetch to the backend.
463+
///
464+
/// Note: This method uses a Firebase Installations token to identify the app instance, and once
465+
/// it's called, it periodically sends data to the Firebase backend. (see
466+
/// `Installations.authToken(completion:)`).
467+
/// To stop the periodic sync, call `Installations.delete(completion:)`
468+
/// and avoid calling this method again.
469+
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
470+
public func fetch(withExpirationDuration expirationDuration: TimeInterval) async throws
471+
-> RemoteConfigFetchStatus {
472+
return try await withCheckedThrowingContinuation { continuation in
473+
self.fetch(withExpirationDuration: expirationDuration) { status, error in
474+
if let error {
475+
continuation.resume(throwing: error)
476+
} else {
477+
continuation.resume(returning: status)
478+
}
479+
}
480+
}
481+
}
482+
422483
/// Fetches Remote Config data and sets a duration that specifies how long config data lasts.
423484
/// Call `activateWithCompletion:` to make fetched data available to your app.
424485
///
@@ -440,6 +501,28 @@ public class RemoteConfig: NSObject, NSFastEnumeration {
440501

441502
// MARK: fetchAndActivate
442503

504+
/// Fetches Remote Config data and if successful, activates fetched data. Optional completion
505+
/// handler callback is invoked after the attempted activation of data, if the fetch call
506+
/// succeeded.
507+
///
508+
/// Note: This method uses a Firebase Installations token to identify the app instance, and once
509+
/// it's called, it periodically sends data to the Firebase backend. (see
510+
/// `Installations.authToken(completion:)`).
511+
/// To stop the periodic sync, call `Installations.delete(completion:)`
512+
/// and avoid calling this method again.
513+
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
514+
public func fetchAndActivate() async throws -> RemoteConfigFetchAndActivateStatus {
515+
return try await withCheckedThrowingContinuation { continuation in
516+
self.fetchAndActivate { status, error in
517+
if let error {
518+
continuation.resume(throwing: error)
519+
} else {
520+
continuation.resume(returning: status)
521+
}
522+
}
523+
}
524+
}
525+
443526
/// Fetches Remote Config data and if successful, activates fetched data. Optional completion
444527
/// handler callback is invoked after the attempted activation of data, if the fetch call
445528
/// succeeded.
@@ -451,8 +534,9 @@ public class RemoteConfig: NSObject, NSFastEnumeration {
451534
/// and avoid calling this method again.
452535
///
453536
/// - Parameter completionHandler Fetch operation callback with status and error parameters.
454-
@objc public func fetchAndActivate(withCompletionHandler completionHandler:
455-
@escaping (RemoteConfigFetchAndActivateStatus, Error?) -> Void) {
537+
@objc public func fetchAndActivate(completionHandler:
538+
((RemoteConfigFetchAndActivateStatus, Error?) -> Void)? =
539+
nil) {
456540
fetch { [weak self] status, error in
457541
guard let self = self else { return }
458542
// Fetch completed. We are being called on the main queue.
@@ -461,11 +545,13 @@ public class RemoteConfig: NSObject, NSFastEnumeration {
461545
self.activate { changed, error in
462546
let status: RemoteConfigFetchAndActivateStatus = error == nil ?
463547
.successFetchedFromRemote : .successUsingPreFetchedData
464-
DispatchQueue.main.async {
465-
completionHandler(status, nil)
548+
if let completionHandler {
549+
DispatchQueue.main.async {
550+
completionHandler(status, nil)
551+
}
466552
}
467553
}
468-
} else {
554+
} else if let completionHandler {
469555
DispatchQueue.main.async {
470556
completionHandler(.error, error)
471557
}
@@ -475,10 +561,26 @@ public class RemoteConfig: NSObject, NSFastEnumeration {
475561

476562
// MARK: activate
477563

564+
/// Applies Fetched Config data to the Active Config, causing updates to the behavior and
565+
/// appearance of the app to take effect (depending on how config data is used in the app).
566+
/// - Returns A Bool indicating whether or not a change occurred.
567+
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
568+
public func activate() async throws -> Bool {
569+
return try await withCheckedThrowingContinuation { continuation in
570+
self.activate { updated, error in
571+
if let error {
572+
continuation.resume(throwing: error)
573+
} else {
574+
continuation.resume(returning: updated)
575+
}
576+
}
577+
}
578+
}
579+
478580
/// Applies Fetched Config data to the Active Config, causing updates to the behavior and
479581
/// appearance of the app to take effect (depending on how config data is used in the app).
480582
/// - Parameter completion Activate operation callback with changed and error parameters.
481-
@objc public func activate(withCompletion completion: ((Bool, Error?) -> Void)?) {
583+
@objc public func activate(completion: ((Bool, Error?) -> Void)? = nil) {
482584
queue.async { [weak self] in
483585
guard let self = self else {
484586
let error = NSError(
@@ -759,8 +861,9 @@ public class RemoteConfig: NSObject, NSFastEnumeration {
759861
/// - Returns Returns a registration representing the listener. The registration
760862
/// contains a remove method, which can be used to stop receiving updates for the provided
761863
/// listener.
762-
@objc public func addOnConfigUpdateListener(_ listener: @Sendable @escaping (RemoteConfigUpdate?,
763-
Error?) -> Void)
864+
@objc public func addOnConfigUpdateListener(remoteConfigUpdateCompletion listener: @Sendable @escaping (RemoteConfigUpdate?,
865+
Error?)
866+
-> Void)
764867
-> ConfigUpdateListenerRegistration {
765868
return configRealtime.addConfigUpdateListener(listener)
766869
}

FirebaseRemoteConfig/Tests/Swift/FakeUtils/FakeConsole.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
@testable import FirebaseRemoteConfig
1516
#if SWIFT_PACKAGE
1617
import RemoteConfigFakeConsoleObjC
1718
#endif
@@ -31,13 +32,17 @@ class FakeConsole {
3132
func get() -> [String: AnyHashable] {
3233
if config.count == 0 {
3334
last = config
34-
return [RCNFetchResponseKeyState: RCNFetchResponseKeyStateEmptyConfig]
35+
return [ConfigConstants.fetchResponseKeyState: ConfigConstants
36+
.fetchResponseKeyStateEmptyConfig]
3537
}
36-
var state = RCNFetchResponseKeyStateNoChange
38+
var state = ConfigConstants.fetchResponseKeyStateNoChange
3739
if last != config {
38-
state = RCNFetchResponseKeyStateUpdate
40+
state = ConfigConstants.fetchResponseKeyStateUpdate
3941
}
4042
last = config
41-
return [RCNFetchResponseKeyState: state, RCNFetchResponseKeyEntries: config]
43+
return [
44+
ConfigConstants.fetchResponseKeyState: state,
45+
ConfigConstants.fetchResponseKeyEntries: config,
46+
]
4247
}
4348
}

FirebaseRemoteConfig/Tests/Swift/FakeUtils/URLSessionPartialMock.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class URLSessionMock: URLSession, @unchecked Sendable {
5353
completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void)
5454
-> URLSessionDataTask {
5555
let consoleValues = fakeConsole.get()
56-
if etag == "" || consoleValues["state"] as! String == RCNFetchResponseKeyStateUpdate {
56+
if etag == "" || consoleValues["state"] as! String == "UPDATE" {
5757
// Time string in microseconds to insure a different string from previous change.
5858
etag = String(NSDate().timeIntervalSince1970)
5959
}

FirebaseRemoteConfig/Tests/Swift/SwiftAPI/APITestBase.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import FirebaseCore
1616
import FirebaseInstallations
17-
import FirebaseRemoteConfig
17+
@testable import FirebaseRemoteConfig
1818

1919
#if SWIFT_PACKAGE
2020
import RemoteConfigFakeConsoleObjC
@@ -114,7 +114,7 @@ class APITestBase: XCTestCase {
114114
}
115115

116116
// Uncomment for verbose debug logging.
117-
// FirebaseConfiguration.shared.setLoggerLevel(FirebaseLoggerLevel.debug)
117+
FirebaseConfiguration.shared.setLoggerLevel(FirebaseLoggerLevel.debug)
118118
}
119119

120120
override func tearDown() {

FirebaseRemoteConfig/Tests/Swift/SwiftAPI/FirebaseRemoteConfigSwift_APIBuildTests.swift

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ import FirebaseRemoteConfig
1919
import FirebaseRemoteConfigInterop
2020

2121
final class FirebaseRemoteConfig_APIBuildTests: XCTestCase {
22-
func usage() throws {
22+
func usage(code: FirebaseRemoteConfig.RemoteConfigError,
23+
updateErrorCode: FirebaseRemoteConfig.RemoteConfigUpdateError) throws {
2324
// MARK: - FirebaseRemoteConfig
2425

2526
// TODO(ncooke3): These global constants should be lowercase.
@@ -51,13 +52,14 @@ final class FirebaseRemoteConfig_APIBuildTests: XCTestCase {
5152
let nsError = NSError(domain: "", code: 0, userInfo: nil)
5253

5354
// TODO(ncooke3): Global constants should be lowercase.
54-
let _: String = FirebaseRemoteConfig.RemoteConfigErrorDomain
55-
let _ = FirebaseRemoteConfig.RemoteConfigError(_nsError: nsError)
56-
let _: FirebaseRemoteConfig.RemoteConfigError.Code._ErrorType = FirebaseRemoteConfig
57-
.RemoteConfigError(_nsError: nsError)
58-
let _: String = FirebaseRemoteConfig.RemoteConfigError.errorDomain
59-
let code: FirebaseRemoteConfig.RemoteConfigError.Code? = nil
60-
switch code! {
55+
// TODO(paulb777): Decide if ok to break and add release note.
56+
// let _: String = FirebaseRemoteConfig.RemoteConfigErrorDomain
57+
// let _ = FirebaseRemoteConfig.RemoteConfigError(_nsError: nsError)
58+
// let _: FirebaseRemoteConfig.RemoteConfigError.Code._ErrorType = FirebaseRemoteConfig
59+
// .RemoteConfigError(_nsError: nsError)
60+
// let _: String = FirebaseRemoteConfig.RemoteConfigError.errorDomain
61+
// let code: FirebaseRemoteConfig.RemoteConfigError
62+
switch code {
6163
case .unknown: break
6264
case .throttled: break
6365
case .internalError: break
@@ -68,13 +70,14 @@ final class FirebaseRemoteConfig_APIBuildTests: XCTestCase {
6870
_ = FirebaseRemoteConfig.RemoteConfigError.internalError
6971

7072
// TODO(ncooke3): Global constants should be lowercase.
71-
let _: String = FirebaseRemoteConfig.RemoteConfigUpdateErrorDomain
72-
let _ = FirebaseRemoteConfig.RemoteConfigUpdateError(_nsError: nsError)
73-
let _: FirebaseRemoteConfig.RemoteConfigUpdateError.Code._ErrorType = FirebaseRemoteConfig
74-
.RemoteConfigUpdateError(_nsError: nsError)
75-
let _: String = FirebaseRemoteConfig.RemoteConfigUpdateError.errorDomain
76-
let updateErrorCode: FirebaseRemoteConfig.RemoteConfigUpdateError.Code? = nil
77-
switch updateErrorCode! {
73+
// TODO(paulb777): Decide if ok to break and add release note.
74+
// let _: String = FirebaseRemoteConfig.RemoteConfigUpdateErrorDomain
75+
// let _ = FirebaseRemoteConfig.RemoteConfigUpdateError(_nsError: nsError)
76+
// let _: FirebaseRemoteConfig.RemoteConfigUpdateError.Code._ErrorType = FirebaseRemoteConfig
77+
// .RemoteConfigUpdateError(_nsError: nsError)
78+
// let _: String = FirebaseRemoteConfig.RemoteConfigUpdateError.errorDomain
79+
// let updateErrorCode: FirebaseRemoteConfig.RemoteConfigUpdateError
80+
switch updateErrorCode {
7881
case .streamError: break
7982
case .notFetched: break
8083
case .messageInvalid: break
@@ -160,13 +163,16 @@ final class FirebaseRemoteConfig_APIBuildTests: XCTestCase {
160163
let _: FirebaseRemoteConfig.RemoteConfigValue = config["key"]
161164
let _: FirebaseRemoteConfig.RemoteConfigValue = config.configValue(forKey: "key")
162165
// TODO(ncooke3): Should `nil` be acceptable here in a Swift context?
163-
let _: FirebaseRemoteConfig.RemoteConfigValue = config.configValue(forKey: nil)
166+
let _: FirebaseRemoteConfig.RemoteConfigValue = config.configValue(forKey: "key")
164167
let _: FirebaseRemoteConfig.RemoteConfigValue = config.configValue(
165168
forKey: "key",
166169
source: source
167170
)
168171
// TODO(ncooke3): Should `nil` be acceptable here in a Swift context?
169-
let _: FirebaseRemoteConfig.RemoteConfigValue = config.configValue(forKey: nil, source: source)
172+
let _: FirebaseRemoteConfig.RemoteConfigValue = config.configValue(
173+
forKey: "key",
174+
source: source
175+
)
170176

171177
let _: [String] = config.allKeys(from: source)
172178

@@ -184,8 +190,7 @@ final class FirebaseRemoteConfig_APIBuildTests: XCTestCase {
184190
config.setDefaults(fromPlist: nil)
185191

186192
let _: FirebaseRemoteConfig.RemoteConfigValue? = config.defaultValue(forKey: "")
187-
// TODO(ncooke3): Should `nil` be acceptable here in a Swift context?
188-
let _: FirebaseRemoteConfig.RemoteConfigValue? = config.defaultValue(forKey: nil)
193+
let _: FirebaseRemoteConfig.RemoteConfigValue? = config.defaultValue(forKey: "key")
189194

190195
let _: FirebaseRemoteConfig.ConfigUpdateListenerRegistration = config
191196
.addOnConfigUpdateListener(

0 commit comments

Comments
 (0)