This is an automated email from the ASF dual-hosted git repository.

dpogue pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cordova-ios.git


The following commit(s) were added to refs/heads/master by this push:
     new 3a426fd3 refactor!: Add CDVSettingsDictionary class (#1458)
3a426fd3 is described below

commit 3a426fd37d478fb8ee93d6de613e55888c4fa02b
Author: Darryl Pogue <dar...@dpogue.ca>
AuthorDate: Tue Aug 20 11:32:24 2024 -0700

    refactor!: Add CDVSettingsDictionary class (#1458)
    
    This will replace the extension category on NSDictionary, which has
    always felt a little bit hacky and has caused issues in weird
    circumstances due to requiring special build flags to work properly.
---
 .../Classes/Private/CDVCommandDelegateImpl.m       |   2 +-
 .../Plugins/CDVWebViewEngine/CDVWebViewEngine.m    |  14 +-
 CordovaLib/Classes/Public/CDVSettingsDictionary.m  | 159 +++++++++++++++
 CordovaLib/Classes/Public/CDVViewController.m      |   5 +-
 .../Public/NSDictionary+CordovaPreferences.m       |   2 +
 CordovaLib/CordovaLib.xcodeproj/project.pbxproj    |  15 ++
 CordovaLib/include/Cordova/CDV.h                   |   5 +
 CordovaLib/include/Cordova/CDVCommandDelegate.h    |   3 +-
 CordovaLib/include/Cordova/CDVPlugin.h             |   1 +
 CordovaLib/include/Cordova/CDVSettingsDictionary.h | 136 ++++++++++++
 CordovaLib/include/Cordova/CDVViewController.h     |  11 +-
 .../Cordova/NSDictionary+CordovaPreferences.h      |  13 +-
 .../CDVSettingsDictionarySwiftTests.swift          |  27 +--
 tests/CordovaLibTests/CDVSettingsDictionaryTests.m | 227 +++++++++++++++++++++
 tests/CordovaLibTests/CDVWebViewEngineTest.m       |  10 +-
 .../CordovaLibTests.xcodeproj/project.pbxproj      |   6 +
 16 files changed, 604 insertions(+), 32 deletions(-)

diff --git a/CordovaLib/Classes/Private/CDVCommandDelegateImpl.m 
b/CordovaLib/Classes/Private/CDVCommandDelegateImpl.m
index bdb35dad..b019b2d0 100644
--- a/CordovaLib/Classes/Private/CDVCommandDelegateImpl.m
+++ b/CordovaLib/Classes/Private/CDVCommandDelegateImpl.m
@@ -173,7 +173,7 @@
     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 
0), block);
 }
 
-- (NSDictionary*)settings
+- (CDVSettingsDictionary*)settings
 {
     return _viewController.settings;
 }
diff --git 
a/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m 
b/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m
index 948c2f4b..95230026 100644
--- a/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m
+++ b/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m
@@ -20,7 +20,7 @@
 #import "CDVWebViewEngine.h"
 #import "CDVWebViewUIDelegate.h"
 #import <Cordova/CDVWebViewProcessPoolFactory.h>
-#import <Cordova/NSDictionary+CordovaPreferences.h>
+#import <Cordova/CDVSettingsDictionary.h>
 #import <Cordova/CDVURLSchemeHandler.h>
 
 #import <objc/message.h>
@@ -75,7 +75,7 @@
     return [self initWithFrame:frame configuration:nil];
 }
 
