diff --git a/index.js b/index.js index 84c890a..b946acb 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ import { NativeModules, Platform } from 'react-native'; const invariant = require('invariant'); const RNCookieManagerIOS = NativeModules.RNCookieManagerIOS; const RNCookieManagerAndroid = NativeModules.RNCookieManagerAndroid; +const RNCookieManagerMacOS = NativeModules.RNCookieManagerMacOS; let CookieManager; @@ -24,10 +25,16 @@ if (Platform.OS === 'ios') { '@react-native-community/cookies: Import libraries to android "react-native link @react-native-community/cookies"', ); CookieManager = RNCookieManagerAndroid; +} else if (Platform.OS === 'macos') { + invariant( + RNCookieManagerMacOS, + '@react-native-community/cookies: Add RNCookieManagerMacOS.h and RNCookieManagerMacOS.m to your Xcode project', + ); + CookieManager = RNCookieManagerMacOS; } else { invariant( CookieManager, - '@react-native-community/cookies: Invalid platform. This library only supports Android and iOS.', + '@react-native-community/cookies: Invalid platform. This library only supports Android, iOS and MacOS.', ); } diff --git a/macos/RNCookieManagerMacOs.xcodeproj/project.pbxproj b/macos/RNCookieManagerMacOs.xcodeproj/project.pbxproj new file mode 100644 index 0000000..c964f24 --- /dev/null +++ b/macos/RNCookieManagerMacOs.xcodeproj/project.pbxproj @@ -0,0 +1,388 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + A2387BE556B46F28BB9C62FD /* Pods_RNCookieManagerMacOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A3E91C9DB889DAEAB299FB5 /* Pods_RNCookieManagerMacOS.framework */; }; + F886FBE8286B8108002AAD66 /* RNCookieManagerMacOS.m in Sources */ = {isa = PBXBuildFile; fileRef = F886FBE7286B8108002AAD66 /* RNCookieManagerMacOS.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 1BD725D81CF77A8B005DBD79 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1BD725DA1CF77A8B005DBD79 /* libRNCookieManagerMacOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNCookieManagerMacOS.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 6CDA467A38779CEFE54AF652 /* Pods-RNCookieManagerMacOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNCookieManagerMacOS.debug.xcconfig"; path = "Target Support Files/Pods-RNCookieManagerMacOS/Pods-RNCookieManagerMacOS.debug.xcconfig"; sourceTree = ""; }; + 7A3E91C9DB889DAEAB299FB5 /* Pods_RNCookieManagerMacOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RNCookieManagerMacOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + AC0CCEFD0D5DDBC08A3F9FDB /* Pods-RNCookieManagerMacOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNCookieManagerMacOS.release.xcconfig"; path = "Target Support Files/Pods-RNCookieManagerMacOS/Pods-RNCookieManagerMacOS.release.xcconfig"; sourceTree = ""; }; + F886FBE6286B8108002AAD66 /* RNCookieManagerMacOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCookieManagerMacOS.h; sourceTree = ""; }; + F886FBE7286B8108002AAD66 /* RNCookieManagerMacOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCookieManagerMacOS.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1BD725D71CF77A8B005DBD79 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A2387BE556B46F28BB9C62FD /* Pods_RNCookieManagerMacOS.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1ABCC227200901826854EA3A /* Frameworks */ = { + isa = PBXGroup; + children = ( + 7A3E91C9DB889DAEAB299FB5 /* Pods_RNCookieManagerMacOS.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 1BD725CD1CF7795C005DBD79 = { + isa = PBXGroup; + children = ( + F886FBE3286B7E50002AAD66 /* RNCookieManagerMacOS */, + 1BD725DB1CF77A8B005DBD79 /* Products */, + 71405C5FF90D2ACAB4F0366B /* Pods */, + 1ABCC227200901826854EA3A /* Frameworks */, + ); + sourceTree = ""; + }; + 1BD725DB1CF77A8B005DBD79 /* Products */ = { + isa = PBXGroup; + children = ( + 1BD725DA1CF77A8B005DBD79 /* libRNCookieManagerMacOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 71405C5FF90D2ACAB4F0366B /* Pods */ = { + isa = PBXGroup; + children = ( + 6CDA467A38779CEFE54AF652 /* Pods-RNCookieManagerMacOS.debug.xcconfig */, + AC0CCEFD0D5DDBC08A3F9FDB /* Pods-RNCookieManagerMacOS.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + F886FBE3286B7E50002AAD66 /* RNCookieManagerMacOS */ = { + isa = PBXGroup; + children = ( + F886FBE6286B8108002AAD66 /* RNCookieManagerMacOS.h */, + F886FBE7286B8108002AAD66 /* RNCookieManagerMacOS.m */, + ); + path = RNCookieManagerMacOS; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1BD725D91CF77A8B005DBD79 /* RNCookieManagerMacOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1BD725E31CF77A8B005DBD79 /* Build configuration list for PBXNativeTarget "RNCookieManagerMacOS" */; + buildPhases = ( + 749140BD317DEE62C1AE6F51 /* [CP] Check Pods Manifest.lock */, + 1BD725D61CF77A8B005DBD79 /* Sources */, + 1BD725D71CF77A8B005DBD79 /* Frameworks */, + 1BD725D81CF77A8B005DBD79 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = RNCookieManagerMacOS; + productName = RNCookieManagerMacOS; + productReference = 1BD725DA1CF77A8B005DBD79 /* libRNCookieManagerMacOS.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1BD725CE1CF7795C005DBD79 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1130; + TargetAttributes = { + 1BD725D91CF77A8B005DBD79 = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = 1BD725D11CF7795C005DBD79 /* Build configuration list for PBXProject "RNCookieManagerMacOS" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + English, + en, + ); + mainGroup = 1BD725CD1CF7795C005DBD79; + productRefGroup = 1BD725DB1CF77A8B005DBD79 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1BD725D91CF77A8B005DBD79 /* RNCookieManagerMacOS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + 749140BD317DEE62C1AE6F51 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RNCookieManagerMacOS-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1BD725D61CF77A8B005DBD79 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F886FBE8286B8108002AAD66 /* RNCookieManagerMacOS.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1BD725D21CF7795C005DBD79 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; + }; + name = Debug; + }; + 1BD725D31CF7795C005DBD79 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + }; + name = Release; + }; + 1BD725E11CF77A8B005DBD79 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6CDA467A38779CEFE54AF652 /* Pods-RNCookieManagerMacOS.debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SRCROOT)/../../React/**", + "$(inherited)", + "$(SRCROOT)/node_modules/react-native/React/**", + "$(SRCROOT)/../react-native/React/**", + "$(SRCROOT)/../../../node_modules/react-native/React/**", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/../../React/**", + "$(inherited)", + "$(SRCROOT)/node_modules/react-native/React/**", + "$(SRCROOT)/../react-native/React/**", + "$(SRCROOT)/../../../node_modules/react-native/React/**", + ); + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 1BD725E21CF77A8B005DBD79 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AC0CCEFD0D5DDBC08A3F9FDB /* Pods-RNCookieManagerMacOS.release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SRCROOT)/../../React/**", + "$(inherited)", + "$(SRCROOT)/node_modules/react-native/React/**", + "$(SRCROOT)/../react-native/React/**", + "$(SRCROOT)/../../../node_modules/react-native/React/**", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/../../React/**", + "$(inherited)", + "$(SRCROOT)/node_modules/react-native/React/**", + "$(SRCROOT)/../react-native/React/**", + "$(SRCROOT)/../../../node_modules/react-native/React/**", + ); + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1BD725D11CF7795C005DBD79 /* Build configuration list for PBXProject "RNCookieManagerMacOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1BD725D21CF7795C005DBD79 /* Debug */, + 1BD725D31CF7795C005DBD79 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1BD725E31CF77A8B005DBD79 /* Build configuration list for PBXNativeTarget "RNCookieManagerMacOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1BD725E11CF77A8B005DBD79 /* Debug */, + 1BD725E21CF77A8B005DBD79 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 1BD725CE1CF7795C005DBD79 /* Project object */; +} diff --git a/macos/RNCookieManagerMacOs/RNCookieManagerMacOs.h b/macos/RNCookieManagerMacOs/RNCookieManagerMacOs.h new file mode 100644 index 0000000..6cb4274 --- /dev/null +++ b/macos/RNCookieManagerMacOs/RNCookieManagerMacOs.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) Joseph P. Ferraro + * + * This source code is licensed under the MIT license found in the + * LICENSE file here: https://github.com/joeferraro/react-native-cookies/blob/master/LICENSE.md. + */ + +#import + +#import + +@interface RNCookieManagerMacOS : NSObject + +@property (nonatomic, strong) NSDateFormatter *formatter; + +@end diff --git a/macos/RNCookieManagerMacOs/RNCookieManagerMacOs.m b/macos/RNCookieManagerMacOs/RNCookieManagerMacOs.m new file mode 100644 index 0000000..436c902 --- /dev/null +++ b/macos/RNCookieManagerMacOs/RNCookieManagerMacOs.m @@ -0,0 +1,350 @@ +/** + * Copyright (c) Joseph P. Ferraro + * + * This source code is licensed under the MIT license found in the + * LICENSE file here: https://github.com/joeferraro/react-native-cookies/blob/master/LICENSE.md. + */ + +#import "RNCookieManagerMacOS.h" +#import + +static NSString * const NOT_AVAILABLE_ERROR_MESSAGE = @"WebKit/WebKit-Components are only available with macOS 10.13 and higher!"; +static NSString * const INVALID_URL_MISSING_HTTP = @"Invalid URL: It may be missing a protocol (ex. http:// or https://)."; +static NSString * const INVALID_DOMAINS = @"Cookie URL host %@ and domain %@ mismatched. The cookie won't set correctly."; + +static inline BOOL isEmpty(id value) +{ + return value == nil + || ([value respondsToSelector:@selector(length)] && [(NSData *)value length] == 0) + || ([value respondsToSelector:@selector(count)] && [(NSArray *)value count] == 0); +} + +@implementation RNCookieManagerMacOS + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.formatter = [NSDateFormatter new]; + [self.formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"]; + } + return self; +} + ++ (BOOL)requiresMainQueueSetup +{ + return NO; +} + +RCT_EXPORT_MODULE() + +RCT_EXPORT_METHOD( + set:(NSURL *)url + cookie:(NSDictionary *)props + useWebKit:(BOOL)useWebKit + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + NSHTTPCookie *cookie; + @try { + cookie = [self makeHTTPCookieObject:url props:props]; + } + @catch ( NSException *e ) { + reject(@"", [e reason], nil); + return; + } + + if (useWebKit) { + if (@available(macOS 10.13, *)) { + dispatch_async(dispatch_get_main_queue(), ^(){ + WKHTTPCookieStore *cookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore]; + [cookieStore setCookie:cookie completionHandler:^() { + resolve(@(YES)); + }]; + }); + } else { + reject(@"", NOT_AVAILABLE_ERROR_MESSAGE, nil); + } + } else { + [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie]; + resolve(@(YES)); + } +} + +RCT_EXPORT_METHOD( + setFromResponse:(NSURL *)url + cookie:(NSString *)cookie + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { + + NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:@{@"Set-Cookie": cookie} forURL:url]; + [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookies forURL:url mainDocumentURL:nil]; + resolve(@(YES)); +} + +RCT_EXPORT_METHOD( + getFromResponse:(NSURL *)url + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { + NSURLRequest *request = [NSURLRequest requestWithURL:url]; + [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] + completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { + + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:httpResponse.allHeaderFields forURL:response.URL]; + NSMutableDictionary *dics = [NSMutableDictionary dictionary]; + + for (int i = 0; i < cookies.count; i++) { + NSHTTPCookie *cookie = [cookies objectAtIndex:i]; + [dics setObject:cookie.value forKey:cookie.name]; + [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie]; + } + resolve(dics); + }]; +} + +RCT_EXPORT_METHOD( + get:(NSURL *)url + useWebKit:(BOOL)useWebKit + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + if (useWebKit) { + if (@available(macOS 10.13, *)) { + dispatch_async(dispatch_get_main_queue(), ^(){ + NSString *topLevelDomain = url.host; + + if (isEmpty(topLevelDomain)) { + reject(@"", INVALID_URL_MISSING_HTTP, nil); + return; + } + + WKHTTPCookieStore *cookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore]; + [cookieStore getAllCookies:^(NSArray *allCookies) { + NSMutableDictionary *cookies = [NSMutableDictionary dictionary]; + for (NSHTTPCookie *cookie in allCookies) { + if ([topLevelDomain containsString:cookie.domain] || + [cookie.domain isEqualToString: topLevelDomain]) { + [cookies setObject:[self createCookieData:cookie] forKey:cookie.name]; + } + } + resolve(cookies); + }]; + }); + } else { + reject(@"", NOT_AVAILABLE_ERROR_MESSAGE, nil); + } + } else { + NSMutableDictionary *cookies = [NSMutableDictionary dictionary]; + for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:url]) { + [cookies setObject:[self createCookieData:cookie] forKey:cookie.name]; + } + resolve(cookies); + } +} + +RCT_EXPORT_METHOD( + clearAll:(BOOL)useWebKit + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + if (useWebKit) { + if (@available(macOS 10.13, *)) { + dispatch_async(dispatch_get_main_queue(), ^(){ + // https://stackoverflow.com/questions/46465070/how-to-delete-cookies-from-wkhttpcookiestore#answer-47928399 + NSSet *websiteDataTypes = [NSSet setWithArray:@[WKWebsiteDataTypeCookies]]; + NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0]; + [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes + modifiedSince:dateFrom + completionHandler:^() { + resolve(@(YES)); + }]; + }); + } else { + reject(@"", NOT_AVAILABLE_ERROR_MESSAGE, nil); + } + } else { + NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + for (NSHTTPCookie *c in cookieStorage.cookies) { + [cookieStorage deleteCookie:c]; + } + [[NSUserDefaults standardUserDefaults] synchronize]; + resolve(@(YES)); + } +} + +RCT_EXPORT_METHOD( + clearByName:(NSURL *)url + name:(NSString *)name + useWebKit:(BOOL)useWebKit + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { + __block NSNumber * foundCookies = @NO; + NSMutableArray * foundCookiesList = [NSMutableArray new]; + + if (useWebKit) { + if (@available(macOS 10.13, *)) { + dispatch_async(dispatch_get_main_queue(), ^(){ + NSString *topLevelDomain = url.host; + + if (isEmpty(topLevelDomain)) { + reject(@"", INVALID_URL_MISSING_HTTP, nil); + return; + } + + WKHTTPCookieStore *cookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore]; + [cookieStore getAllCookies:^(NSArray *allCookies) { + for (NSHTTPCookie *cookie in allCookies) { + if ([name isEqualToString:cookie.name]) { + [foundCookiesList addObject:cookie]; + foundCookies = @YES; + } + } + for (NSHTTPCookie *fCookie in foundCookiesList) { + [cookieStore deleteCookie:fCookie completionHandler:nil]; + } + resolve(foundCookies); + }]; + }); + } else { + reject(@"", NOT_AVAILABLE_ERROR_MESSAGE, nil); + } + } else { + NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + for (NSHTTPCookie *c in cookieStorage.cookies) { + if ([[c name] isEqualToString:name]) { + [cookieStorage deleteCookie:c]; + foundCookies = @YES; + } + } + resolve(foundCookies); + } +} + +RCT_EXPORT_METHOD( + getAll:(BOOL)useWebKit + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + if (useWebKit) { + if (@available(macOS 10.13, *)) { + dispatch_async(dispatch_get_main_queue(), ^(){ + WKHTTPCookieStore *cookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore]; + [cookieStore getAllCookies:^(NSArray *allCookies) { + resolve([self createCookieList: allCookies]); + }]; + }); + } else { + reject(@"", NOT_AVAILABLE_ERROR_MESSAGE, nil); + } + } else { + NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + resolve([self createCookieList:cookieStorage.cookies]); + } +} + +-(NSDictionary *)createCookieList:(NSArray*)cookies +{ + NSMutableDictionary *cookieList = [NSMutableDictionary dictionary]; + for (NSHTTPCookie *cookie in cookies) { + [cookieList setObject:[self createCookieData:cookie] forKey:cookie.name]; + } + return cookieList; +} + +-(NSHTTPCookie *)makeHTTPCookieObject:(NSURL *)url + props:(NSDictionary *)props +{ + NSString *topLevelDomain = url.host; + + if (isEmpty(topLevelDomain)){ + NSException* myException = [NSException + exceptionWithName:@"Exception" + reason:INVALID_URL_MISSING_HTTP + userInfo:nil]; + @throw myException; + } + + NSString *name = [RCTConvert NSString:props[@"name"]]; + NSString *value = [RCTConvert NSString:props[@"value"]]; + NSString *path = [RCTConvert NSString:props[@"path"]]; + NSString *domain = [RCTConvert NSString:props[@"domain"]]; + NSString *version = [RCTConvert NSString:props[@"version"]]; + NSDate *expires = [RCTConvert NSDate:props[@"expires"]]; + NSNumber *secure = [RCTConvert NSNumber:props[@"secure"]]; + NSNumber *httpOnly = [RCTConvert NSNumber:props[@"httpOnly"]]; + + NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary]; + [cookieProperties setObject:name forKey:NSHTTPCookieName]; + [cookieProperties setObject:value forKey:NSHTTPCookieValue]; + + if (!isEmpty(path)) { + [cookieProperties setObject:path forKey:NSHTTPCookiePath]; + } else { + [cookieProperties setObject:@"/" forKey:NSHTTPCookiePath]; + } + if (!isEmpty(domain)) { + // Stripping the leading . to ensure the following check is accurate + NSString *strippedDomain = domain; + if ([strippedDomain hasPrefix:@"."]) { + strippedDomain = [strippedDomain substringFromIndex:1]; + } + + if (![topLevelDomain containsString:strippedDomain] && + ![topLevelDomain isEqualToString: strippedDomain]) { + NSException* myException = [NSException + exceptionWithName:@"Exception" + reason: [NSString stringWithFormat:INVALID_DOMAINS, topLevelDomain, domain] + userInfo:nil]; + @throw myException; + } + + [cookieProperties setObject:domain forKey:NSHTTPCookieDomain]; + } else { + [cookieProperties setObject:topLevelDomain forKey:NSHTTPCookieDomain]; + } + if (!isEmpty(version)) { + [cookieProperties setObject:version forKey:NSHTTPCookieVersion]; + } + if (!isEmpty(expires)) { + [cookieProperties setObject:expires forKey:NSHTTPCookieExpires]; + } + if ([secure boolValue]) { + [cookieProperties setObject:secure forKey:NSHTTPCookieSecure]; + } + if ([httpOnly boolValue]) { + [cookieProperties setObject:httpOnly forKey:@"HttpOnly"]; + } + + NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieProperties]; + + return cookie; +} + +-(NSDictionary *)createCookieData:(NSHTTPCookie *)cookie +{ + NSMutableDictionary *cookieData = [NSMutableDictionary dictionary]; + [cookieData setObject:cookie.name forKey:@"name"]; + [cookieData setObject:cookie.value forKey:@"value"]; + [cookieData setObject:cookie.path forKey:@"path"]; + [cookieData setObject:cookie.domain forKey:@"domain"]; + [cookieData setObject:[NSString stringWithFormat:@"%@", @(cookie.version)] forKey:@"version"]; + if (!isEmpty(cookie.expiresDate)) { + [cookieData setObject:[self.formatter stringFromDate:cookie.expiresDate] forKey:@"expires"]; + } + [cookieData setObject:[NSNumber numberWithBool:(BOOL)cookie.secure] forKey:@"secure"]; + [cookieData setObject:[NSNumber numberWithBool:(BOOL)cookie.HTTPOnly] forKey:@"httpOnly"]; + return cookieData; +} + +-(BOOL)isMatchingDomain:(NSString *)originDomain + cookieDomain:(NSString *)cookieDomain +{ + if ([originDomain isEqualToString: cookieDomain]) { + return @YES; + } + NSString *parentDomain = [cookieDomain hasPrefix:@"."] ? cookieDomain : [@"." stringByAppendingString: cookieDomain]; + return [originDomain hasSuffix:parentDomain]; +} + +@end diff --git a/package.json b/package.json index 2ee9b1f..1fccc24 100644 --- a/package.json +++ b/package.json @@ -13,11 +13,13 @@ "android", "ios", "cookies", - "webview" + "webview", + "macos" ], "files": [ "android", "ios", + "macos", "index.d.ts", "react-native-cookies.podspec" ], diff --git a/react-native-cookies.podspec b/react-native-cookies.podspec index ec1b754..776d2c4 100644 --- a/react-native-cookies.podspec +++ b/react-native-cookies.podspec @@ -11,8 +11,9 @@ Pod::Spec.new do |s| s.author = { package["author"]["name"] => package["author"]["email"] } s.source = { :git => "git@github.com:react-native-community/cookies.git", :tag => "v#{s.version}" } s.requires_arc = true - s.platform = :ios, "7.0" + s.platforms = { :ios => "7.0", :osx => "10.2" } s.preserve_paths = "*.framework" - s.source_files = "ios/**/*.{h,m}" + s.ios.source_files = "ios/**/*.{h,m}" + s.osx.source_files = "macos/**/*.{h,m,mm,swift}" s.dependency "React-Core" end