|
34 | 34 | /// Remote Config Error Domain.
|
35 | 35 | /// TODO: Rename according to obj-c style for constants.
|
36 | 36 | NSString *const FIRRemoteConfigErrorDomain = @"com.google.remoteconfig.ErrorDomain";
|
| 37 | +// Remote Config Custom Signals Error Domain |
| 38 | +NSString *const FIRRemoteConfigCustomSignalsErrorDomain = |
| 39 | + @"com.google.remoteconfig.customsignals.ErrorDomain"; |
37 | 40 | // Remote Config Realtime Error Domain
|
38 | 41 | NSString *const FIRRemoteConfigUpdateErrorDomain = @"com.google.remoteconfig.update.ErrorDomain";
|
39 | 42 | /// Remote Config Error Info End Time Seconds;
|
|
47 | 50 | @"FIRRemoteConfigActivateNotification";
|
48 | 51 | static NSNotificationName FIRRolloutsStateDidChangeNotificationName =
|
49 | 52 | @"FIRRolloutsStateDidChangeNotification";
|
| 53 | +/// Maximum allowed length for a custom signal key (in characters). |
| 54 | +static const NSUInteger FIRRemoteConfigCustomSignalsMaxKeyLength = 250; |
| 55 | +/// Maximum allowed length for a string value in custom signals (in characters). |
| 56 | +static const NSUInteger FIRRemoteConfigCustomSignalsMaxStringValueLength = 500; |
| 57 | +/// Maximum number of custom signals allowed. |
| 58 | +static const NSUInteger FIRRemoteConfigCustomSignalsMaxCount = 100; |
50 | 59 |
|
51 | 60 | /// Listener for the get methods.
|
52 | 61 | typedef void (^FIRRemoteConfigListener)(NSString *_Nonnull, NSDictionary *_Nonnull);
|
@@ -237,6 +246,103 @@ - (void)callListeners:(NSString *)key config:(NSDictionary *)config {
|
237 | 246 | }
|
238 | 247 | }
|
239 | 248 |
|
| 249 | +- (void)setCustomSignals:(nonnull NSDictionary<NSString *, NSObject *> *)customSignals |
| 250 | + withCompletion:(void (^_Nullable)(NSError *_Nullable error))completionHandler { |
| 251 | + void (^setCustomSignalsBlock)(void) = ^{ |
| 252 | + // Validate value type, and key and value length |
| 253 | + for (NSString *key in customSignals) { |
| 254 | + NSObject *value = customSignals[key]; |
| 255 | + if (![value isKindOfClass:[NSNull class]] && ![value isKindOfClass:[NSString class]] && |
| 256 | + ![value isKindOfClass:[NSNumber class]]) { |
| 257 | + if (completionHandler) { |
| 258 | + dispatch_async(dispatch_get_main_queue(), ^{ |
| 259 | + NSError *error = |
| 260 | + [NSError errorWithDomain:FIRRemoteConfigCustomSignalsErrorDomain |
| 261 | + code:FIRRemoteConfigCustomSignalsErrorInvalidValueType |
| 262 | + userInfo:@{ |
| 263 | + NSLocalizedDescriptionKey : |
| 264 | + @"Invalid value type. Must be NSString, NSNumber or NSNull" |
| 265 | + }]; |
| 266 | + completionHandler(error); |
| 267 | + }); |
| 268 | + } |
| 269 | + return; |
| 270 | + } |
| 271 | + |
| 272 | + if (key.length > FIRRemoteConfigCustomSignalsMaxKeyLength || |
| 273 | + ([value isKindOfClass:[NSString class]] && |
| 274 | + [(NSString *)value length] > FIRRemoteConfigCustomSignalsMaxStringValueLength)) { |
| 275 | + if (completionHandler) { |
| 276 | + dispatch_async(dispatch_get_main_queue(), ^{ |
| 277 | + NSError *error = [NSError |
| 278 | + errorWithDomain:FIRRemoteConfigCustomSignalsErrorDomain |
| 279 | + code:FIRRemoteConfigCustomSignalsErrorLimitExceeded |
| 280 | + userInfo:@{ |
| 281 | + NSLocalizedDescriptionKey : [NSString |
| 282 | + stringWithFormat:@"Custom signal keys and string values must be " |
| 283 | + @"%lu and %lu characters or less respectively.", |
| 284 | + FIRRemoteConfigCustomSignalsMaxKeyLength, |
| 285 | + FIRRemoteConfigCustomSignalsMaxStringValueLength] |
| 286 | + }]; |
| 287 | + completionHandler(error); |
| 288 | + }); |
| 289 | + } |
| 290 | + return; |
| 291 | + } |
| 292 | + } |
| 293 | + |
| 294 | + // Merge new signals with existing ones, overwriting existing keys. |
| 295 | + // Also, remove entries where the new value is null. |
| 296 | + NSMutableDictionary<NSString *, NSString *> *newCustomSignals = |
| 297 | + [[NSMutableDictionary alloc] initWithDictionary:self->_settings.customSignals]; |
| 298 | + |
| 299 | + for (NSString *key in customSignals) { |
| 300 | + NSObject *value = customSignals[key]; |
| 301 | + if (![value isKindOfClass:[NSNull class]]) { |
| 302 | + NSString *stringValue = [value isKindOfClass:[NSNumber class]] |
| 303 | + ? [(NSNumber *)value stringValue] |
| 304 | + : (NSString *)value; |
| 305 | + [newCustomSignals setObject:stringValue forKey:key]; |
| 306 | + } else { |
| 307 | + [newCustomSignals removeObjectForKey:key]; |
| 308 | + } |
| 309 | + } |
| 310 | + |
| 311 | + // Check the size limit. |
| 312 | + if (newCustomSignals.count > FIRRemoteConfigCustomSignalsMaxCount) { |
| 313 | + if (completionHandler) { |
| 314 | + dispatch_async(dispatch_get_main_queue(), ^{ |
| 315 | + NSError *error = [NSError |
| 316 | + errorWithDomain:FIRRemoteConfigCustomSignalsErrorDomain |
| 317 | + code:FIRRemoteConfigCustomSignalsErrorLimitExceeded |
| 318 | + userInfo:@{ |
| 319 | + NSLocalizedDescriptionKey : [NSString |
| 320 | + stringWithFormat:@"Custom signals count exceeds the limit of %lu.", |
| 321 | + FIRRemoteConfigCustomSignalsMaxCount] |
| 322 | + }]; |
| 323 | + completionHandler(error); |
| 324 | + }); |
| 325 | + } |
| 326 | + return; |
| 327 | + } |
| 328 | + |
| 329 | + // Update only if there are changes. |
| 330 | + if (![newCustomSignals isEqualToDictionary:self->_settings.customSignals]) { |
| 331 | + self->_settings.customSignals = newCustomSignals; |
| 332 | + } |
| 333 | + // Log the keys of the updated custom signals. |
| 334 | + FIRLogDebug(kFIRLoggerRemoteConfig, @"I-RCN000078", @"Keys of updated custom signals: %@", |
| 335 | + [newCustomSignals allKeys]); |
| 336 | + |
| 337 | + if (completionHandler) { |
| 338 | + dispatch_async(dispatch_get_main_queue(), ^{ |
| 339 | + completionHandler(nil); |
| 340 | + }); |
| 341 | + } |
| 342 | + }; |
| 343 | + dispatch_async(_queue, setCustomSignalsBlock); |
| 344 | +} |
| 345 | + |
240 | 346 | #pragma mark - fetch
|
241 | 347 |
|
242 | 348 | - (void)fetchWithCompletionHandler:(FIRRemoteConfigFetchCompletion)completionHandler {
|
|
0 commit comments