-- (WKWebViewConfiguration*) 
createConfigurationFromSettings:(NSDictionary*)settings
+- (WKWebViewConfiguration*) 
createConfigurationFromSettings:(CDVSettingsDictionary*)settings
 {
     WKWebViewConfiguration* configuration;
     if (_configuration) {
@@ -170,7 +170,7 @@
 {
     // viewController would be available now. we attempt to set all possible 
delegates to it, by default
     CDVViewController* vc = (CDVViewController*)self.viewController;
-    NSDictionary* settings = self.commandDelegate.settings;
+    CDVSettingsDictionary* settings = self.commandDelegate.settings;
 
     NSString *scheme = [settings cordovaSettingForKey:@"scheme"];
 
@@ -365,7 +365,7 @@
     return YES;
 }
 
-- (void)updateSettings:(NSDictionary*)settings
+- (void)updateSettings:(CDVSettingsDictionary*)settings
 {
     WKWebView* wkWebView = (WKWebView*)_engineWebView;
 
@@ -410,7 +410,7 @@
 - (void)updateWithInfo:(NSDictionary*)info
 {
     NSDictionary* scriptMessageHandlers = [info 
objectForKey:kCDVWebViewEngineScriptMessageHandlers];
-    NSDictionary* settings = [info 
objectForKey:kCDVWebViewEngineWebViewPreferences];
+    id settings = [info objectForKey:kCDVWebViewEngineWebViewPreferences];
     id navigationDelegate = [info 
objectForKey:kCDVWebViewEngineWKNavigationDelegate];
     id uiDelegate = [info objectForKey:kCDVWebViewEngineWKUIDelegate];
 
@@ -435,8 +435,10 @@
         wkWebView.UIDelegate = uiDelegate;
     }
 
-    if (settings && [settings isKindOfClass:[NSDictionary class]]) {
+    if (settings && [settings isKindOfClass:[CDVSettingsDictionary class]]) {
         [self updateSettings:settings];
+    } else if (settings && [settings isKindOfClass:[NSDictionary class]]) {
+        [self updateSettings:[[CDVSettingsDictionary alloc] 
initWithDictionary:settings]];
     }
 }
 
diff --git a/CordovaLib/Classes/Public/CDVSettingsDictionary.m 
b/CordovaLib/Classes/Public/CDVSettingsDictionary.m
new file mode 100644
index 00000000..0c9e5059
--- /dev/null
+++ b/CordovaLib/Classes/Public/CDVSettingsDictionary.m
@@ -0,0 +1,159 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+#import <Cordova/CDVSettingsDictionary.h>
+
+@interface CDVSettingsDictionary () {
+    // Ideally this should not be mutable, but we've got legacy API that allows
+    // plugins to set values in here, so this is the world we have to live in
+    NSMutableDictionary *_dict;
+}
+@end
+
+@implementation CDVSettingsDictionary
+
+- (instancetype)init
+{
+    return [self initWithDictionary:@{}];
+}
+
+- (instancetype)initWithDictionary:(NSDictionary *)dict
+{
+    self = [super init];
+    if (self != nil) {
+        if ([dict isKindOfClass:[NSMutableDictionary class]]) {
+            _dict = (NSMutableDictionary *)dict;
+        } else {
+            _dict = [NSMutableDictionary dictionaryWithDictionary:dict];
+        }
+    }
+    return self;
+}
+
+- (instancetype)initWithObjects:(const id _Nonnull [ _Nullable ])objects 
forKeys:(const id <NSCopying> _Nonnull [ _Nullable ])keys count:(NSUInteger)cnt
+{
+    self = [self init];
+    if (self != nil) {
+        _dict = [NSMutableDictionary dictionaryWithObjects:objects 
forKeys:keys count:cnt];
+    }
+    return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder
+{
+    NSMutableDictionary *dict = [[NSMutableDictionary alloc] 
initWithCoder:coder];
+
+    if (dict != nil) {
+        self = [self initWithDictionary:dict];
+    } else {
+        self = [self initWithDictionary:@{}];
+    }
+    return self;
+}
+
++ (BOOL)supportsSecureCoding
+{
+    return YES;
+}
+
+- (Class)classForCoder
+{
+    return [self class];
+}
+
+- (id)forwardingTargetForSelector:(SEL)selector
+{
+    return _dict;
+}
+
+- (NSUInteger)count
+{
+    return _dict.count;
+}
+
+- (id)objectForKey:(NSString *)key
+{
+    return [_dict objectForKey:[key lowercaseString]];
+}
+
+- (NSEnumerator *)keyEnumerator
+{
+    return [_dict keyEnumerator];
+}
+
+- (id)cordovaSettingForKey:(NSString *)key
+{
+    return [self objectForKey:key];
+}
+
+- (BOOL)cordovaBoolSettingForKey:(NSString *)key 
defaultValue:(BOOL)defaultValue
+{
+    BOOL value = defaultValue;
+
+    id prefObj = [self objectForKey:key];
+    if (prefObj == nil) {
+#ifdef DEBUG
+        NSLog(@"The preference key \"%@\" is not defined and will default to 
\"%@\"", key, (defaultValue ? @"TRUE" : @"FALSE"));
+#endif
+        return value;
+    }
+
+    if ([prefObj isKindOfClass:NSString.class]) {
+        prefObj = [prefObj lowercaseString];
+
+        if ([prefObj isEqualToString:@"true"] || [prefObj 
isEqualToString:@"1"] || [prefObj isEqualToString:@"yes"]) {
+            return YES;
+        } else if ([prefObj isEqualToString:@"false"] || [prefObj 
isEqualToString:@"0"] || [prefObj isEqualToString:@"no"]) {
+            return NO;
+        }
+    } else if ([prefObj isKindOfClass:NSNumber.class] && ([prefObj 
isEqual:@YES] || [prefObj isEqual:@NO])) {
+        return [prefObj isEqual:@YES];
+    }
+
+    return value;
+}
+
+- (CGFloat)cordovaFloatSettingForKey:(NSString *)key 
defaultValue:(CGFloat)defaultValue
+{
+    CGFloat value = defaultValue;
+
+    id prefObj = [self objectForKey:key];
+    if (prefObj != nil) {
+        value = [prefObj floatValue];
+    }
+
+    return value;
+}
+
+- (void)setObject:(id)value forKey:(NSString *)key
+{
+    [_dict setObject:value forKey:[key lowercaseString]];
+}
+
+- (void)setObject:(id)value forKeyedSubscript:(NSString *)key
+{
+    [_dict setObject:value forKey:[key lowercaseString]];
+}
+
+- (void)setCordovaSetting:(id)value forKey:(NSString *)key
+{
+    [self setObject:value forKey:key];
+}
+
+@end
diff --git a/CordovaLib/Classes/Public/CDVViewController.m 
b/CordovaLib/Classes/Public/CDVViewController.m
index 767a9a2d..dfaa18db 100644
--- a/CordovaLib/Classes/Public/CDVViewController.m
+++ b/CordovaLib/Classes/Public/CDVViewController.m
@@ -27,6 +27,7 @@
 #import <Cordova/CDVPlugin.h>
 #import "CDVPlugin+Private.h"
 #import <Cordova/CDVConfigParser.h>
+#import <Cordova/CDVSettingsDictionary.h>
 #import <Cordova/NSDictionary+CordovaPreferences.h>
 #import "CDVCommandDelegateImpl.h"
 
@@ -49,7 +50,7 @@ static UIColor* defaultBackgroundColor(void) {
 }
 
 @property (nonatomic, readwrite, strong) NSXMLParser* configParser;
-@property (nonatomic, readwrite, strong) NSMutableDictionary* settings;
+@property (nonatomic, readwrite, strong) CDVSettingsDictionary* settings;
 @property (nonatomic, readwrite, strong) NSMutableDictionary* pluginObjects;
 @property (nonatomic, readwrite, strong) NSMutableArray* startupPluginNames;
 @property (nonatomic, readwrite, strong) NSDictionary* pluginsMap;
@@ -178,7 +179,7 @@ static UIColor* defaultBackgroundColor(void) {
     // Get the plugin dictionary, allowList and settings from the delegate.
     self.pluginsMap = delegate.pluginsDict;
     self.startupPluginNames = delegate.startupPluginNames;
-    self.settings = delegate.settings;
+    self.settings = [[CDVSettingsDictionary alloc] 
initWithDictionary:delegate.settings];
 
     // And the start folder/page.
     if(self.wwwFolderName == nil){
diff --git a/CordovaLib/Classes/Public/NSDictionary+CordovaPreferences.m 
b/CordovaLib/Classes/Public/NSDictionary+CordovaPreferences.m
index a60004d9..3a1e8bc3 100644
--- a/CordovaLib/Classes/Public/NSDictionary+CordovaPreferences.m
+++ b/CordovaLib/Classes/Public/NSDictionary+CordovaPreferences.m
@@ -19,7 +19,9 @@
 
 @import Foundation;
 
+#define __CORDOVA_SILENCE_HEADER_DEPRECATIONS
 #import <Cordova/NSDictionary+CordovaPreferences.h>
+#undef __CORDOVA_SILENCE_HEADER_DEPRECATIONS
 
 @implementation NSDictionary (CordovaPreferences)
 
diff --git a/CordovaLib/CordovaLib.xcodeproj/project.pbxproj 
b/CordovaLib/CordovaLib.xcodeproj/project.pbxproj
index 5e16a895..ee7300f5 100644
--- a/CordovaLib/CordovaLib.xcodeproj/project.pbxproj
+++ b/CordovaLib/CordovaLib.xcodeproj/project.pbxproj
@@ -113,6 +113,10 @@
                9052DE8E2150D06B008E83D4 /* CDVIntentAndNavigationFilter.h in 
Headers */ = {isa = PBXBuildFile; fileRef = 3093E2211B16D6A3003F381A /* 
CDVIntentAndNavigationFilter.h */; };
                9052DE8F2150D06B008E83D4 /* CDVHandleOpenURL.h in Headers */ = 
{isa = PBXBuildFile; fileRef = 7ED95CF81AB9028C008C4574 /* CDVHandleOpenURL.h 
*/; };
                9059F51C26F2CE2400B3B2B7 /* CDVURLSchemeHandler.h in Headers */ 
= {isa = PBXBuildFile; fileRef = 2F4D42BA23F218BA00501999 /* 
CDVURLSchemeHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               9068B5332C6DFE2000B13532 /* CDVSettingsDictionary.h in Headers 
*/ = {isa = PBXBuildFile; fileRef = 9068B5322C6DFE2000B13532 /* 
CDVSettingsDictionary.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               9068B5342C6DFE2000B13532 /* CDVSettingsDictionary.h in Headers 
*/ = {isa = PBXBuildFile; fileRef = 9068B5322C6DFE2000B13532 /* 
CDVSettingsDictionary.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               9068B5362C6E007400B13532 /* CDVSettingsDictionary.m in Sources 
*/ = {isa = PBXBuildFile; fileRef = 9068B5352C6E007400B13532 /* 
CDVSettingsDictionary.m */; };
+               9068B5372C6E007400B13532 /* CDVSettingsDictionary.m in Sources 
*/ = {isa = PBXBuildFile; fileRef = 9068B5352C6E007400B13532 /* 
CDVSettingsDictionary.m */; };
                90B382512AEB72DD00F3F4D7 /* PrivacyInfo.xcprivacy in Resources 
*/ = {isa = PBXBuildFile; fileRef = 902D0BC12AEB64EB009C68E5 /* 
PrivacyInfo.xcprivacy */; };
                90DE61742B8F11D300810C2E /* Cordova.h in Headers */ = {isa = 
PBXBuildFile; fileRef = C0C01EB41E3911D50056E6CB /* Cordova.h */; settings = 
{ATTRIBUTES = (Public, ); }; };
                A3B082D41BB15CEA00D8DC35 /* CDVGestureHandler.h in Headers */ = 
{isa = PBXBuildFile; fileRef = A3B082D21BB15CEA00D8DC35 /* CDVGestureHandler.h 
*/; };
@@ -193,6 +197,8 @@
                902D0BC12AEB64EB009C68E5 /* PrivacyInfo.xcprivacy */ = {isa = 
PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; 
sourceTree = "<group>"; };
                9036843B2C6EB06500A3338C /* CDVAllowList.h */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CDVAllowList.h; 
sourceTree = "<group>"; };
                9036843C2C6EB06500A3338C /* CDVAllowList.m */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CDVAllowList.m; 
sourceTree = "<group>"; };
+               9068B5322C6DFE2000B13532 /* CDVSettingsDictionary.h */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.c.h; path = 
CDVSettingsDictionary.h; sourceTree = "<group>"; };
+               9068B5352C6E007400B13532 /* CDVSettingsDictionary.m */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = 
CDVSettingsDictionary.m; sourceTree = "<group>"; };
                A3B082D21BB15CEA00D8DC35 /* CDVGestureHandler.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
CDVGestureHandler.h; sourceTree = "<group>"; };
                A3B082D31BB15CEA00D8DC35 /* CDVGestureHandler.m */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path 
= CDVGestureHandler.m; sourceTree = "<group>"; };
                C0C01EB21E3911D50056E6CB /* Cordova.framework */ = {isa = 
PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; 
path = Cordova.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -326,6 +332,7 @@
                                7ED95D1E1AB9029B008C4574 /* 
CDVPlugin+Resources.m */,
                                7ED95D201AB9029B008C4574 /* CDVPlugin.m */,
                                7ED95D221AB9029B008C4574 /* CDVPluginResult.m 
*/,
+                               9068B5352C6E007400B13532 /* 
CDVSettingsDictionary.m */,
                                7ED95D251AB9029B008C4574 /* CDVTimer.m */,
                                4E23F8F523E16E96006CD852 /* 
CDVWebViewProcessPoolFactory.m */,
                                7ED95D2B1AB9029B008C4574 /* CDVViewController.m 
*/,
@@ -361,6 +368,7 @@
                                7ED95D1F1AB9029B008C4574 /* CDVPlugin.h */,
                                7ED95D211AB9029B008C4574 /* CDVPluginResult.h 
*/,
                                7ED95D231AB9029B008C4574 /* 
CDVScreenOrientationDelegate.h */,
+                               9068B5322C6DFE2000B13532 /* 
CDVSettingsDictionary.h */,
                                7ED95D241AB9029B008C4574 /* CDVTimer.h */,
                                7ED95D2A1AB9029B008C4574 /* CDVViewController.h 
*/,
                                4E23F8F923E16E96006CD852 /* 
CDVWebViewProcessPoolFactory.h */,
@@ -391,6 +399,7 @@
                                C0C01EB61E3911D50056E6CB /* Cordova.h in 
Headers */,
                                C0C01EBB1E39131A0056E6CB /* CDV.h in Headers */,
                                C0C01EBC1E39131A0056E6CB /* CDVAppDelegate.h in 
Headers */,
+                               9068B5332C6DFE2000B13532 /* 
CDVSettingsDictionary.h in Headers */,
                                C0C01EBD1E39131A0056E6CB /* CDVAvailability.h 
in Headers */,
                                C0C01EBE1E39131A0056E6CB /* 
CDVAvailabilityDeprecated.h in Headers */,
                                C0C01EBF1E39131A0056E6CB /* 
CDVCommandDelegate.h in Headers */,
@@ -430,6 +439,7 @@
                                7ED95D351AB9029B008C4574 /* CDV.h in Headers */,
                                7ED95D361AB9029B008C4574 /* CDVAppDelegate.h in 
Headers */,
                                7ED95D381AB9029B008C4574 /* CDVAvailability.h 
in Headers */,
+                               9068B5342C6DFE2000B13532 /* 
CDVSettingsDictionary.h in Headers */,
                                7ED95D391AB9029B008C4574 /* 
CDVAvailabilityDeprecated.h in Headers */,
                                7ED95D3A1AB9029B008C4574 /* 
CDVCommandDelegate.h in Headers */,
                                7ED95D3B1AB9029B008C4574 /* 
CDVCommandDelegateImpl.h in Headers */,
@@ -506,6 +516,7 @@
                0867D690FE84028FC02AAC07 /* Project object */ = {
                        isa = PBXProject;
                        attributes = {
+                               BuildIndependentTargetsInParallel = YES;
                                LastUpgradeCheck = 1010;
                                TargetAttributes = {
                                        C0C01EB11E3911D50056E6CB = {
@@ -559,6 +570,7 @@
                                9052DE742150D040008E83D4 /* CDVConfigParser.m 
in Sources */,
                                9052DE752150D040008E83D4 /* 
CDVInvokedUrlCommand.m in Sources */,
                                9052DE762150D040008E83D4 /* 
CDVPlugin+Resources.m in Sources */,
+                               9068B5362C6E007400B13532 /* 
CDVSettingsDictionary.m in Sources */,
                                9052DE772150D040008E83D4 /* CDVPlugin.m in 
Sources */,
                                9052DE782150D040008E83D4 /* CDVPluginResult.m 
in Sources */,
                                9036843F2C6EB06500A3338C /* CDVAllowList.m in 
Sources */,
@@ -589,6 +601,7 @@
                                7ED95D401AB9029B008C4574 /* CDVConfigParser.m 
in Sources */,
                                7ED95D421AB9029B008C4574 /* 
CDVInvokedUrlCommand.m in Sources */,
                                7ED95D441AB9029B008C4574 /* 
CDVPlugin+Resources.m in Sources */,
+                               9068B5372C6E007400B13532 /* 
CDVSettingsDictionary.m in Sources */,
                                2F4D42BD23F218BA00501999 /* 
CDVURLSchemeHandler.m in Sources */,
                                7ED95D461AB9029B008C4574 /* CDVPlugin.m in 
Sources */,
                                9036843E2C6EB06500A3338C /* CDVAllowList.m in 
Sources */,
@@ -638,6 +651,7 @@
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
+                               
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = 
YES_AGGRESSIVE;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
@@ -708,6 +722,7 @@
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
+                               
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = 
YES_AGGRESSIVE;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
diff --git a/CordovaLib/include/Cordova/CDV.h b/CordovaLib/include/Cordova/CDV.h
index 83913dc9..08fcd100 100644
--- a/CordovaLib/include/Cordova/CDV.h
+++ b/CordovaLib/include/Cordova/CDV.h
@@ -17,6 +17,8 @@
  under the License.
  */
 
+#define __CORDOVA_SILENCE_HEADER_DEPRECATIONS
+
 #import <Cordova/CDVAvailability.h>
 #import <Cordova/CDVAvailabilityDeprecated.h>
 #import <Cordova/CDVAppDelegate.h>
@@ -28,6 +30,7 @@
 #import <Cordova/CDVConfigParser.h>
 #import <Cordova/CDVInvokedUrlCommand.h>
 #import <Cordova/CDVPlugin+Resources.h>
+#import <Cordova/CDVSettingsDictionary.h>
 #import <Cordova/CDVWebViewEngineProtocol.h>
 #import <Cordova/CDVWebViewProcessPoolFactory.h>
 #import <Cordova/NSDictionary+CordovaPreferences.h>
@@ -35,3 +38,5 @@
 #import <Cordova/CDVScreenOrientationDelegate.h>
 #import <Cordova/CDVTimer.h>
 #import <Cordova/CDVURLSchemeHandler.h>
+
+#undef __CORDOVA_SILENCE_HEADER_DEPRECATIONS
diff --git a/CordovaLib/include/Cordova/CDVCommandDelegate.h 
b/CordovaLib/include/Cordova/CDVCommandDelegate.h
index 2efe4a48..ff830e18 100644
--- a/CordovaLib/include/Cordova/CDVCommandDelegate.h
+++ b/CordovaLib/include/Cordova/CDVCommandDelegate.h
@@ -22,12 +22,13 @@
 
 @class CDVPlugin;
 @class CDVPluginResult;
+@class CDVSettingsDictionary;
 
 typedef NSURL* (^ UrlTransformerBlock)(NSURL*);
 
 @protocol CDVCommandDelegate <NSObject>
 
-@property (nonatomic, readonly) NSDictionary* settings;
+@property (nonatomic, readonly) CDVSettingsDictionary* settings;
 @property (nonatomic, copy) UrlTransformerBlock urlTransformer;
 
 - (NSString*)pathForResource:(NSString*)resourcepath;
diff --git a/CordovaLib/include/Cordova/CDVPlugin.h 
b/CordovaLib/include/Cordova/CDVPlugin.h
index cc3a1bf0..cff1caf2 100644
--- a/CordovaLib/include/Cordova/CDVPlugin.h
+++ b/CordovaLib/include/Cordova/CDVPlugin.h
@@ -23,6 +23,7 @@
 #import <Cordova/CDVPluginResult.h>
 #import <Cordova/NSMutableArray+QueueAdditions.h>
 #import <Cordova/CDVCommandDelegate.h>
+#import <Cordova/CDVSettingsDictionary.h>
 #import <Cordova/CDVViewController.h>
 #import <Cordova/CDVWebViewEngineProtocol.h>
 
diff --git a/CordovaLib/include/Cordova/CDVSettingsDictionary.h 
b/CordovaLib/include/Cordova/CDVSettingsDictionary.h
new file mode 100644
index 00000000..e197db7e
--- /dev/null
+++ b/CordovaLib/include/Cordova/CDVSettingsDictionary.h
@@ -0,0 +1,136 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ A dictionary-like interface providing access to the preference settings for a 
Cordova web view.
+ */
+@interface CDVSettingsDictionary : NSDictionary
+
+/**
+ The number of entries in the dictionary.
+ */
+@property(readonly) NSUInteger count;
+
+/**
+ Initializes a newly allocated dictionary by placing in it the keys and values
+ contained in another given dictionary.
+
+ - Parameters:
+   - dict: A dictionary containing the keys and values with which to initialize
+           the new dictionary.
+ - Returns: An initialized dictionary containing the keys and values found in 
`dict`.
+ */
+- (instancetype)initWithDictionary:(NSDictionary *)dict 
NS_DESIGNATED_INITIALIZER;
+
+/**
+ Returns the value associated with a given key.
+
+ - Parameters:
+   - key:  The key for which to return the corresponding value.
+ - Returns:  The value associated with `key`, or `nil` if no value is 
associated with `key`.
+ */
+- (id)objectForKey:(NSString *)key;
+
+/**
+ Provides an enumerator to access the keys in the dictionary.
+
+ - Returns: An enumerator object that lets you access each key in the 
dictionary.
+ */
+- (NSEnumerator *)keyEnumerator;
+
+/**
+ Returns the value associated with a given key.
+
+ - Parameters:
+   - key:  The key for which to return the corresponding value.
+ - Returns:  The value associated with `key`, or `nil` if no value is 
associated with `key`.
+ */
+- (id)cordovaSettingForKey:(NSString *)key;
+
+/**
+ Returns the boolean value associated with a given key, or the given default
+ value if the key is not found.
+
+ - Parameters:
+   - key:  The key for which to return the corresponding value.
+   - defaultValue: The default value to return if the key is missing.
+ - Returns:  The value associated with `key`, or the provided default value.
+ */
+- (BOOL)cordovaBoolSettingForKey:(NSString *)key 
defaultValue:(BOOL)defaultValue;
+
+/**
+ Returns the floating-point numeric value associated with a given key, or the
+ given default value if the key is not found.
+
+ - Parameters:
+   - key:  The key for which to return the corresponding value.
+   - defaultValue: The default value to return if the key is missing.
+ - Returns:  The value associated with `key`, or the provided default value.
+ */
+- (CGFloat)cordovaFloatSettingForKey:(NSString *)key 
defaultValue:(CGFloat)defaultValue;
+
+/**
+ Adds a preference with the given name and value to the dictionary.
+
+ > Warning: Use of this method is highly discouraged. Preferences should be set
+ > and customized by app authors in the Cordova XML configuration file, not
+ > changed at runtime by plugins.
+
+ - Parameters:
+   - value: The value to be stored with the given key.
+   - key: The preference name.
+ */
+- (void)setObject:(id)value forKey:(NSString *)key;
+
+/**
+ Adds a preference with the given name and value to the dictionary.
+
+ You shouldn’t need to call this method directly. Instead, this method is 
called
+ when setting an object for a key using subscripting.
+
+ > Warning: Use of this method is highly discouraged. Preferences should be set
+ > and customized by app authors in the Cordova XML configuration file, not
+ > changed at runtime by plugins.
+
+ - Parameters:
+   - value: The value to be stored with the given key.
+   - key: The preference name.
+ */
+- (void)setObject:(id)value forKeyedSubscript:(NSString *)key;
+
+/**
+ Adds a preference with the given name and value to the dictionary.
+
+ > Warning: Use of this method is highly discouraged. Preferences should be set
+ > and customized by app authors in the Cordova XML configuration file, not
+ > changed at runtime by plugins.
+
+ - Parameters:
+   - value: The value to be stored with the given key.
+   - key: The preference name.
+ */
+- (void)setCordovaSetting:(id)value forKey:(NSString *)key;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/CordovaLib/include/Cordova/CDVViewController.h 
b/CordovaLib/include/Cordova/CDVViewController.h
index 760ab103..7549fc47 100644
--- a/CordovaLib/include/Cordova/CDVViewController.h
+++ b/CordovaLib/include/Cordova/CDVViewController.h
@@ -24,6 +24,7 @@
 #import <Cordova/CDVInvokedUrlCommand.h>
 #import <Cordova/CDVCommandDelegate.h>
 #import <Cordova/CDVCommandQueue.h>
+#import <Cordova/CDVSettingsDictionary.h>
 #import <Cordova/CDVScreenOrientationDelegate.h>
 #import <Cordova/CDVWebViewEngineProtocol.h>
 
@@ -37,7 +38,15 @@ NS_ASSUME_NONNULL_BEGIN
 
 @property (nullable, nonatomic, readonly, strong) NSMutableDictionary* 
pluginObjects;
 @property (nonatomic, readonly, strong) NSDictionary* pluginsMap;
-@property (nonatomic, readonly, strong) NSMutableDictionary* settings;
+
+/**
+ The Cordova preferences for this view.
+
+ This is a dictionary populated from the preference key/value pairs in the
+ Cordova XML configuration file.
+ */
+@property (nonatomic, readonly, strong) CDVSettingsDictionary* settings;
+
 @property (nonatomic, readonly, strong) NSXMLParser* configParser;
 
 @property (nonatomic, readwrite, copy) NSString* appScheme;
diff --git a/CordovaLib/include/Cordova/NSDictionary+CordovaPreferences.h 
b/CordovaLib/include/Cordova/NSDictionary+CordovaPreferences.h
index 9be2be2d..0d41c83d 100644
--- a/CordovaLib/include/Cordova/NSDictionary+CordovaPreferences.h
+++ b/CordovaLib/include/Cordova/NSDictionary+CordovaPreferences.h
@@ -19,17 +19,22 @@
 
 #import <Foundation/Foundation.h>
 #import <UIKit/UIKit.h>
+#import <Cordova/CDVAvailabilityDeprecated.h>
+
+#ifndef __CORDOVA_SILENCE_HEADER_DEPRECATIONS
+#warning "Use CDVSettingsDictionary.h and the CDVSettingsDictionary class 
instead"
+#endif
 
 @interface NSDictionary (CordovaPreferences)
 
-- (id)cordovaSettingForKey:(NSString*)key;
-- (BOOL)cordovaBoolSettingForKey:(NSString*)key 
defaultValue:(BOOL)defaultValue;
-- (CGFloat)cordovaFloatSettingForKey:(NSString*)key 
defaultValue:(CGFloat)defaultValue;
+- (id)cordovaSettingForKey:(NSString*)key CDV_DEPRECATED(8, "Use 
CDVSettingsDictionary");
+- (BOOL)cordovaBoolSettingForKey:(NSString*)key 
defaultValue:(BOOL)defaultValue CDV_DEPRECATED(8, "Use CDVSettingsDictionary");
+- (CGFloat)cordovaFloatSettingForKey:(NSString*)key 
defaultValue:(CGFloat)defaultValue CDV_DEPRECATED(8, "Use 
CDVSettingsDictionary");
 
 @end
 
 @interface NSMutableDictionary (CordovaPreferences)
 
-- (void)setCordovaSetting:(id)value forKey:(NSString*)key;
+- (void)setCordovaSetting:(id)value forKey:(NSString*)key CDV_DEPRECATED(8, 
"Use CDVSettingsDictionary");
 
 @end
diff --git a/CordovaLib/include/Cordova/NSDictionary+CordovaPreferences.h 
b/tests/CordovaLibTests/CDVSettingsDictionarySwiftTests.swift
similarity index 62%
copy from CordovaLib/include/Cordova/NSDictionary+CordovaPreferences.h
copy to tests/CordovaLibTests/CDVSettingsDictionarySwiftTests.swift
index 9be2be2d..a64f76b1 100644
--- a/CordovaLib/include/Cordova/NSDictionary+CordovaPreferences.h
+++ b/tests/CordovaLibTests/CDVSettingsDictionarySwiftTests.swift
@@ -17,19 +17,22 @@
  under the License.
  */
 
-#import <Foundation/Foundation.h>
-#import <UIKit/UIKit.h>
+import XCTest
 
-@interface NSDictionary (CordovaPreferences)
+let testSettings = [
+    "disallowoverscroll": true
+];
 
-- (id)cordovaSettingForKey:(NSString*)key;
-- (BOOL)cordovaBoolSettingForKey:(NSString*)key 
defaultValue:(BOOL)defaultValue;
-- (CGFloat)cordovaFloatSettingForKey:(NSString*)key 
defaultValue:(CGFloat)defaultValue;
+class CDVSettingsDictionarySwiftTests: XCTestCase
+{
+    func testInitWithSwiftDictionary()
+    {
+        let dict = CDVSettingsDictionary(dictionary:testSettings);
 
-@end
+        XCTAssertEqual(1, dict.count);
+        XCTAssertEqual(true, dict["DisallowOverscroll"] as? Bool);
 
-@interface NSMutableDictionary (CordovaPreferences)
-
-- (void)setCordovaSetting:(id)value forKey:(NSString*)key;
-
-@end
+        dict.setObject(false, forKey:"DisallowOverScroll");
+        XCTAssertEqual(false, dict["disallowoverscroll"] as? Bool);
+    }
+}
diff --git a/tests/CordovaLibTests/CDVSettingsDictionaryTests.m 
b/tests/CordovaLibTests/CDVSettingsDictionaryTests.m
new file mode 100644
index 00000000..59d5b6f7
--- /dev/null
+++ b/tests/CordovaLibTests/CDVSettingsDictionaryTests.m
@@ -0,0 +1,227 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+#import <XCTest/XCTest.h>
+#import <Cordova/CDVSettingsDictionary.h>
+
+static NSDictionary *testSettings;
+
+@interface CDVSettingsDictionary (Testing)
+// Not actually implemented, but should forward to the internal dictionary
+- (void)removeObjectForKey:(NSString *)key;
+@end
+
+@interface CDVSettingsDictionaryTests : XCTestCase
+@end
+
+@implementation CDVSettingsDictionaryTests
+
+- (void)setUp
+{
+    [super setUp];
+
+    testSettings = @{
+        @"test": @"CDVSettingsDictionary Test",
+        @"minimumfontsize": @1.1,
+        @"disallowoverscroll": @YES,
+        @"mediatypesrequiringuseractionforplayback": @"all"
+    };
+}
+
+- (void)tearDown
+{
+    [super tearDown];
+}
+
+- (void)testEmptyInit
+{
+    CDVSettingsDictionary *dict = [[CDVSettingsDictionary alloc] init];
+
+    XCTAssertEqual(0, dict.count, @"Newly initialized settings dictionary is 
not empty");
+    XCTAssertEqualObjects(@[], [dict allKeys], @"Newly initialized settings 
dictionary had keys");
+    XCTAssertEqualObjects(@[], [dict allValues], @"Newly initialized settings 
dictionary had values");
+    XCTAssertNil([dict objectForKey:@"Test"], @"Value found with 
objectForKey:");
+    XCTAssertNil([dict cordovaSettingForKey:@"Test"], @"Value found with 
objectForKey:");
+}
+
+- (void)testInitWithDictionary
+{
+    CDVSettingsDictionary *dict = [[CDVSettingsDictionary alloc] 
initWithDictionary:testSettings];
+
+    XCTAssertEqual(4, dict.count, @"Incorrect dictionary length");
+    XCTAssertEqual(4, [[dict allKeys] count], @"Incorrect number of keys");
+    XCTAssertEqual(4, [[dict allValues] count], @"Incorrect number of values");
+    XCTAssertNotNil([dict objectForKey:@"Test"], @"Value not found with 
objectForKey:");
+    XCTAssertNotNil([dict cordovaSettingForKey:@"Test"], @"Value not found 
with objectForKey:");
+    XCTAssertTrue([dict isEqualToDictionary:testSettings], @"Not equal to 
creating dictionary");
+}
+
+- (void)testInitWithCoder
+{
+    CDVSettingsDictionary *dict = [[CDVSettingsDictionary alloc] 
initWithDictionary:testSettings];
+
+    NSError *err = nil;
+    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:dict 
requiringSecureCoding:YES error:&err];
+    XCTAssertNil(err);
+
+    err = nil;
+    id result = [NSKeyedUnarchiver 
unarchivedObjectOfClass:[CDVSettingsDictionary class] fromData:data error:&err];
+    XCTAssertNil(err);
+
+    XCTAssertTrue([dict isEqualToDictionary:result], @"Not equal to creating 
dictionary");
+}
+
+- (void)testInitWithObjectsForKeys
+{
+    CDVSettingsDictionary* dict = [[CDVSettingsDictionary alloc] 
initWithObjects:[testSettings allValues] forKeys:[testSettings allKeys]];
+    XCTAssertTrue([dict isEqualToDictionary:testSettings], @"Not equal to 
creating dictionary");
+}
+
+- (void)testCreateWithDictionary
+{
+    CDVSettingsDictionary *dict = [CDVSettingsDictionary 
dictionaryWithDictionary:testSettings];
+    XCTAssertTrue([dict isEqualToDictionary:testSettings], @"Not equal to 
creating dictionary");
+}
+
+- (void)testKeyAccessCaseInsensitive
+{
+    CDVSettingsDictionary* dict = [[CDVSettingsDictionary alloc] 
initWithDictionary:testSettings];
+
+    XCTAssertEqualObjects(@YES, [dict objectForKey:@"DisallowOverscroll"], 
@"Uppercase key name failed to match");
+    XCTAssertEqualObjects(@YES, [dict objectForKey:@"disallowoverscroll"], 
@"Lowercase key name failed to match");
+
+    XCTAssertEqualObjects(@YES, [dict 
cordovaSettingForKey:@"DisallowOverscroll"], @"Uppercase key name failed to 
match");
+    XCTAssertEqualObjects(@YES, [dict 
cordovaSettingForKey:@"disallowoverscroll"], @"Lowercase key name failed to 
match");
+
+    XCTAssertEqualObjects(@YES, dict[@"DisallowOverscroll"], @"Uppercase key 
name failed to match");
+    XCTAssertEqualObjects(@YES, dict[@"disallowoverscroll"], @"Lowercase key 
name failed to match");
+}
+
+- (void)testSetObjectForKeyCaseInsensitive
+{
+    CDVSettingsDictionary *dict = [[CDVSettingsDictionary alloc] init];
+
+    [dict setObject:@NO forKey:@"AllowInlineMediaPlayback"];
+    XCTAssertEqualObjects(@NO, [dict 
objectForKey:@"AllowInlineMediaPlayback"], @"Uppercase key name failed to 
match");
+    XCTAssertEqualObjects(@NO, [dict 
objectForKey:@"allowinlinemediaplayback"], @"Lowercase key name failed to 
match");
+
+    [dict setObject:@YES forKey:@"allowinlinemediaplayback"];
+    XCTAssertEqualObjects(@YES, [dict 
cordovaSettingForKey:@"AllowInlineMediaPlayback"], @"Uppercase key name failed 
to match");
+    XCTAssertEqualObjects(@YES, [dict 
cordovaSettingForKey:@"allowinlinemediaplayback"], @"Lowercase key name failed 
to match");
+
+    XCTAssertEqual(1, dict.count, @"Incorrect dictionary length");
+}
+
+- (void)testSetCordovaSettingForKeyCaseInsensitive
+{
+    CDVSettingsDictionary *dict = [[CDVSettingsDictionary alloc] init];
+
+    [dict setCordovaSetting:@NO forKey:@"AllowInlineMediaPlayback"];
+    XCTAssertEqualObjects(@NO, [dict 
cordovaSettingForKey:@"AllowInlineMediaPlayback"], @"Uppercase key name failed 
to match");
+    XCTAssertEqualObjects(@NO, [dict 
cordovaSettingForKey:@"allowinlinemediaplayback"], @"Lowercase key name failed 
to match");
+
+    [dict setCordovaSetting:@YES forKey:@"allowinlinemediaplayback"];
+    XCTAssertEqualObjects(@YES, [dict 
objectForKey:@"AllowInlineMediaPlayback"], @"Uppercase key name failed to 
match");
+    XCTAssertEqualObjects(@YES, [dict 
objectForKey:@"allowinlinemediaplayback"], @"Lowercase key name failed to 
match");
+
+    XCTAssertEqual(1, dict.count, @"Incorrect dictionary length");
+}
+
+- (void)testSubscriptSetForKeyCaseInsensitive
+{
+    CDVSettingsDictionary *dict = [[CDVSettingsDictionary alloc] init];
+
+    dict[@"AllowInlineMediaPlayback"] = @NO;
+    XCTAssertEqualObjects(@NO, [dict 
cordovaSettingForKey:@"AllowInlineMediaPlayback"], @"Uppercase key name failed 
to match");
+    XCTAssertEqualObjects(@NO, [dict 
cordovaSettingForKey:@"allowinlinemediaplayback"], @"Lowercase key name failed 
to match");
+
+    dict[@"allowinlinemediaplayback"] = @YES;
+    XCTAssertEqualObjects(@YES, [dict 
objectForKey:@"AllowInlineMediaPlayback"], @"Uppercase key name failed to 
match");
+    XCTAssertEqualObjects(@YES, [dict 
objectForKey:@"allowinlinemediaplayback"], @"Lowercase key name failed to 
match");
+
+    XCTAssertEqual(1, dict.count, @"Incorrect dictionary length");
+}
+
+- (void)testMessageForwardingToDictionary
+{
+    CDVSettingsDictionary *dict = [[CDVSettingsDictionary alloc] 
initWithDictionary:testSettings];
+
+    XCTAssertEqual(4, dict.count, @"Incorrect dictionary length");
+    [dict removeObjectForKey:@"test"];
+    XCTAssertEqual(3, dict.count, @"Incorrect dictionary length after 
removal");
+}
+
+- (void)testGetWithBoolDefaultValue
+{
+    CDVSettingsDictionary *dict = [[CDVSettingsDictionary alloc] 
initWithDictionary:@{
+        @"btruthy": @YES,
+        @"bfalsy": @NO,
+        @"itruthy": @1,
+        @"ifalsy": @0,
+        @"struthy": @"true",
+        @"sfalsy": @"false",
+        @"sbtruthy": @"yes",
+        @"sbfalsy": @"no",
+        @"sitruthy": @"1",
+        @"sifalsy": @"0",
+
+        @"nonbool": @"some string"
+    }];
+
+    XCTAssertTrue([dict cordovaBoolSettingForKey:@"bTruthy" defaultValue:NO]);
+    XCTAssertTrue([dict cordovaBoolSettingForKey:@"iTruthy" defaultValue:NO]);
+    XCTAssertTrue([dict cordovaBoolSettingForKey:@"sTruthy" defaultValue:NO]);
+    XCTAssertTrue([dict cordovaBoolSettingForKey:@"sbTruthy" defaultValue:NO]);
+    XCTAssertTrue([dict cordovaBoolSettingForKey:@"siTruthy" defaultValue:NO]);
+
+    XCTAssertFalse([dict cordovaBoolSettingForKey:@"bFalsy" defaultValue:YES]);
+    XCTAssertFalse([dict cordovaBoolSettingForKey:@"iFalsy" defaultValue:YES]);
+    XCTAssertFalse([dict cordovaBoolSettingForKey:@"sFalsy" defaultValue:YES]);
+    XCTAssertFalse([dict cordovaBoolSettingForKey:@"sbFalsy" 
defaultValue:YES]);
+    XCTAssertFalse([dict cordovaBoolSettingForKey:@"siFalsy" 
defaultValue:YES]);
+
+    XCTAssertTrue([dict cordovaBoolSettingForKey:@"nonBool" defaultValue:YES]);
+    XCTAssertTrue([dict cordovaBoolSettingForKey:@"nonExistentKey" 
defaultValue:YES]);
+
+    XCTAssertFalse([dict cordovaBoolSettingForKey:@"nonBool" defaultValue:NO]);
+    XCTAssertFalse([dict cordovaBoolSettingForKey:@"nonExistentKey" 
defaultValue:NO]);
+}
+
+- (void)testGetWithFloatDefaultValue
+{
+    CDVSettingsDictionary *dict = [[CDVSettingsDictionary alloc] 
initWithDictionary:@{
+        @"floatvalue": @3.14,
+        @"nonfloat": @"some string",
+        @"boolvalue": @YES
+    }];
+
+    XCTAssertEqualWithAccuracy(3.14, [dict 
cordovaFloatSettingForKey:@"FloatValue" defaultValue:0.0], 0.001);
+
+    // NSString floatValue always returns 0.0 for non-numeric strings
+    XCTAssertEqualWithAccuracy(0.0, [dict 
cordovaFloatSettingForKey:@"nonFloat" defaultValue:0.0], 0.001);
+    XCTAssertEqualWithAccuracy(0.0, [dict 
cordovaFloatSettingForKey:@"nonFloat" defaultValue:1.0], 0.001);
+
+    // NSBoolean floatValue converts YES to 1.0
+    XCTAssertEqualWithAccuracy(1.0, [dict 
cordovaFloatSettingForKey:@"BoolValue" defaultValue:0.0], 0.001);
+
+    XCTAssertEqualWithAccuracy(0.0, [dict 
cordovaFloatSettingForKey:@"NonExistentKey" defaultValue:0.0], 0.001);
+    XCTAssertEqualWithAccuracy(1.0, [dict 
cordovaFloatSettingForKey:@"NonExistentKey" defaultValue:1.0], 0.001);
+}
+
+@end
diff --git a/tests/CordovaLibTests/CDVWebViewEngineTest.m 
b/tests/CordovaLibTests/CDVWebViewEngineTest.m
index 962d9434..ee9971d8 100644
--- a/tests/CordovaLibTests/CDVWebViewEngineTest.m
+++ b/tests/CordovaLibTests/CDVWebViewEngineTest.m
@@ -21,7 +21,7 @@
 #import <XCTest/XCTest.h>
 #import "CDVWebViewEngine.h"
 #import "CDVWebViewProcessPoolFactory.h"
-#import <Cordova/NSDictionary+CordovaPreferences.h>
+#import <Cordova/CDVSettingsDictionary.h>
 #import <Cordova/CDVAvailability.h>
 
 @interface CDVWebViewEngineTest : XCTestCase
@@ -42,7 +42,7 @@
 @interface CDVViewController ()
 
 // expose property as readwrite, for test purposes
-@property (nonatomic, readwrite, strong) NSMutableDictionary* settings;
+@property (nonatomic, readwrite, strong) CDVSettingsDictionary* settings;
 
 @end
 
@@ -130,7 +130,7 @@
     self.viewController = [[CDVViewController alloc] init];
 
     // generate the app settings
-    NSDictionary* settings = @{
+    CDVSettingsDictionary* settings = [[CDVSettingsDictionary alloc] 
initWithDictionary:@{
                                   [@"MinimumFontSize" lowercaseString] : @1.1, 
// default is 0.0
                                   [@"AllowInlineMediaPlayback" 
lowercaseString] : @YES, // default is NO
                                   [@"MediaTypesRequiringUserActionForPlayback" 
lowercaseString] : @"all", // default is none
@@ -138,9 +138,9 @@
                                   [@"AllowsAirPlayForMediaPlayback" 
lowercaseString] : @NO, // default is YES
                                   [@"DisallowOverscroll" lowercaseString] : 
@YES, // so bounces is to be NO. defaults to NO
                                   [@"WKWebViewDecelerationSpeed" 
lowercaseString] : @"fast" // default is 'normal'
-                                  };
+                                  }];
     // this can be set because of the Category at the top of the file
-    self.viewController.settings = [settings mutableCopy];
+    self.viewController.settings = settings;
 
     // app settings are read after you register the plugin
     [self.viewController registerPlugin:self.plugin 
withClassName:NSStringFromClass([self.plugin class])];
diff --git a/tests/CordovaLibTests/CordovaLibTests.xcodeproj/project.pbxproj 
b/tests/CordovaLibTests/CordovaLibTests.xcodeproj/project.pbxproj
index c67c0f5a..6a74217c 100644
--- a/tests/CordovaLibTests/CordovaLibTests.xcodeproj/project.pbxproj
+++ b/tests/CordovaLibTests/CordovaLibTests.xcodeproj/project.pbxproj
@@ -44,6 +44,8 @@
                902530FC29A6DC53004AF1CF /* CDVPluginInitTests.m in Sources */ 
= {isa = PBXBuildFile; fileRef = 902530FA29A6DC53004AF1CF /* 
CDVPluginInitTests.m */; };
                908630C829A6D003002963FA /* SwiftInitPlugin.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 908630C729A6D003002963FA /* 
SwiftInitPlugin.swift */; };
                908630C929A6D003002963FA /* SwiftInitPlugin.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 908630C729A6D003002963FA /* 
SwiftInitPlugin.swift */; };
+               90A775DD2C6F257500FC5BB0 /* CDVSettingsDictionaryTests.m in 
Sources */ = {isa = PBXBuildFile; fileRef = 90A775DC2C6F256D00FC5BB0 /* 
CDVSettingsDictionaryTests.m */; };
+               90A775DE2C6F257500FC5BB0 /* CDVSettingsDictionaryTests.m in 
Sources */ = {isa = PBXBuildFile; fileRef = 90A775DC2C6F256D00FC5BB0 /* 
CDVSettingsDictionaryTests.m */; };
                C0FA7C9B1E4BB6420077B045 /* main.m in Sources */ = {isa = 
PBXBuildFile; fileRef = 303A4073152124BB00182201 /* main.m */; };
                C0FA7C9C1E4BB6420077B045 /* AppDelegate.m in Sources */ = {isa 
= PBXBuildFile; fileRef = 303A4077152124BB00182201 /* AppDelegate.m */; };
                C0FA7C9D1E4BB6420077B045 /* ViewController.m in Sources */ = 
{isa = PBXBuildFile; fileRef = 303A407A152124BB00182201 /* ViewController.m */; 
};
@@ -123,6 +125,7 @@
                7EF33BD61911ABA20048544E /* default-5...@2x.png */ = {isa = 
PBXFileReference; lastKnownFileType = image.png; path = "default-5...@2x.png"; 
sourceTree = "<group>"; };
                902530FA29A6DC53004AF1CF /* CDVPluginInitTests.m */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path 
= CDVPluginInitTests.m; sourceTree = "<group>"; };
                908630C729A6D003002963FA /* SwiftInitPlugin.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
SwiftInitPlugin.swift; sourceTree = "<group>"; };
+               90A775DC2C6F256D00FC5BB0 /* CDVSettingsDictionaryTests.m */ = 
{isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = 
CDVSettingsDictionaryTests.m; sourceTree = "<group>"; };
                C0FA7CAA1E4BB6420077B045 /* CordovaFrameworkApp.app */ = {isa = 
PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; 
path = CordovaFrameworkApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
                C0FA7CAC1E4BB6B30077B045 /* Cordova.framework */ = {isa = 
PBXFileReference; lastKnownFileType = wrapper.framework; name = 
Cordova.framework; path = "../Debug-iphonesimulator/Cordova.framework"; 
sourceTree = BUILT_PRODUCTS_DIR; };
                C0FA7CC71E4BBBBE0077B045 /* CordovaFrameworkTests.xctest */ = 
{isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 
0; path = CordovaFrameworkTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -239,6 +242,7 @@
                EB3B34F4161B585D003DBE7D /* CordovaLibTests */ = {
                        isa = PBXGroup;
                        children = (
+                               90A775DC2C6F256D00FC5BB0 /* 
CDVSettingsDictionaryTests.m */,
                                902530FA29A6DC53004AF1CF /* 
CDVPluginInitTests.m */,
                                4E23F90123E175AD006CD852 /* 
CDVWebViewEngineTest.m */,
                                30D1B08B15A2B36D0060C291 /* CDVBase64Tests.m */,
@@ -467,6 +471,7 @@
                        files = (
                                3035621714104C34006C2D43 /* CDVAllowListTests.m 
in Sources */,
                                686357BA141002F200DF4CF2 /* 
CDVPluginResultJSONSerializationTests.m in Sources */,
+                               90A775DE2C6F257500FC5BB0 /* 
CDVSettingsDictionaryTests.m in Sources */,
                                902530FB29A6DC53004AF1CF /* 
CDVPluginInitTests.m in Sources */,
                                30D1B08C15A2B36D0060C291 /* CDVBase64Tests.m in 
Sources */,
                                EBA3554615A731F100F4DE24 /* 
CDVFakeFileManager.m in Sources */,
@@ -496,6 +501,7 @@
                                C0FA7CB61E4BBBBE0077B045 /* 
CDVPluginResultJSONSerializationTests.m in Sources */,
                                902530FC29A6DC53004AF1CF /* 
CDVPluginInitTests.m in Sources */,
                                C0FA7CB91E4BBBBE0077B045 /* CDVBase64Tests.m in 
Sources */,
+                               90A775DD2C6F257500FC5BB0 /* 
CDVSettingsDictionaryTests.m in Sources */,
                                C0FA7CBA1E4BBBBE0077B045 /* 
CDVFakeFileManager.m in Sources */,
                                C0FA7CBB1E4BBBBE0077B045 /* 
CDVViewControllerTest.m in Sources */,
                                C0FA7CBC1E4BBBBE0077B045 /* 
CDVInvokedUrlCommandTests.m in Sources */,


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@cordova.apache.org
For additional commands, e-mail: commits-h...@cordova.apache.org


Reply via email to