diff --git a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj index d5297cc11b..b6f67624a0 100644 --- a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj +++ b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ 623D691D254C7462002D01D1 /* CAPInstanceConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 623D691B254C7462002D01D1 /* CAPInstanceConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; 623D691E254C7462002D01D1 /* CAPInstanceConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 623D691C254C7462002D01D1 /* CAPInstanceConfiguration.m */; }; 625AF1ED258963C700869675 /* WebViewAssetHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625AF1EC258963C700869675 /* WebViewAssetHandler.swift */; }; + 6263686025F6EC0100576C1C /* PluginCallAccessorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6263685F25F6EC0100576C1C /* PluginCallAccessorTests.m */; }; 62959B162524DA7800A3D7F1 /* CAPPluginCall.h in Headers */ = {isa = PBXBuildFile; fileRef = 62959AE22524DA7700A3D7F1 /* CAPPluginCall.h */; settings = {ATTRIBUTES = (Public, ); }; }; 62959B172524DA7800A3D7F1 /* JSExport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62959AE32524DA7700A3D7F1 /* JSExport.swift */; }; 62959B192524DA7800A3D7F1 /* CAPBridgedPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 62959AE52524DA7700A3D7F1 /* CAPBridgedPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -153,6 +154,7 @@ 623D691B254C7462002D01D1 /* CAPInstanceConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CAPInstanceConfiguration.h; sourceTree = ""; }; 623D691C254C7462002D01D1 /* CAPInstanceConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CAPInstanceConfiguration.m; sourceTree = ""; }; 625AF1EC258963C700869675 /* WebViewAssetHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebViewAssetHandler.swift; sourceTree = ""; }; + 6263685F25F6EC0100576C1C /* PluginCallAccessorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PluginCallAccessorTests.m; sourceTree = ""; }; 62959AE22524DA7700A3D7F1 /* CAPPluginCall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPPluginCall.h; sourceTree = ""; }; 62959AE32524DA7700A3D7F1 /* JSExport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSExport.swift; sourceTree = ""; }; 62959AE52524DA7700A3D7F1 /* CAPBridgedPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPBridgedPlugin.h; sourceTree = ""; }; @@ -275,6 +277,7 @@ 62FABD2A25AE6182007B3814 /* BridgedTypesHelper.swift */, 621ECCC7254204BE00D3D615 /* JSONSerializationWrapper.h */, 621ECCC6254204BE00D3D615 /* JSONSerializationWrapper.m */, + 6263685F25F6EC0100576C1C /* PluginCallAccessorTests.m */, 621ECCCD254204C400D3D615 /* CapacitorTests-Bridging-Header.h */, 62959BBD2526510200A3D7F1 /* Info.plist */, ); @@ -603,6 +606,7 @@ 62A91C3425535F5700861508 /* ConfigurationTests.swift in Sources */, 62FABD2325AE60BA007B3814 /* BridgedTypesTests.m in Sources */, 621ECCC3254204B700D3D615 /* BridgedTypesTests.swift in Sources */, + 6263686025F6EC0100576C1C /* PluginCallAccessorTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/Capacitor/Capacitor/CAPBridgedJSTypes.h b/ios/Capacitor/Capacitor/CAPBridgedJSTypes.h index ecf3129642..516c8648b8 100644 --- a/ios/Capacitor/Capacitor/CAPBridgedJSTypes.h +++ b/ios/Capacitor/Capacitor/CAPBridgedJSTypes.h @@ -10,6 +10,8 @@ - (NSString * _Nullable)getString:(NSString * _Nonnull)key defaultValue:(NSString * _Nullable)defaultValue; - (NSDate * _Nullable)getDate:(NSString * _Nonnull)key defaultValue:(NSDate * _Nullable)defaultValue; - (NSDictionary * _Nullable)getObject:(NSString * _Nonnull)key defaultValue:(NSDictionary * _Nullable)defaultValue; +- (NSNumber * _Nullable)getNumber:(NSString * _Nonnull)key defaultValue:(NSNumber * _Nullable)defaultValue; +- (BOOL)getBool:(NSString * _Nonnull)key defaultValue:(BOOL)defaultValue; @end @interface CAPPluginCall (BridgedJSProtocol) diff --git a/ios/Capacitor/Capacitor/CAPBridgedJSTypes.m b/ios/Capacitor/Capacitor/CAPBridgedJSTypes.m index 686531a3a4..fb6da4586f 100644 --- a/ios/Capacitor/Capacitor/CAPBridgedJSTypes.m +++ b/ios/Capacitor/Capacitor/CAPBridgedJSTypes.m @@ -12,7 +12,10 @@ - (NSString * _Nullable)getString:(NSString * _Nonnull)key defaultValue:(NSStrin - (NSDate * _Nullable)getDate:(NSString * _Nonnull)key defaultValue:(NSDate * _Nullable)defaultValue { id value = [[self dictionaryRepresentation] objectForKey:key]; - if (value != nil && [value isKindOfClass:[NSString class]]) { + if (value != nil && [value isKindOfClass:[NSDate class]]) { + return value; + } + else if (value != nil && [value isKindOfClass:[NSString class]]) { return [[[self class] jsDateFormatter] dateFromString:value]; } return defaultValue; @@ -25,4 +28,16 @@ - (NSDictionary * _Nullable)getObject:(NSString * _Nonnull)key defaultValue:(NSD } return defaultValue; } + +- (NSNumber * _Nullable)getNumber:(NSString * _Nonnull)key defaultValue:(NSNumber * _Nullable)defaultValue { + id value = [[self dictionaryRepresentation] objectForKey:key]; + if (value != nil && [value isKindOfClass:[NSNumber class]]) { + return value; + } + return defaultValue; +} + +- (BOOL)getBool:(NSString * _Nonnull)key defaultValue:(BOOL)defaultValue { + return [[self getNumber:key defaultValue:[NSNumber numberWithBool:defaultValue]] boolValue]; +} @end diff --git a/ios/Capacitor/Capacitor/CAPPlugin.h b/ios/Capacitor/Capacitor/CAPPlugin.h index d0814cb253..4f77e31f37 100644 --- a/ios/Capacitor/Capacitor/CAPPlugin.h +++ b/ios/Capacitor/Capacitor/CAPPlugin.h @@ -42,8 +42,8 @@ // need to override init() -(void)load; -(NSString* _Nonnull)getId; --(BOOL)getBool:(CAPPluginCall* _Nonnull) call field:(NSString* _Nonnull)field defaultValue:(BOOL)defaultValue; --(NSString* _Nullable)getString:(CAPPluginCall* _Nonnull)call field:(NSString* _Nonnull)field defaultValue:(NSString* _Nonnull)defaultValue; +-(BOOL)getBool:(CAPPluginCall* _Nonnull) call field:(NSString* _Nonnull)field defaultValue:(BOOL)defaultValue DEPRECATED_MSG_ATTRIBUTE("Use accessors on CAPPluginCall instead. See CAPBridgedJSTypes.h for Obj-C implementations."); +-(NSString* _Nullable)getString:(CAPPluginCall* _Nonnull)call field:(NSString* _Nonnull)field defaultValue:(NSString* _Nonnull)defaultValue DEPRECATED_MSG_ATTRIBUTE("Use accessors on CAPPluginCall instead. See CAPBridgedJSTypes.h for Obj-C implementations."); -(id _Nullable)getConfigValue:(NSString* _Nonnull)key; -(void)setCenteredPopover:(UIViewController* _Nonnull)vc; -(BOOL)supportsPopover; diff --git a/ios/Capacitor/Capacitor/CAPPlugin.m b/ios/Capacitor/Capacitor/CAPPlugin.m index 63634d982d..517ad44288 100644 --- a/ios/Capacitor/Capacitor/CAPPlugin.m +++ b/ios/Capacitor/Capacitor/CAPPlugin.m @@ -1,4 +1,5 @@ #import "CAPPlugin.h" +#import "CAPBridgedJSTypes.h" #import #import @@ -19,34 +20,13 @@ -(NSString *) getId { return self.pluginName; } --(BOOL) getBool:(CAPPluginCall *)call field:(NSString *)field defaultValue:(BOOL)defaultValue { - id idVal = [call.options objectForKey:field]; - - if(![idVal isKindOfClass:[NSNumber class]]) { - return defaultValue; - } - - NSNumber *value = (NSNumber *)idVal; - if(value == nil) { - return defaultValue; - } - if(value.integerValue == 0) { - return FALSE; - } - return TRUE; +- (BOOL)getBool:(CAPPluginCall *)call field:(NSString *)field defaultValue:(BOOL)defaultValue { + NSNumber* value = [call getNumber:field defaultValue:[NSNumber numberWithBool:defaultValue]]; + return [value boolValue]; } --(NSString *) getString:(CAPPluginCall *)call field:(NSString *)field defaultValue:(NSString *)defaultValue -{ - id idVal = [call.options objectForKey:field]; - if(![idVal isKindOfClass:[NSString class]]) { - return defaultValue; - } - NSString *value = (NSString *)idVal; - if(value == nil) { - return defaultValue; - } - return value; +- (NSString *) getString:(CAPPluginCall *)call field:(NSString *)field defaultValue:(NSString *)defaultValue { + return [call getString:field defaultValue:defaultValue]; } -(id)getConfigValue:(NSString *)key { diff --git a/ios/Capacitor/CapacitorTests/PluginCallAccessorTests.m b/ios/Capacitor/CapacitorTests/PluginCallAccessorTests.m new file mode 100644 index 0000000000..0f8269c153 --- /dev/null +++ b/ios/Capacitor/CapacitorTests/PluginCallAccessorTests.m @@ -0,0 +1,106 @@ +#import +#import +#import +// forward declaration of internal capacitor classes that are exposed in the swift header via the @testable import. +@interface CAPWebViewAssetHandler: NSObject +@end +@interface CapacitorBridge: NSObject +@end +@interface CAPWebViewDelegationHandler: NSObject +@end +// import that will fail without the declarations +#import "CapacitorTests-Swift.h" + +// interface for this class +@interface PluginCallAccessorTests : XCTestCase +@property (strong, nonatomic) CAPPluginCall* call; +@end + +@implementation PluginCallAccessorTests + +- (void)setUp { + [super setUp]; + NSDate* date = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate:632854800]; + NSISO8601DateFormatter *formatter = [[NSISO8601DateFormatter alloc] init]; + NSDictionary* options = @{@"testString":@"foo", + @"testDict": @{@"testSubkey":@"sub value"}, + @"testFloat": @3.14159, + @"testDateObject": date, + @"testDateString": [formatter stringFromDate:date], + @"testBoolTrue": @TRUE, + @"testBoolFalse": @FALSE}; + [self setCall:[[CAPPluginCall alloc] initWithCallbackId:@"test" options:options success:NULL error:NULL]]; +} + +- (void)testStringAccessor { + NSString* value = [[self call] getString:@"testString" defaultValue:NULL]; + XCTAssertEqual(value, @"foo"); + + value = [[self call] getString:@"badString" defaultValue:NULL]; + XCTAssertNil(value); + + value = [[self call] getString:@"badString" defaultValue:@"default"]; + XCTAssertEqual(value, @"default"); +} + +- (void)testDateObjectAccessor { + NSDate* value = [[self call] getDate:@"testDateObject" defaultValue:NULL]; + XCTAssertEqual([value timeIntervalSinceReferenceDate], 632854800); + + value = [[self call] getDate:@"badString" defaultValue:NULL]; + XCTAssertNil(value); + + NSDate *defaultDate = [NSDate date]; + value = [[self call] getDate:@"badString" defaultValue:defaultDate]; + XCTAssertEqual(value, defaultDate); +} + +- (void)testDateStringAccessor { + NSDate* objectValue = [[self call] getDate:@"testDateObject" defaultValue:NULL]; + NSDate* stringValue = [[self call] getDate:@"testDateString" defaultValue:NULL]; + XCTAssertNotNil(objectValue); + XCTAssertNotNil(stringValue); + XCTAssertEqual(objectValue, stringValue); +} + +- (void)testObjectAccessor { + NSDictionary* value = [[self call] getObject:@"testDict" defaultValue:NULL]; + XCTAssertEqual([value objectForKey:@"testSubkey"], @"sub value"); + + value = [[self call] getObject:@"badString" defaultValue:NULL]; + XCTAssertNil(value); + + value = [[self call] getObject:@"badString" defaultValue:@{@"defaultKey":@"default"}]; + XCTAssertEqual([value objectForKey:@"defaultKey"], @"default"); +} + +- (void)testNumberAccessor { + NSNumber* value = [[self call] getNumber:@"testFloat" defaultValue:NULL]; + XCTAssertNotNil(value); + XCTAssertTrue([value isEqualToNumber:@3.14159]); + + value = [[self call] getNumber:@"badString" defaultValue:NULL]; + XCTAssertNil(value); + + value = [[self call] getNumber:@"badString" defaultValue:@100]; + XCTAssertEqual([value intValue], 100); + + value = [[self call] getNumber:@"testBoolTrue" defaultValue:NULL]; + XCTAssertNotNil(value); + XCTAssertEqual([value boolValue], TRUE); +} + +- (void)testBoolAccessor { + BOOL value = [[self call] getBool:@"testBoolTrue" defaultValue:false]; + XCTAssertTrue(value); + + value = [[self call] getBool:@"testBoolFalse" defaultValue:true]; + XCTAssertFalse(value); + + value = [[self call] getBool:@"badString" defaultValue:true]; + XCTAssertTrue(value); + + value = [[self call] getBool:@"badString" defaultValue:false]; + XCTAssertFalse(value); +} +@end