diff options
234 files changed, 3719 insertions, 1930 deletions
diff --git a/Carthage.md b/Carthage.md index cab512e..8f9c331 100644 --- a/Carthage.md +++ b/Carthage.md @@ -26,8 +26,9 @@ more details and additional installation methods. ## Carthage Usage -- Create a Cartfile with a **subset** of the following components. Note that - **FirebaseAnalytics** must always be included. +- Create a Cartfile with a **subset** of the following components - choosing the +Firebase components that you want to include in your app. Note that +**FirebaseAnalytics** must always be included. ``` binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseABTestingBinary.json" binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAdMobBinary.json" diff --git a/Example/Auth/Sample/MainViewController.m b/Example/Auth/Sample/MainViewController.m index e343508..d4915cd 100644 --- a/Example/Auth/Sample/MainViewController.m +++ b/Example/Auth/Sample/MainViewController.m @@ -429,10 +429,10 @@ static NSString *const kRemoveIDTokenListenerTitle = @"Remove Last ID Token Chan */ static NSString *const kSectionTitleApp = @"APP"; -/** @var kUpdateCurrentUserFromSavedTitle - @brief The text of the "Upgrade to saved user" button. +/** @var kSwitchToInMemoryUserTitle + @brief The text of the "Switch to in memory user" button. */ -static NSString *const kUpdateCurrentUserFromSavedTitle = @"Upgrade to saved user"; +static NSString *const kSwitchToInMemoryUserTitle = @"Switch to in memory user"; /** @var kCreateUserTitle @brief The text of the "Create User" button. @@ -750,7 +750,7 @@ typedef enum { }], ]], [StaticContentTableViewSection sectionWithTitle:kSectionTitleSignIn cells:@[ - [StaticContentTableViewCell cellWithTitle:kUpdateCurrentUserFromSavedTitle + [StaticContentTableViewCell cellWithTitle:kSwitchToInMemoryUserTitle value:nil action:^{ [weakSelf updateToSavedUser]; }], [StaticContentTableViewCell cellWithTitle:kCreateUserTitle @@ -2762,7 +2762,8 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) { } if (!_userInMemory) { - NSLog(@"You need an in memory user to perform this action"); + [self showMessagePrompt:[NSString stringWithFormat:@"You need an in-memory user to perform this" + "action, use the M+ button to save a user to memory.", nil]]; return; } diff --git a/Example/Auth/Tests/FIRAuthTests.m b/Example/Auth/Tests/FIRAuthTests.m index 507fdf2..47a430b 100644 --- a/Example/Auth/Tests/FIRAuthTests.m +++ b/Example/Auth/Tests/FIRAuthTests.m @@ -20,9 +20,7 @@ #import <FirebaseCore/FIRAppInternal.h> -#import <FirebaseAuth/FIREmailAuthProvider.h> -#import <FirebaseAuth/FIRGoogleAuthProvider.h> -#import <FirebaseAuth/FIRAdditionalUserInfo.h> +#import <FirebaseAuth/FirebaseAuth.h> #import "FIRAuth_Internal.h" #import "FIRAuthOperationType.h" diff --git a/Example/Auth/Tests/FIRUserTests.m b/Example/Auth/Tests/FIRUserTests.m index 68b0614..96a8082 100644 --- a/Example/Auth/Tests/FIRUserTests.m +++ b/Example/Auth/Tests/FIRUserTests.m @@ -18,11 +18,7 @@ #import <XCTest/XCTest.h> -#import <FirebaseAuth/FIRUser.h> -#import <FirebaseAuth/FIREmailAuthProvider.h> -#import <FirebaseAuth/FIRFacebookAuthProvider.h> -#import <FirebaseAuth/FIRGoogleAuthProvider.h> -#import <FirebaseAuth/FIRAdditionalUserInfo.h> +#import <FirebaseAuth/FirebaseAuth.h> #import "FIRAuth_Internal.h" #import "FIRAuthErrorUtils.h" diff --git a/Example/Core/Tests/FIRAppTest.m b/Example/Core/Tests/FIRAppTest.m index 3784a6e..6825e6a 100644 --- a/Example/Core/Tests/FIRAppTest.m +++ b/Example/Core/Tests/FIRAppTest.m @@ -119,21 +119,11 @@ NSString *const kFIRTestAppName2 = @"test-app-name-2"; } - (void)testConfigureWithCustomizedOptions { -// valid customized options -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnonnull" -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID - bundleID:kBundleID - GCMSenderID:kGCMSenderID - APIKey:kCustomizedAPIKey - clientID:nil - trackingID:nil - androidClientID:nil - databaseURL:nil - storageBucket:nil - deepLinkURLScheme:nil]; -#pragma clang diagnostic pop + // valid customized options + FIROptions *options = + [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID]; + options.bundleID = kBundleID; + options.APIKey = kCustomizedAPIKey; NSDictionary *expectedUserInfo = [self expectedUserInfoWithAppName:kFIRDefaultAppName isDefaultApp:YES]; OCMExpect([self.notificationCenterMock postNotificationName:kFIRAppReadyToConfigureSDKNotification @@ -194,21 +184,11 @@ NSString *const kFIRTestAppName2 = @"test-app-name-2"; XCTAssertTrue([FIRApp allApps].count == 1); self.app = [FIRApp appNamed:kFIRTestAppName1]; -// Configure a different app with valid customized options -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnonnull" -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - FIROptions *customizedOptions = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID - bundleID:kBundleID - GCMSenderID:kGCMSenderID - APIKey:kCustomizedAPIKey - clientID:nil - trackingID:nil - androidClientID:nil - databaseURL:nil - storageBucket:nil - deepLinkURLScheme:nil]; -#pragma clang diagnostic pop + // Configure a different app with valid customized options + FIROptions *customizedOptions = + [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID]; + customizedOptions.bundleID = kBundleID; + customizedOptions.APIKey = kCustomizedAPIKey; NSDictionary *expectedUserInfo2 = [self expectedUserInfoWithAppName:kFIRTestAppName2 isDefaultApp:NO]; diff --git a/Example/Core/Tests/FIRLoggerTest.m b/Example/Core/Tests/FIRLoggerTest.m index 5cc7465..31a495d 100644 --- a/Example/Core/Tests/FIRLoggerTest.m +++ b/Example/Core/Tests/FIRLoggerTest.m @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#ifdef DEBUG +// The tests depend upon library methods only built with #ifdef DEBUG + #import "FIRTestCase.h" #import <FirebaseCore/FIRLogger.h> @@ -267,3 +270,4 @@ static NSString *const kMessageCode = @"I-COR000001"; } @end +#endif diff --git a/Example/Core/Tests/FIROptionsTest.m b/Example/Core/Tests/FIROptionsTest.m index 5d66ca9..20aec94 100644 --- a/Example/Core/Tests/FIROptionsTest.m +++ b/Example/Core/Tests/FIROptionsTest.m @@ -81,51 +81,19 @@ extern NSString *const kFIRLibraryVersionID; } - (void)testInitCustomizedOptions { -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID - bundleID:kBundleID - GCMSenderID:kGCMSenderID - APIKey:kAPIKey - clientID:kClientID - trackingID:kTrackingID - androidClientID:(id _Nonnull)nil - databaseURL:kDatabaseURL - storageBucket:kStorageBucket - deepLinkURLScheme:kDeepLinkURLScheme]; -#pragma clang pop - [self assertOptionsMatchDefaults:options andProjectID:NO]; - XCTAssertEqualObjects(options.deepLinkURLScheme, kDeepLinkURLScheme); - XCTAssertFalse(options.usingOptionsFromDefaultPlist); - - FIROptions *options2 = + FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID]; - options2.APIKey = kAPIKey; - options2.bundleID = kBundleID; - options2.clientID = kClientID; - options2.databaseURL = kDatabaseURL; - options2.deepLinkURLScheme = kDeepLinkURLScheme; - options2.projectID = kProjectID; - options2.storageBucket = kStorageBucket; - options2.trackingID = kTrackingID; - [self assertOptionsMatchDefaults:options2 andProjectID:YES]; - XCTAssertEqualObjects(options2.deepLinkURLScheme, kDeepLinkURLScheme); + options.APIKey = kAPIKey; + options.bundleID = kBundleID; + options.clientID = kClientID; + options.databaseURL = kDatabaseURL; + options.deepLinkURLScheme = kDeepLinkURLScheme; + options.projectID = kProjectID; + options.storageBucket = kStorageBucket; + options.trackingID = kTrackingID; + [self assertOptionsMatchDefaults:options andProjectID:YES]; + XCTAssertEqualObjects(options.deepLinkURLScheme, kDeepLinkURLScheme); XCTAssertFalse(options.usingOptionsFromDefaultPlist); - -// nil GoogleAppID should throw an exception -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnonnull" -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - XCTAssertThrows([[FIROptions alloc] initWithGoogleAppID:nil - bundleID:kBundleID - GCMSenderID:kGCMSenderID - APIKey:kCustomizedAPIKey - clientID:nil - trackingID:nil - androidClientID:nil - databaseURL:nil - storageBucket:nil - deepLinkURLScheme:nil]); -#pragma clang diagnostic pop } - (void)testInitWithContentsOfFile { @@ -239,16 +207,9 @@ extern NSString *const kFIRLibraryVersionID; XCTAssertEqualObjects(newOptions.deepLinkURLScheme, kDeepLinkURLScheme); // customized options - FIROptions *customizedOptions = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID - bundleID:kBundleID - GCMSenderID:kGCMSenderID - APIKey:kAPIKey - clientID:kClientID - trackingID:kTrackingID - androidClientID:(id _Nonnull)nil - databaseURL:kDatabaseURL - storageBucket:kStorageBucket - deepLinkURLScheme:kDeepLinkURLScheme]; + FIROptions *customizedOptions = + [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID GCMSenderID:kGCMSenderID]; + customizedOptions.deepLinkURLScheme = kDeepLinkURLScheme; FIROptions *copyCustomizedOptions = [customizedOptions copy]; [copyCustomizedOptions setDeepLinkURLScheme:kNewDeepLinkURLScheme]; XCTAssertEqualObjects(customizedOptions.deepLinkURLScheme, kDeepLinkURLScheme); diff --git a/Example/Core/Tests/FIRTestCase.m b/Example/Core/Tests/FIRTestCase.m index b52886b..631075f 100644 --- a/Example/Core/Tests/FIRTestCase.m +++ b/Example/Core/Tests/FIRTestCase.m @@ -14,6 +14,7 @@ #import "FIRTestCase.h" +NSString *const kAndroidClientID = @"correct_android_client_id"; NSString *const kAPIKey = @"correct_api_key"; NSString *const kCustomizedAPIKey = @"customized_api_key"; NSString *const kClientID = @"correct_client_id"; diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj index 5b887a9..9fa954c 100644 --- a/Example/Firebase.xcodeproj/project.pbxproj +++ b/Example/Firebase.xcodeproj/project.pbxproj @@ -130,15 +130,6 @@ AFAF36F71EC28C25004BDEE5 /* Shared.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AFAF36F41EC28C25004BDEE5 /* Shared.xcassets */; }; AFAF36F81EC28C25004BDEE5 /* Shared.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AFAF36F41EC28C25004BDEE5 /* Shared.xcassets */; }; AFAF36F91EC28C25004BDEE5 /* Shared.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AFAF36F41EC28C25004BDEE5 /* Shared.xcassets */; }; - AFC8BA9D1EBD230E00B8EEAE /* NotificationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFC8BA9C1EBD230E00B8EEAE /* NotificationsController.swift */; }; - AFC8BA9F1EBD51A700B8EEAE /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFC8BA9E1EBD51A700B8EEAE /* Environment.swift */; }; - AFC8BAA71EC257D800B8EEAE /* FIRSampleAppUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = AFC8BAA31EC257D800B8EEAE /* FIRSampleAppUtilities.m */; }; - AFD5630E1EB1402300EA2233 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFD562FF1EB13DF200EA2233 /* AppDelegate.swift */; }; - AFD5630F1EB1402300EA2233 /* MessagingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFD563011EB13DF200EA2233 /* MessagingViewController.swift */; }; - AFD563151EB29EDE00EA2233 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = AFD563131EB1466100EA2233 /* GoogleService-Info.plist */; }; - AFD563171EBBEF7B00EA2233 /* Data+MessagingExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFD563161EBBEF7B00EA2233 /* Data+MessagingExtensions.swift */; }; - D018534D1EDACED4003A645C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D01853491EDACED4003A645C /* LaunchScreen.storyboard */; }; - D018534E1EDACED4003A645C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D018534B1EDACED4003A645C /* Main.storyboard */; }; D01853721EDAD084003A645C /* Shared.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AFAF36F41EC28C25004BDEE5 /* Shared.xcassets */; }; D01853831EDAD113003A645C /* FIRAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D018537E1EDAD0E6003A645C /* FIRAppDelegate.m */; }; D01853841EDAD113003A645C /* FIRViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D01853801EDAD0E6003A645C /* FIRViewController.m */; }; @@ -194,12 +185,7 @@ D064E6B51ED9B31C001956DF /* FIRTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = DEE14D7C1E844677006FA992 /* FIRTestCase.m */; }; D067EF831ED9BDE00095C27F /* Shared.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AFAF36F41EC28C25004BDEE5 /* Shared.xcassets */; }; D067EF841ED9BDFF0095C27F /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = DEE14D711E844677006FA992 /* GoogleService-Info.plist */; }; - D09005311EDB32D600154410 /* OCMock-iOS/OCMock.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D09005301EDB32D600154410 /* OCMock-iOS/OCMock.framework */; settings = {ATTRIBUTES = (); }; }; - D09005331EDB32F100154410 /* OCMock-iOS/OCMock.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D09005301EDB32D600154410 /* OCMock-iOS/OCMock.framework */; settings = {ATTRIBUTES = (); }; }; - D09005351EDB330E00154410 /* OCMock-iOS/OCMock.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D09005301EDB32D600154410 /* OCMock-iOS/OCMock.framework */; settings = {ATTRIBUTES = (); }; }; D09005371EDB331C00154410 /* OCMock-iOS/OCMock.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D09005301EDB32D600154410 /* OCMock-iOS/OCMock.framework */; settings = {ATTRIBUTES = (); }; }; - D09005391EDB333A00154410 /* OCMock-iOS/OCMock.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D09005301EDB32D600154410 /* OCMock-iOS/OCMock.framework */; settings = {ATTRIBUTES = (); }; }; - D090053B1EDB334400154410 /* OCMock-iOS/OCMock.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D09005301EDB32D600154410 /* OCMock-iOS/OCMock.framework */; settings = {ATTRIBUTES = (); }; }; D090053D1EDB334D00154410 /* OCMock-iOS/OCMock.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D09005301EDB32D600154410 /* OCMock-iOS/OCMock.framework */; settings = {ATTRIBUTES = (); }; }; D0EDB2C51EDA04F800B6C31B /* Shared.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AFAF36F41EC28C25004BDEE5 /* Shared.xcassets */; }; D0EDB2D71EDA057800B6C31B /* FIRAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D0EDB2D21EDA056A00B6C31B /* FIRAppDelegate.m */; }; @@ -337,6 +323,22 @@ DE26D2931F705F4D004AE1D3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE26D2071F70333E004AE1D3 /* ViewController.swift */; }; DE26D2941F705F51004AE1D3 /* AuthCredentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE26D1FE1F70333E004AE1D3 /* AuthCredentials.swift */; }; DE26D2951F705F53004AE1D3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE26D1FD1F70333E004AE1D3 /* AppDelegate.swift */; }; + DE47C0E2207AC87D00B1AEDF /* FIRSampleAppUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = AFC8BAA31EC257D800B8EEAE /* FIRSampleAppUtilities.m */; }; + DE47C0E7207AC87D00B1AEDF /* Shared.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AFAF36F41EC28C25004BDEE5 /* Shared.xcassets */; }; + DE47C114207AC94A00B1AEDF /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = DE47C107207AC94A00B1AEDF /* GoogleService-Info.plist */; }; + DE47C115207AC94A00B1AEDF /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE47C109207AC94A00B1AEDF /* Environment.swift */; }; + DE47C116207AC94A00B1AEDF /* MessagingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE47C10A207AC94A00B1AEDF /* MessagingViewController.swift */; }; + DE47C117207AC94A00B1AEDF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DE47C10C207AC94A00B1AEDF /* LaunchScreen.storyboard */; }; + DE47C118207AC94A00B1AEDF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DE47C10E207AC94A00B1AEDF /* Main.storyboard */; }; + DE47C119207AC94A00B1AEDF /* Messaging-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = DE47C110207AC94A00B1AEDF /* Messaging-Info.plist */; }; + DE47C11A207AC94A00B1AEDF /* Data+MessagingExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE47C111207AC94A00B1AEDF /* Data+MessagingExtensions.swift */; }; + DE47C11B207AC94A00B1AEDF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE47C112207AC94A00B1AEDF /* AppDelegate.swift */; }; + DE47C11C207AC94A00B1AEDF /* NotificationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE47C113207AC94A00B1AEDF /* NotificationsController.swift */; }; + DE47C13F207ACAA900B1AEDF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DE47C136207ACAA900B1AEDF /* LaunchScreen.storyboard */; }; + DE47C140207ACAA900B1AEDF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DE47C138207ACAA900B1AEDF /* Main.storyboard */; }; + DE47C142207ACAA900B1AEDF /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DE47C13B207ACAA900B1AEDF /* main.m */; }; + DE47C143207ACAA900B1AEDF /* FIRAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DE47C13C207ACAA900B1AEDF /* FIRAppDelegate.m */; }; + DE47C144207ACAA900B1AEDF /* FIRViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DE47C13D207ACAA900B1AEDF /* FIRViewController.m */; }; DE750DBD1EB3DD5B00A75E47 /* FIRAuthAPNSTokenTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE750DB61EB3DD4000A75E47 /* FIRAuthAPNSTokenTests.m */; }; DE750DBE1EB3DD6800A75E47 /* FIRAuthAPNSTokenManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE750DB51EB3DD4000A75E47 /* FIRAuthAPNSTokenManagerTests.m */; }; DE750DBF1EB3DD6C00A75E47 /* FIRAuthAppCredentialManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DE750DB71EB3DD4000A75E47 /* FIRAuthAppCredentialManagerTests.m */; }; @@ -456,6 +458,7 @@ DE9316031E8738E60083EDBF /* FIRMessagingSyncMessageManagerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315D41E8738B70083EDBF /* FIRMessagingSyncMessageManagerTest.m */; }; DE9316041E8738E60083EDBF /* FIRMessagingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315D51E8738B70083EDBF /* FIRMessagingTest.m */; }; DE9316051E8738E60083EDBF /* FIRMessagingTestNotificationUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9315D71E8738B70083EDBF /* FIRMessagingTestNotificationUtilities.m */; }; + DEA7795D207ACC8000245121 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = DE47C107207AC94A00B1AEDF /* GoogleService-Info.plist */; }; DEAAD3C31FBA1CD90053BF48 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DEAAD3BD1FBA1CD80053BF48 /* main.m */; }; DEAAD3CE1FBA1EFA0053BF48 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DEAAD3C51FBA1EF90053BF48 /* Assets.xcassets */; }; DEAAD3CF1FBA1EFA0053BF48 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DEAAD3C81FBA1EFA0053BF48 /* AppDelegate.m */; }; @@ -816,36 +819,6 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - D090052F1EDB32B700154410 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 16; - files = ( - D09005311EDB32D600154410 /* OCMock-iOS/OCMock.framework in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D09005321EDB32EA00154410 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 16; - files = ( - D09005331EDB32F100154410 /* OCMock-iOS/OCMock.framework in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D09005341EDB330800154410 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 16; - files = ( - D09005351EDB330E00154410 /* OCMock-iOS/OCMock.framework in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; D09005361EDB331700154410 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -856,26 +829,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - D09005381EDB333700154410 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 16; - files = ( - D09005391EDB333A00154410 /* OCMock-iOS/OCMock.framework in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D090053A1EDB334000154410 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 16; - files = ( - D090053B1EDB334400154410 /* OCMock-iOS/OCMock.framework in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; D090053C1EDB334800154410 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -960,20 +913,10 @@ 923F824D206C4D8B00034974 /* SafariServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SafariServices.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/System/Library/Frameworks/SafariServices.framework; sourceTree = DEVELOPER_DIR; }; 923F8250206C4DC500034974 /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; }; AFAF36F41EC28C25004BDEE5 /* Shared.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Shared.xcassets; path = Shared/Shared.xcassets; sourceTree = "<group>"; }; - AFC8BA9C1EBD230E00B8EEAE /* NotificationsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsController.swift; sourceTree = "<group>"; }; - AFC8BA9E1EBD51A700B8EEAE /* Environment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Environment.swift; sourceTree = "<group>"; }; AFC8BAA11EC257D700B8EEAE /* Messaging_Example-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Messaging_Example-Bridging-Header.h"; sourceTree = "<group>"; }; AFC8BAA21EC257D800B8EEAE /* FIRSampleAppUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FIRSampleAppUtilities.h; path = Shared/FIRSampleAppUtilities.h; sourceTree = "<group>"; }; AFC8BAA31EC257D800B8EEAE /* FIRSampleAppUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FIRSampleAppUtilities.m; path = Shared/FIRSampleAppUtilities.m; sourceTree = "<group>"; }; AFD562E51EB13C6D00EA2233 /* Messaging_Example_iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Messaging_Example_iOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; - AFD562FF1EB13DF200EA2233 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; - AFD563001EB13DF200EA2233 /* Messaging-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Messaging-Info.plist"; sourceTree = "<group>"; }; - AFD563011EB13DF200EA2233 /* MessagingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagingViewController.swift; sourceTree = "<group>"; }; - AFD563131EB1466100EA2233 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "App/GoogleService-Info.plist"; sourceTree = "<group>"; }; - AFD563141EB29B8C00EA2233 /* Messaging_Example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Messaging_Example.entitlements; sourceTree = "<group>"; }; - AFD563161EBBEF7B00EA2233 /* Data+MessagingExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+MessagingExtensions.swift"; sourceTree = "<group>"; }; - D018534A1EDACED4003A645C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; - D018534C1EDACED4003A645C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; D01853791EDAD084003A645C /* Auth_Example_macOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Auth_Example_macOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; D018537C1EDAD0E6003A645C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; D018537D1EDAD0E6003A645C /* FIRAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FIRAppDelegate.h; sourceTree = "<group>"; }; @@ -1094,6 +1037,24 @@ DE26D26D1F705C35004AE1D3 /* Auth_EarlGreyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Auth_EarlGreyTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DE26D27D1F705EC7004AE1D3 /* SwiftSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; DE45C6641E7DA8CB009E6ACD /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + DE47C0ED207AC87D00B1AEDF /* Messaging_Sample_iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Messaging_Sample_iOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; + DE47C107207AC94A00B1AEDF /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; }; + DE47C109207AC94A00B1AEDF /* Environment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Environment.swift; sourceTree = "<group>"; }; + DE47C10A207AC94A00B1AEDF /* MessagingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagingViewController.swift; sourceTree = "<group>"; }; + DE47C10B207AC94A00B1AEDF /* Messaging_Example.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Messaging_Example.entitlements; sourceTree = "<group>"; }; + DE47C10D207AC94A00B1AEDF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; + DE47C10F207AC94A00B1AEDF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; + DE47C110207AC94A00B1AEDF /* Messaging-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Messaging-Info.plist"; sourceTree = "<group>"; }; + DE47C111207AC94A00B1AEDF /* Data+MessagingExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+MessagingExtensions.swift"; sourceTree = "<group>"; }; + DE47C112207AC94A00B1AEDF /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; + DE47C113207AC94A00B1AEDF /* NotificationsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsController.swift; sourceTree = "<group>"; }; + DE47C134207ACAA900B1AEDF /* FIRAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FIRAppDelegate.h; sourceTree = "<group>"; }; + DE47C135207ACAA900B1AEDF /* FIRViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FIRViewController.h; sourceTree = "<group>"; }; + DE47C137207ACAA900B1AEDF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; + DE47C139207ACAA900B1AEDF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; + DE47C13B207ACAA900B1AEDF /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; + DE47C13C207ACAA900B1AEDF /* FIRAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRAppDelegate.m; sourceTree = "<group>"; }; + DE47C13D207ACAA900B1AEDF /* FIRViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRViewController.m; sourceTree = "<group>"; }; DE53893E1FBB62E100199FC2 /* Auth_Tests_tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Auth_Tests_tvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DE53894C1FBB635400199FC2 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; }; DE53894D1FBB635400199FC2 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; }; @@ -1433,6 +1394,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + DE47C0E4207AC87D00B1AEDF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; DE53893B1FBB62E100199FC2 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1666,6 +1634,7 @@ DE53893E1FBB62E100199FC2 /* Auth_Tests_tvOS.xctest */, DE1FAE901FBCF5E100897AAA /* Auth_Example_tvOS.app */, DEDFEFEC1FD1B8C100F7D466 /* Analytics_Tests_iOS.xctest */, + DE47C0ED207AC87D00B1AEDF /* Messaging_Sample_iOS.app */, ); name = Products; sourceTree = "<group>"; @@ -1705,15 +1674,6 @@ name = Shared; sourceTree = "<group>"; }; - AFD562F71EB13CC700EA2233 /* App */ = { - isa = PBXGroup; - children = ( - AFD563131EB1466100EA2233 /* GoogleService-Info.plist */, - D01853481EDACE1A003A645C /* iOS */, - ); - name = App; - sourceTree = "<group>"; - }; D01853461EDACC10003A645C /* iOS */ = { isa = PBXGroup; children = ( @@ -1743,23 +1703,6 @@ path = macOS; sourceTree = "<group>"; }; - D01853481EDACE1A003A645C /* iOS */ = { - isa = PBXGroup; - children = ( - AFD563001EB13DF200EA2233 /* Messaging-Info.plist */, - AFD563141EB29B8C00EA2233 /* Messaging_Example.entitlements */, - AFD562FF1EB13DF200EA2233 /* AppDelegate.swift */, - AFD563161EBBEF7B00EA2233 /* Data+MessagingExtensions.swift */, - AFC8BA9E1EBD51A700B8EEAE /* Environment.swift */, - AFD563011EB13DF200EA2233 /* MessagingViewController.swift */, - AFC8BA9C1EBD230E00B8EEAE /* NotificationsController.swift */, - D01853491EDACED4003A645C /* LaunchScreen.storyboard */, - D018534B1EDACED4003A645C /* Main.storyboard */, - ); - name = iOS; - path = App/iOS; - sourceTree = "<group>"; - }; D0D857F61ED9ADA8002342D2 /* iOS */ = { isa = PBXGroup; children = ( @@ -1946,6 +1889,53 @@ path = SwiftSample; sourceTree = "<group>"; }; + DE47C106207AC94A00B1AEDF /* Sample */ = { + isa = PBXGroup; + children = ( + DE47C107207AC94A00B1AEDF /* GoogleService-Info.plist */, + DE47C108207AC94A00B1AEDF /* iOS */, + ); + path = Sample; + sourceTree = "<group>"; + }; + DE47C108207AC94A00B1AEDF /* iOS */ = { + isa = PBXGroup; + children = ( + DE47C109207AC94A00B1AEDF /* Environment.swift */, + DE47C10A207AC94A00B1AEDF /* MessagingViewController.swift */, + DE47C10B207AC94A00B1AEDF /* Messaging_Example.entitlements */, + DE47C10C207AC94A00B1AEDF /* LaunchScreen.storyboard */, + DE47C10E207AC94A00B1AEDF /* Main.storyboard */, + DE47C110207AC94A00B1AEDF /* Messaging-Info.plist */, + DE47C111207AC94A00B1AEDF /* Data+MessagingExtensions.swift */, + DE47C112207AC94A00B1AEDF /* AppDelegate.swift */, + DE47C113207AC94A00B1AEDF /* NotificationsController.swift */, + ); + path = iOS; + sourceTree = "<group>"; + }; + DE47C131207ACAA900B1AEDF /* App */ = { + isa = PBXGroup; + children = ( + DE47C133207ACAA900B1AEDF /* iOS */, + ); + path = App; + sourceTree = "<group>"; + }; + DE47C133207ACAA900B1AEDF /* iOS */ = { + isa = PBXGroup; + children = ( + DE47C134207ACAA900B1AEDF /* FIRAppDelegate.h */, + DE47C135207ACAA900B1AEDF /* FIRViewController.h */, + DE47C136207ACAA900B1AEDF /* LaunchScreen.storyboard */, + DE47C138207ACAA900B1AEDF /* Main.storyboard */, + DE47C13B207ACAA900B1AEDF /* main.m */, + DE47C13C207ACAA900B1AEDF /* FIRAppDelegate.m */, + DE47C13D207ACAA900B1AEDF /* FIRViewController.m */, + ); + path = iOS; + sourceTree = "<group>"; + }; DE53894B1FBB635400199FC2 /* tvOS */ = { isa = PBXGroup; children = ( @@ -2152,8 +2142,9 @@ DE9315B41E8738B70083EDBF /* Messaging */ = { isa = PBXGroup; children = ( + DE47C131207ACAA900B1AEDF /* App */, + DE47C106207AC94A00B1AEDF /* Sample */, AFC8BAA11EC257D700B8EEAE /* Messaging_Example-Bridging-Header.h */, - AFD562F71EB13CC700EA2233 /* App */, DE9315C21E8738B70083EDBF /* Tests */, ); path = Messaging; @@ -2651,6 +2642,23 @@ productReference = DE26D27D1F705EC7004AE1D3 /* SwiftSample.app */; productType = "com.apple.product-type.application"; }; + DE47C0DC207AC87D00B1AEDF /* Messaging_Sample_iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = DE47C0EA207AC87D00B1AEDF /* Build configuration list for PBXNativeTarget "Messaging_Sample_iOS" */; + buildPhases = ( + DE47C0DD207AC87D00B1AEDF /* Sources */, + DE47C0E4207AC87D00B1AEDF /* Frameworks */, + DE47C0E5207AC87D00B1AEDF /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Messaging_Sample_iOS; + productName = Messaging_Example_iOS; + productReference = DE47C0ED207AC87D00B1AEDF /* Messaging_Sample_iOS.app */; + productType = "com.apple.product-type.application"; + }; DE53893D1FBB62E100199FC2 /* Auth_Tests_tvOS */ = { isa = PBXNativeTarget; buildConfigurationList = DE5389491FBB62E100199FC2 /* Build configuration list for PBXNativeTarget "Auth_Tests_tvOS" */; @@ -2693,7 +2701,6 @@ DE7B8D191E8EF078009EB6DF /* Sources */, DE7B8D1A1E8EF078009EB6DF /* Frameworks */, DE7B8D1B1E8EF078009EB6DF /* Resources */, - D09005341EDB330800154410 /* CopyFiles */, ); buildRules = ( ); @@ -2729,7 +2736,6 @@ DE9314DA1E86C6BE0083EDBF /* Sources */, DE9314DB1E86C6BE0083EDBF /* Frameworks */, DE9314DC1E86C6BE0083EDBF /* Resources */, - D090052F1EDB32B700154410 /* CopyFiles */, ); buildRules = ( ); @@ -2748,7 +2754,6 @@ DE9315A31E8738460083EDBF /* Sources */, DE9315A41E8738460083EDBF /* Frameworks */, DE9315A51E8738460083EDBF /* Resources */, - D09005381EDB333700154410 /* CopyFiles */, ); buildRules = ( ); @@ -2854,7 +2859,6 @@ DEB13A0E1E73507E00AC236D /* Sources */, DEB13A161E73507E00AC236D /* Frameworks */, DEB13A1D1E73507E00AC236D /* Resources */, - D090053A1EDB334000154410 /* CopyFiles */, ); buildRules = ( ); @@ -2908,7 +2912,6 @@ DEE14D551E84464D006FA992 /* Sources */, DEE14D561E84464D006FA992 /* Frameworks */, DEE14D571E84464D006FA992 /* Resources */, - D09005321EDB32EA00154410 /* CopyFiles */, ); buildRules = ( ); @@ -3047,6 +3050,9 @@ DevelopmentTeam = EQHXZ8M8AV; ProvisioningStyle = Automatic; }; + DE47C0DC207AC87D00B1AEDF = { + DevelopmentTeam = EQHXZ8M8AV; + }; DE53893D1FBB62E100199FC2 = { CreatedOnToolsVersion = 9.1; DevelopmentTeam = EQHXZ8M8AV; @@ -3166,6 +3172,7 @@ DE1EC27E1FBA5E63007D18D8 /* Database_Tests_tvOS */, AFD562E41EB13C6D00EA2233 /* Messaging_Example_iOS */, DE9315A61E8738460083EDBF /* Messaging_Tests_iOS */, + DE47C0DC207AC87D00B1AEDF /* Messaging_Sample_iOS */, DEB139E01E73506A00AC236D /* Storage_Example_iOS */, DEB13A0A1E73507E00AC236D /* Storage_Tests_iOS */, 06121EBB1EC399C50008D70E /* Storage_IntegrationTests_iOS */, @@ -3201,10 +3208,10 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - D018534D1EDACED4003A645C /* LaunchScreen.storyboard in Resources */, + DE47C13F207ACAA900B1AEDF /* LaunchScreen.storyboard in Resources */, + DEA7795D207ACC8000245121 /* GoogleService-Info.plist in Resources */, AFAF36F81EC28C25004BDEE5 /* Shared.xcassets in Resources */, - AFD563151EB29EDE00EA2233 /* GoogleService-Info.plist in Resources */, - D018534E1EDACED4003A645C /* Main.storyboard in Resources */, + DE47C140207ACAA900B1AEDF /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3363,6 +3370,18 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + DE47C0E5207AC87D00B1AEDF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DE47C118207AC94A00B1AEDF /* Main.storyboard in Resources */, + DE47C0E7207AC87D00B1AEDF /* Shared.xcassets in Resources */, + DE47C114207AC94A00B1AEDF /* GoogleService-Info.plist in Resources */, + DE47C117207AC94A00B1AEDF /* LaunchScreen.storyboard in Resources */, + DE47C119207AC94A00B1AEDF /* Messaging-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; DE53893C1FBB62E100199FC2 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -3545,12 +3564,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - AFD563171EBBEF7B00EA2233 /* Data+MessagingExtensions.swift in Sources */, - AFD5630E1EB1402300EA2233 /* AppDelegate.swift in Sources */, - AFC8BA9D1EBD230E00B8EEAE /* NotificationsController.swift in Sources */, - AFD5630F1EB1402300EA2233 /* MessagingViewController.swift in Sources */, - AFC8BAA71EC257D800B8EEAE /* FIRSampleAppUtilities.m in Sources */, - AFC8BA9F1EBD51A700B8EEAE /* Environment.swift in Sources */, + DE47C142207ACAA900B1AEDF /* main.m in Sources */, + DE47C143207ACAA900B1AEDF /* FIRAppDelegate.m in Sources */, + DE47C144207ACAA900B1AEDF /* FIRViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3856,6 +3872,19 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + DE47C0DD207AC87D00B1AEDF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DE47C11A207AC94A00B1AEDF /* Data+MessagingExtensions.swift in Sources */, + DE47C115207AC94A00B1AEDF /* Environment.swift in Sources */, + DE47C116207AC94A00B1AEDF /* MessagingViewController.swift in Sources */, + DE47C0E2207AC87D00B1AEDF /* FIRSampleAppUtilities.m in Sources */, + DE47C11C207AC94A00B1AEDF /* NotificationsController.swift in Sources */, + DE47C11B207AC94A00B1AEDF /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; DE53893A1FBB62E100199FC2 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -4342,22 +4371,6 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ - D01853491EDACED4003A645C /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - D018534A1EDACED4003A645C /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = "<group>"; - }; - D018534B1EDACED4003A645C /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - D018534C1EDACED4003A645C /* Base */, - ); - name = Main.storyboard; - sourceTree = "<group>"; - }; D018537B1EDAD0E6003A645C /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( @@ -4402,6 +4415,38 @@ name = Localizable.strings; sourceTree = "<group>"; }; + DE47C10C207AC94A00B1AEDF /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + DE47C10D207AC94A00B1AEDF /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = "<group>"; + }; + DE47C10E207AC94A00B1AEDF /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + DE47C10F207AC94A00B1AEDF /* Base */, + ); + name = Main.storyboard; + sourceTree = "<group>"; + }; + DE47C136207ACAA900B1AEDF /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + DE47C137207ACAA900B1AEDF /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = "<group>"; + }; + DE47C138207ACAA900B1AEDF /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + DE47C139207ACAA900B1AEDF /* Base */, + ); + name = Main.storyboard; + sourceTree = "<group>"; + }; DE7B8D2C1E8EF202009EB6DF /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( @@ -4683,11 +4728,11 @@ CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CODE_SIGN_ENTITLEMENTS = Messaging/App/iOS/Messaging_Example.entitlements; + CODE_SIGN_ENTITLEMENTS = Messaging/Sample/iOS/Messaging_Example.entitlements; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = EQHXZ8M8AV; - INFOPLIST_FILE = "Messaging/App/iOS/Messaging-Info.plist"; + INFOPLIST_FILE = "Messaging/Sample/iOS/Messaging-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = com.google.FirebaseMessagingSample.dev; @@ -4707,12 +4752,12 @@ CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CODE_SIGN_ENTITLEMENTS = Messaging/App/iOS/Messaging_Example.entitlements; + CODE_SIGN_ENTITLEMENTS = Messaging/Sample/iOS/Messaging_Example.entitlements; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = EQHXZ8M8AV; - INFOPLIST_FILE = "Messaging/App/iOS/Messaging-Info.plist"; + INFOPLIST_FILE = "Messaging/Sample/iOS/Messaging-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = com.google.FirebaseMessagingSample.dev; @@ -5762,6 +5807,54 @@ }; name = Release; }; + DE47C0EB207AC87D00B1AEDF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_ENTITLEMENTS = Messaging/Sample/iOS/Messaging_Example.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = EQHXZ8M8AV; + INFOPLIST_FILE = "$(SRCROOT)/Messaging/Sample/iOS/Messaging-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.google.FirebaseMessagingSample.dev; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OBJC_BRIDGING_HEADER = "Messaging/Messaging_Example-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + DE47C0EC207AC87D00B1AEDF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_ENTITLEMENTS = Messaging/Sample/iOS/Messaging_Example.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = EQHXZ8M8AV; + INFOPLIST_FILE = "$(SRCROOT)/Messaging/Sample/iOS/Messaging-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = com.google.FirebaseMessagingSample.dev; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "Messaging/Messaging_Example-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; DE5389451FBB62E100199FC2 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -5915,6 +6008,7 @@ "\"${PODS_ROOT}/../../Firebase/Database/Persistence\"", "\"${PODS_ROOT}/../../Firebase/Database/third_party/FImmutableSortedDictionary/FImmutableSortedDictionary\"", "\"${PODS_ROOT}/../../Firebase/Database/Core/View\"", + "\"${PODS_ROOT}/Headers/Private\"", ); INFOPLIST_FILE = "Database/Tests/FirebaseTests-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 10.2; @@ -5955,6 +6049,7 @@ "\"${PODS_ROOT}/../../Firebase/Database/Persistence\"", "\"${PODS_ROOT}/../../Firebase/Database/third_party/FImmutableSortedDictionary/FImmutableSortedDictionary\"", "\"${PODS_ROOT}/../../Firebase/Database/Core/View\"", + "\"${PODS_ROOT}/Headers/Private\"", ); INFOPLIST_FILE = "Database/Tests/FirebaseTests-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 10.2; @@ -6022,6 +6117,7 @@ "\"${PODS_ROOT}/../../Firebase/Auth/Source/RPCs\"", "\"${PODS_ROOT}/../../Firebase/Auth/Source\"", "\"${PODS_ROOT}/../../Firebase/Auth/Source/AuthProviders\"", + "\"${PODS_ROOT}/Headers/Private\"", ); INFOPLIST_FILE = "Auth/Tests/Tests-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 10.2; @@ -6047,6 +6143,7 @@ "\"${PODS_ROOT}/../../Firebase/Auth/Source/RPCs\"", "\"${PODS_ROOT}/../../Firebase/Auth/Source\"", "\"${PODS_ROOT}/../../Firebase/Auth/Source/AuthProviders\"", + "\"${PODS_ROOT}/Headers/Private\"", ); INFOPLIST_FILE = "Auth/Tests/Tests-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 10.2; @@ -6061,7 +6158,6 @@ DE9315B01E8738460083EDBF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; @@ -6077,6 +6173,7 @@ "\"${PODS_ROOT}/Headers/Public\"", "\"${PODS_ROOT}/Headers/Public/FirebaseAnalytics\"", "\"${PODS_ROOT}/../../Firebase/Messaging\"", + "\"${PODS_ROOT}/Headers/Private\"", ); INFOPLIST_FILE = Messaging/Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -6090,7 +6187,6 @@ DE9315B11E8738460083EDBF /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; @@ -6107,6 +6203,7 @@ "\"${PODS_ROOT}/Headers/Public\"", "\"${PODS_ROOT}/Headers/Public/FirebaseAnalytics\"", "\"${PODS_ROOT}/../../Firebase/Messaging\"", + "\"${PODS_ROOT}/Headers/Private\"", ); INFOPLIST_FILE = Messaging/Tests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -6433,6 +6530,7 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "\"${PODS_ROOT}/../../Firebase/Storage/Private\"", + "\"${PODS_ROOT}/Headers/Private\"", ); INFOPLIST_FILE = "Storage/Tests/Tests-Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "com.google.Storage-Tests-iOS"; @@ -6452,6 +6550,7 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", "\"${PODS_ROOT}/../../Firebase/Storage/Private\"", + "\"${PODS_ROOT}/Headers/Private\"", ); INFOPLIST_FILE = "Storage/Tests/Tests-Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "com.google.Storage-Tests-iOS"; @@ -6565,7 +6664,10 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = ""; - HEADER_SEARCH_PATHS = "$(inherited)"; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "${PODS_ROOT}/Headers/Private", + ); INFOPLIST_FILE = "Core/Tests/Tests-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -6585,7 +6687,10 @@ COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; - HEADER_SEARCH_PATHS = "$(inherited)"; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "${PODS_ROOT}/Headers/Private", + ); INFOPLIST_FILE = "Core/Tests/Tests-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -6825,6 +6930,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + DE47C0EA207AC87D00B1AEDF /* Build configuration list for PBXNativeTarget "Messaging_Sample_iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DE47C0EB207AC87D00B1AEDF /* Debug */, + DE47C0EC207AC87D00B1AEDF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; DE5389491FBB62E100199FC2 /* Build configuration list for PBXNativeTarget "Auth_Tests_tvOS" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Example/Firebase.xcodeproj/xcshareddata/xcschemes/Messaging_Sample_iOS.xcscheme b/Example/Firebase.xcodeproj/xcshareddata/xcschemes/Messaging_Sample_iOS.xcscheme new file mode 100644 index 0000000..57e31f3 --- /dev/null +++ b/Example/Firebase.xcodeproj/xcshareddata/xcschemes/Messaging_Sample_iOS.xcscheme @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0930" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "DE47C0DC207AC87D00B1AEDF" + BuildableName = "Messaging_Sample_iOS.app" + BlueprintName = "Messaging_Sample_iOS" + ReferencedContainer = "container:Firebase.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + </Testables> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "DE47C0DC207AC87D00B1AEDF" + BuildableName = "Messaging_Sample_iOS.app" + BlueprintName = "Messaging_Sample_iOS" + ReferencedContainer = "container:Firebase.xcodeproj"> + </BuildableReference> + </MacroExpansion> + <AdditionalOptions> + </AdditionalOptions> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "DE47C0DC207AC87D00B1AEDF" + BuildableName = "Messaging_Sample_iOS.app" + BlueprintName = "Messaging_Sample_iOS" + ReferencedContainer = "container:Firebase.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "DE47C0DC207AC87D00B1AEDF" + BuildableName = "Messaging_Sample_iOS.app" + BlueprintName = "Messaging_Sample_iOS" + ReferencedContainer = "container:Firebase.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/Example/Messaging/App/iOS/Base.lproj/LaunchScreen.storyboard b/Example/Messaging/App/iOS/Base.lproj/LaunchScreen.storyboard index fdf3f97..66a7681 100644 --- a/Example/Messaging/App/iOS/Base.lproj/LaunchScreen.storyboard +++ b/Example/Messaging/App/iOS/Base.lproj/LaunchScreen.storyboard @@ -1,8 +1,8 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM"> <dependencies> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/> - <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> + <deployment identifier="iOS"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/> </dependencies> <scenes> <!--View Controller--> @@ -14,9 +14,9 @@ <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/> </layoutGuides> <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> - <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> + <rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> </view> </viewController> <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> diff --git a/Example/Messaging/App/iOS/Base.lproj/Main.storyboard b/Example/Messaging/App/iOS/Base.lproj/Main.storyboard index 6df1a82..d164a23 100644 --- a/Example/Messaging/App/iOS/Base.lproj/Main.storyboard +++ b/Example/Messaging/App/iOS/Base.lproj/Main.storyboard @@ -1,48 +1,27 @@ -<?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12120" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="taE-sK-BOl"> - <device id="retina4_7" orientation="portrait"> - <adaptation id="fullscreen"/> - </device> +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7706" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="whP-gf-Uak"> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/> - <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/> </dependencies> <scenes> - <!--Firebase Cloud Messaging--> - <scene sceneID="tne-QT-ifu"> + <!--View Controller--> + <scene sceneID="wQg-tq-qST"> <objects> - <viewController id="BYZ-38-t0r" customClass="MessagingViewController" customModule="Messaging_Example" customModuleProvider="target" sceneMemberID="viewController"> + <viewController id="whP-gf-Uak" customClass="FIRViewController" sceneMemberID="viewController"> <layoutGuides> - <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/> - <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/> + <viewControllerLayoutGuide type="top" id="uEw-UM-LJ8"/> + <viewControllerLayoutGuide type="bottom" id="Mvr-aV-6Um"/> </layoutGuides> - <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> - <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> + <view key="view" contentMode="scaleToFill" id="TpU-gO-2f1"> + <rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> </view> - <navigationItem key="navigationItem" title="Firebase Cloud Messaging" id="z1u-kE-qKb"/> </viewController> - <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> + <placeholder placeholderIdentifier="IBFirstResponder" id="tc2-Qw-aMS" userLabel="First Responder" sceneMemberID="firstResponder"/> </objects> - <point key="canvasLocation" x="698" y="164"/> - </scene> - <!--Navigation Controller--> - <scene sceneID="rmF-xz-rwn"> - <objects> - <placeholder placeholderIdentifier="IBFirstResponder" id="Ju1-Bj-8eG" userLabel="First Responder" sceneMemberID="firstResponder"/> - <navigationController id="taE-sK-BOl" sceneMemberID="viewController"> - <navigationBar key="navigationBar" contentMode="scaleToFill" id="iTL-Kg-11w"> - <rect key="frame" x="0.0" y="0.0" width="375" height="44"/> - <autoresizingMask key="autoresizingMask"/> - </navigationBar> - <connections> - <segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="04R-HZ-bi6"/> - </connections> - </navigationController> - </objects> - <point key="canvasLocation" x="-92" y="165"/> + <point key="canvasLocation" x="305" y="433"/> </scene> </scenes> </document> diff --git a/Example/Messaging/App/iOS/FIRAppDelegate.h b/Example/Messaging/App/iOS/FIRAppDelegate.h new file mode 100644 index 0000000..1eb5040 --- /dev/null +++ b/Example/Messaging/App/iOS/FIRAppDelegate.h @@ -0,0 +1,23 @@ +/* + * Copyright 2017 Google + * + * Licensed 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 UIKit; + +@interface FIRAppDelegate : UIResponder <UIApplicationDelegate> + +@property(strong, nonatomic) UIWindow *window; + +@end diff --git a/Example/Messaging/App/iOS/FIRAppDelegate.m b/Example/Messaging/App/iOS/FIRAppDelegate.m new file mode 100644 index 0000000..535e529 --- /dev/null +++ b/Example/Messaging/App/iOS/FIRAppDelegate.m @@ -0,0 +1,58 @@ +// Copyright 2017 Google +// +// Licensed 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 "FIRAppDelegate.h" + +#import <FirebaseCore/FIRApp.h> + +@implementation FIRAppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [FIRApp configure]; + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application { + // Sent when the application is about to move from active to inactive state. This can occur for + // certain types of temporary interruptions (such as an incoming phone call or SMS message) or + // when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame + // rates. Games should use this method to pause the game. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application { + // Use this method to release shared resources, save user data, invalidate timers, and store + // enough application state information to restore your application to its current state in case + // it is terminated later. + // If your application supports background execution, this method is called instead of + // applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application { + // Called as part of the transition from the background to the inactive state; here you can undo + // many of the changes made on entering the background. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If + // the application was previously in the background, optionally refresh the user interface. +} + +- (void)applicationWillTerminate:(UIApplication *)application { + // Called when the application is about to terminate. Save data if appropriate. See also + // applicationDidEnterBackground:. +} + +@end diff --git a/Example/Messaging/App/iOS/FIRViewController.h b/Example/Messaging/App/iOS/FIRViewController.h new file mode 100644 index 0000000..64b4b74 --- /dev/null +++ b/Example/Messaging/App/iOS/FIRViewController.h @@ -0,0 +1,21 @@ +/* + * Copyright 2017 Google + * + * Licensed 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 UIKit; + +@interface FIRViewController : UIViewController + +@end diff --git a/Example/Messaging/App/iOS/FIRViewController.m b/Example/Messaging/App/iOS/FIRViewController.m new file mode 100644 index 0000000..027aabf --- /dev/null +++ b/Example/Messaging/App/iOS/FIRViewController.m @@ -0,0 +1,33 @@ +// Copyright 2017 Google +// +// Licensed 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 "FIRViewController.h" + +@interface FIRViewController () + +@end + +@implementation FIRViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view, typically from a nib. +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +@end diff --git a/Example/Messaging/App/iOS/main.m b/Example/Messaging/App/iOS/main.m new file mode 100644 index 0000000..39c05a5 --- /dev/null +++ b/Example/Messaging/App/iOS/main.m @@ -0,0 +1,22 @@ +// Copyright 2017 Google +// +// Licensed 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 UIKit; +#import "FIRAppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([FIRAppDelegate class])); + } +} diff --git a/Example/Messaging/App/GoogleService-Info.plist b/Example/Messaging/Sample/GoogleService-Info.plist index 3f7547f..3f7547f 100644 --- a/Example/Messaging/App/GoogleService-Info.plist +++ b/Example/Messaging/Sample/GoogleService-Info.plist diff --git a/Example/Messaging/App/iOS/AppDelegate.swift b/Example/Messaging/Sample/iOS/AppDelegate.swift index 89a5120..89a5120 100644 --- a/Example/Messaging/App/iOS/AppDelegate.swift +++ b/Example/Messaging/Sample/iOS/AppDelegate.swift diff --git a/Example/Messaging/Sample/iOS/Base.lproj/LaunchScreen.storyboard b/Example/Messaging/Sample/iOS/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..fdf3f97 --- /dev/null +++ b/Example/Messaging/Sample/iOS/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> + <dependencies> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/> + <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> + </dependencies> + <scenes> + <!--View Controller--> + <scene sceneID="EHf-IW-A2E"> + <objects> + <viewController id="01J-lp-oVM" sceneMemberID="viewController"> + <layoutGuides> + <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/> + <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/> + </layoutGuides> + <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> + <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + </view> + </viewController> + <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> + </objects> + <point key="canvasLocation" x="53" y="375"/> + </scene> + </scenes> +</document> diff --git a/Example/Messaging/Sample/iOS/Base.lproj/Main.storyboard b/Example/Messaging/Sample/iOS/Base.lproj/Main.storyboard new file mode 100644 index 0000000..6df1a82 --- /dev/null +++ b/Example/Messaging/Sample/iOS/Base.lproj/Main.storyboard @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12120" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="taE-sK-BOl"> + <device id="retina4_7" orientation="portrait"> + <adaptation id="fullscreen"/> + </device> + <dependencies> + <deployment identifier="iOS"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/> + <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> + </dependencies> + <scenes> + <!--Firebase Cloud Messaging--> + <scene sceneID="tne-QT-ifu"> + <objects> + <viewController id="BYZ-38-t0r" customClass="MessagingViewController" customModule="Messaging_Example" customModuleProvider="target" sceneMemberID="viewController"> + <layoutGuides> + <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/> + <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/> + </layoutGuides> + <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> + <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + </view> + <navigationItem key="navigationItem" title="Firebase Cloud Messaging" id="z1u-kE-qKb"/> + </viewController> + <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> + </objects> + <point key="canvasLocation" x="698" y="164"/> + </scene> + <!--Navigation Controller--> + <scene sceneID="rmF-xz-rwn"> + <objects> + <placeholder placeholderIdentifier="IBFirstResponder" id="Ju1-Bj-8eG" userLabel="First Responder" sceneMemberID="firstResponder"/> + <navigationController id="taE-sK-BOl" sceneMemberID="viewController"> + <navigationBar key="navigationBar" contentMode="scaleToFill" id="iTL-Kg-11w"> + <rect key="frame" x="0.0" y="0.0" width="375" height="44"/> + <autoresizingMask key="autoresizingMask"/> + </navigationBar> + <connections> + <segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="04R-HZ-bi6"/> + </connections> + </navigationController> + </objects> + <point key="canvasLocation" x="-92" y="165"/> + </scene> + </scenes> +</document> diff --git a/Example/Messaging/App/iOS/Data+MessagingExtensions.swift b/Example/Messaging/Sample/iOS/Data+MessagingExtensions.swift index 99ded25..99ded25 100644 --- a/Example/Messaging/App/iOS/Data+MessagingExtensions.swift +++ b/Example/Messaging/Sample/iOS/Data+MessagingExtensions.swift diff --git a/Example/Messaging/App/iOS/Environment.swift b/Example/Messaging/Sample/iOS/Environment.swift index 5219c64..5219c64 100644 --- a/Example/Messaging/App/iOS/Environment.swift +++ b/Example/Messaging/Sample/iOS/Environment.swift diff --git a/Example/Messaging/App/iOS/Messaging-Info.plist b/Example/Messaging/Sample/iOS/Messaging-Info.plist index b43105e..b43105e 100644 --- a/Example/Messaging/App/iOS/Messaging-Info.plist +++ b/Example/Messaging/Sample/iOS/Messaging-Info.plist diff --git a/Example/Messaging/App/iOS/MessagingViewController.swift b/Example/Messaging/Sample/iOS/MessagingViewController.swift index 9bd07c1..9bd07c1 100644 --- a/Example/Messaging/App/iOS/MessagingViewController.swift +++ b/Example/Messaging/Sample/iOS/MessagingViewController.swift diff --git a/Example/Messaging/App/iOS/Messaging_Example.entitlements b/Example/Messaging/Sample/iOS/Messaging_Example.entitlements index 903def2..903def2 100644 --- a/Example/Messaging/App/iOS/Messaging_Example.entitlements +++ b/Example/Messaging/Sample/iOS/Messaging_Example.entitlements diff --git a/Example/Messaging/App/iOS/NotificationsController.swift b/Example/Messaging/Sample/iOS/NotificationsController.swift index 7545b44..7545b44 100644 --- a/Example/Messaging/App/iOS/NotificationsController.swift +++ b/Example/Messaging/Sample/iOS/NotificationsController.swift diff --git a/Example/Messaging/Tests/FIRMessagingServiceTest.m b/Example/Messaging/Tests/FIRMessagingServiceTest.m index 9afdced..073adad 100644 --- a/Example/Messaging/Tests/FIRMessagingServiceTest.m +++ b/Example/Messaging/Tests/FIRMessagingServiceTest.m @@ -146,12 +146,15 @@ XCTestExpectation *exceptionExpectation = [self expectationWithDescription:@"Should throw exception for invalid token"]; @try { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" [messaging.pubsub subscribeWithToken:@"abcdef1234" topic:nil options:nil handler:^(NSError *error) { XCTFail(@"Should not invoke the handler"); }]; +#pragma clang diagnostic pop } @catch (NSException *exception) { [exceptionExpectation fulfill]; @@ -169,12 +172,15 @@ XCTestExpectation *exceptionExpectation = [self expectationWithDescription:@"Should throw exception for invalid token"]; @try { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" [messaging.pubsub unsubscribeWithToken:@"abcdef1234" topic:nil options:nil handler:^(NSError *error) { XCTFail(@"Should not invoke the handler"); }]; +#pragma clang diagnostic pop } @catch (NSException *exception) { [exceptionExpectation fulfill]; diff --git a/Example/Podfile b/Example/Podfile index a26f255..0d46941 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -52,7 +52,6 @@ target 'Messaging_Example_iOS' do platform :ios, '8.0' pod 'FirebaseMessaging' , :path => '../' - pod 'FirebaseInstanceID' target 'Messaging_Tests_iOS' do inherit! :search_paths @@ -60,6 +59,11 @@ target 'Messaging_Example_iOS' do end end +target 'Messaging_Sample_iOS' do + platform :ios, '8.0' + pod 'FirebaseMessaging' , :path => '../' +end + target 'Storage_Example_iOS' do platform :ios, '8.0' diff --git a/Example/Storage/App/iOS/FIRAppDelegate.m b/Example/Storage/App/iOS/FIRAppDelegate.m index d8e4497..9568d06 100644 --- a/Example/Storage/App/iOS/FIRAppDelegate.m +++ b/Example/Storage/App/iOS/FIRAppDelegate.m @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -@import FirebaseStorage; - #import "FIRAppDelegate.h" @implementation FIRAppDelegate diff --git a/Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m b/Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m index 8e826c9..b20108a 100644 --- a/Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m +++ b/Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m @@ -12,10 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#import <FirebaseStorage/FIRStorageMetadata.h> #import <XCTest/XCTest.h> -#import <math.h> - #import "FirebaseStorage.h" #import <FirebaseCore/FIRApp.h> @@ -381,7 +378,7 @@ NSTimeInterval kFIRStorageIntegrationTestTimeout = 30; /// Only allow 1kB size, which is smaller than our file [ref dataWithMaxSize:1 * 1024 completion:^(NSData *data, NSError *error) { - XCTAssertEqual(data, nil); + XCTAssertNil(data); XCTAssertEqual(error.code, FIRStorageErrorCodeDownloadSizeExceeded); [expectation fulfill]; }]; @@ -389,6 +386,33 @@ NSTimeInterval kFIRStorageIntegrationTestTimeout = 30; [self waitForExpectations]; } +- (void)testUnauthenticatedSimpleGetDownloadURL { + XCTestExpectation *expectation = + [self expectationWithDescription:@"testUnauthenticatedSimpleGetDownloadURL"]; + + FIRStorageReference *ref = [self.storage referenceWithPath:@"ios/public/1mb"]; + + // Download URL format is + // "https://firebasestorage.googleapis.com/v0/b/{bucket}/o/{path}?alt=media&token={token}" + NSString *downloadURLPattern = + @"^https:\\/\\/firebasestorage.googleapis.com\\/v0\\/b\\/[^\\/]*\\/o\\/" + @"ios%2Fpublic%2F1mb\\?alt=media&token=[a-z0-9-]*$"; + + [ref downloadURLWithCompletion:^(NSURL *downloadURL, NSError *error) { + XCTAssertNil(error); + NSRegularExpression *testRegex = + [NSRegularExpression regularExpressionWithPattern:downloadURLPattern options:0 error:nil]; + NSString *urlString = [downloadURL absoluteString]; + XCTAssertEqual([testRegex numberOfMatchesInString:urlString + options:0 + range:NSMakeRange(0, [urlString length])], + 1); + [expectation fulfill]; + }]; + + [self waitForExpectations]; +} + - (void)testUnauthenticatedSimpleGetFile { XCTestExpectation *expectation = [self expectationWithDescription:@"testUnauthenticatedSimpleGetData"]; diff --git a/Example/Storage/Tests/Unit/FIRStorageMetadataTests.m b/Example/Storage/Tests/Unit/FIRStorageMetadataTests.m index 84b5271..6a83741 100644 --- a/Example/Storage/Tests/Unit/FIRStorageMetadataTests.m +++ b/Example/Storage/Tests/Unit/FIRStorageMetadataTests.m @@ -15,6 +15,8 @@ #import <FirebaseStorage/FIRStorageMetadata.h> #import <XCTest/XCTest.h> +#import "FIRStorageGetDownloadURLTask.h" +#import "FIRStorageGetDownloadURLTask_Private.h" #import "FIRStorageMetadata.h" #import "FIRStorageMetadata_Private.h" #import "FIRStorageUtils.h" @@ -39,7 +41,6 @@ kFIRStorageMetadataContentLanguage : @"en-us", kFIRStorageMetadataContentType : @"application/octet-stream", kFIRStorageMetadataCustomMetadata : @{@"foo" : @{@"bar" : @"baz"}}, - kFIRStorageMetadataDownloadTokens : @"1234567890", kFIRStorageMetadataGeneration : @"12345", kFIRStorageMetadataMetageneration : @"67890", kFIRStorageMetadataName : @"path/to/object", @@ -58,12 +59,6 @@ XCTAssertEqualObjects(metadata.contentType, metaDict[kFIRStorageMetadataContentType]); XCTAssertEqualObjects(metadata.customMetadata, metaDict[kFIRStorageMetadataCustomMetadata]); XCTAssertEqualObjects(metadata.md5Hash, metaDict[kFIRStorageMetadataMd5Hash]); - NSString *URLFormat = @"https://firebasestorage.googleapis.com/v0/b/%@/o/%@?alt=media&token=%@"; - NSString *URLString = [NSString - stringWithFormat:URLFormat, metaDict[kFIRStorageMetadataBucket], - [FIRStorageUtils GCSEscapedString:metaDict[kFIRStorageMetadataName]], - metaDict[kFIRStorageMetadataDownloadTokens]]; - XCTAssertEqualObjects([metadata.downloadURL description], URLString); NSString *generation = [NSString stringWithFormat:@"%lld", metadata.generation]; XCTAssertEqualObjects(generation, metaDict[kFIRStorageMetadataGeneration]); NSString *metageneration = [NSString stringWithFormat:@"%lld", metadata.metageneration]; @@ -86,7 +81,6 @@ kFIRStorageMetadataContentLanguage : @"en-us", kFIRStorageMetadataContentType : @"application/octet-stream", kFIRStorageMetadataCustomMetadata : @{@"foo" : @{@"bar" : @"baz"}}, - kFIRStorageMetadataDownloadTokens : @"1234567890", kFIRStorageMetadataGeneration : @"12345", kFIRStorageMetadataMetageneration : @"67890", kFIRStorageMetadataName : @"path/to/object", @@ -97,7 +91,7 @@ }; FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:metaDict]; NSDictionary *dictRepresentation = [metadata dictionaryRepresentation]; - XCTAssertNotEqual(dictRepresentation, nil); + XCTAssertNotNil(dictRepresentation); XCTAssertEqualObjects(dictRepresentation[kFIRStorageMetadataBucket], metaDict[kFIRStorageMetadataBucket]); XCTAssertEqualObjects(dictRepresentation[kFIRStorageMetadataCacheControl], @@ -130,60 +124,28 @@ metaDict[kFIRStorageMetadataMd5Hash]); } -- (void)testInitialzeNoDownloadTokensGetToken { - NSDictionary *metaDict = @{ - kFIRStorageMetadataBucket : @"bucket", - kFIRStorageMetadataName : @"path/to/object", - }; - FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:metaDict]; - XCTAssertNotNil(metadata); - XCTAssertEqual(metadata.downloadURL, nil); - XCTAssertEqual(metadata.downloadURLs, nil); -} - -- (void)testInitialzeMultipleDownloadTokensGetToken { +- (void)testInitializeEmptyDownloadURL { NSDictionary *metaDict = @{ kFIRStorageMetadataBucket : @"bucket", - kFIRStorageMetadataDownloadTokens : @"12345,67890", kFIRStorageMetadataName : @"path/to/object", }; - FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:metaDict]; - XCTAssertNotNil(metadata); - NSString *URLformat = @"https://firebasestorage.googleapis.com/v0/b/%@/o/%@?alt=media&token=%@"; - NSString *URLString0 = [NSString - stringWithFormat:URLformat, metaDict[kFIRStorageMetadataBucket], - [FIRStorageUtils GCSEscapedString:metaDict[kFIRStorageMetadataName]], - @"12345"]; - NSString *URLString1 = [NSString - stringWithFormat:URLformat, metaDict[kFIRStorageMetadataBucket], - [FIRStorageUtils GCSEscapedString:metaDict[kFIRStorageMetadataName]], - @"67890"]; - XCTAssertEqualObjects([metadata.downloadURL absoluteString], URLString0); - XCTAssertEqualObjects([metadata.downloadURLs[0] absoluteString], URLString0); - XCTAssertEqualObjects([metadata.downloadURLs[1] absoluteString], URLString1); + NSURL *actualURL = [FIRStorageGetDownloadURLTask downloadURLFromMetadataDictionary:metaDict]; + XCTAssertNil(actualURL); } -- (void)testMultipleDownloadURLsGetToken { +- (void)testInitializeDownloadURLFromToken { NSDictionary *metaDict = @{ kFIRStorageMetadataBucket : @"bucket", + kFIRStorageMetadataDownloadTokens : @"12345,ignored", kFIRStorageMetadataName : @"path/to/object", }; - FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:metaDict]; NSString *URLformat = @"https://firebasestorage.googleapis.com/v0/b/%@/o/%@?alt=media&token=%@"; - NSString *URLString0 = [NSString + NSString *expectedURL = [NSString stringWithFormat:URLformat, metaDict[kFIRStorageMetadataBucket], [FIRStorageUtils GCSEscapedString:metaDict[kFIRStorageMetadataName]], @"12345"]; - NSString *URLString1 = [NSString - stringWithFormat:URLformat, metaDict[kFIRStorageMetadataBucket], - [FIRStorageUtils GCSEscapedString:metaDict[kFIRStorageMetadataName]], - @"67890"]; - NSURL *URL0 = [NSURL URLWithString:URLString0]; - NSURL *URL1 = [NSURL URLWithString:URLString1]; - NSArray *downloadURLs = @[ URL0, URL1 ]; - [metadata setValue:downloadURLs forKey:@"downloadURLs"]; - NSDictionary *newMetaDict = metadata.dictionaryRepresentation; - XCTAssertEqualObjects(newMetaDict[kFIRStorageMetadataDownloadTokens], @"12345,67890"); + NSURL *actualURL = [FIRStorageGetDownloadURLTask downloadURLFromMetadataDictionary:metaDict]; + XCTAssertEqualObjects([actualURL absoluteString], expectedURL); } - (void)testInitialzeMetadataWithFile { diff --git a/Firebase/Auth/Source/Public/FIRAuth.h b/Firebase/Auth/Source/Public/FIRAuth.h index 7d5ea42..236dd10 100644 --- a/Firebase/Auth/Source/Public/FIRAuth.h +++ b/Firebase/Auth/Source/Public/FIRAuth.h @@ -33,7 +33,7 @@ NS_ASSUME_NONNULL_BEGIN /** @typedef FIRUserUpdateCallback - @brief The type of block invoked when a request to update a current user is completed. + @brief The type of block invoked when a request to update the current user is completed. */ typedef void (^FIRUserUpdateCallback)(NSError *_Nullable error) NS_SWIFT_NAME(UserUpdateCallback); diff --git a/Firebase/Core/CHANGELOG.md b/Firebase/Core/CHANGELOG.md index 4289cae..f4707ea 100644 --- a/Firebase/Core/CHANGELOG.md +++ b/Firebase/Core/CHANGELOG.md @@ -1,4 +1,6 @@ # Unreleased +- [changed] Removed `UIKit` import from `FIRApp.h`. +- [changed] Removed deprecated methods. # 2018-03-06 -- v4.0.16 -- M22 - [changed] Addresses CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF warnings that surface in newer versions of Xcode and CocoaPods. diff --git a/Firebase/Core/FIRApp.m b/Firebase/Core/FIRApp.m index c2ce28e..c43db6e 100644 --- a/Firebase/Core/FIRApp.m +++ b/Firebase/Core/FIRApp.m @@ -133,23 +133,6 @@ static NSMutableDictionary *sLibraryVersions; [FIRApp sendNotificationsToSDKs:sDefaultApp]; sDefaultApp.alreadySentConfigureNotification = YES; } - - if (![FIRAppEnvironmentUtil isFromAppStore]) { - // Support for iOS 7 has been deprecated, but will continue to function for the time being. - // Log a notice for developers who are still targeting iOS 7 as the minimum OS version - // supported. - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ - NSDictionary<NSString *, id> *info = [[NSBundle mainBundle] infoDictionary]; - - NSString *minVersion = info[@"MinimumOSVersion"]; - if ([minVersion hasPrefix:@"7."]) { - FIRLogNotice(kFIRLoggerCore, @"I-COR000026", - @"Support for iOS 7 is deprecated and will " - @"stop working in the future. Please upgrade your app to target iOS 8 or " - @"above."); - } - }); - } } } @@ -315,13 +298,6 @@ static NSMutableDictionary *sLibraryVersions; return NO; } - if (NSClassFromString(@"FIRAppIndexing") != nil) { - FIRLogDebug(kFIRLoggerCore, @"I-COR000024", - @"Firebase App Indexing on iOS is deprecated. " - @"You don't need to take any action at this time. Learn more about Firebase App " - @"Indexing at https://firebase.google.com/docs/app-indexing/."); - } - // Initialize the Analytics once there is a valid options under default app. Analytics should // always initialize first by itself before the other SDKs. if ([self.name isEqualToString:kFIRDefaultAppName]) { diff --git a/Firebase/Core/FIRConfiguration.m b/Firebase/Core/FIRConfiguration.m index 02617ef..cd64862 100644 --- a/Firebase/Core/FIRConfiguration.m +++ b/Firebase/Core/FIRConfiguration.m @@ -35,12 +35,6 @@ extern void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel); return self; } -// This is deprecated, use setLoggerLevel instead. -- (void)setLogLevel:(FIRLogLevel)logLevel { - NSAssert(logLevel <= kFIRLogLevelMax, @"Invalid log level, %ld", (long)logLevel); - _logLevel = logLevel; -} - - (void)setLoggerLevel:(FIRLoggerLevel)loggerLevel { NSAssert(loggerLevel <= FIRLoggerLevelMax && loggerLevel >= FIRLoggerLevelMin, @"Invalid logger level, %ld", (long)loggerLevel); diff --git a/Firebase/Core/FIROptions.m b/Firebase/Core/FIROptions.m index 135ed3b..8dcd749 100644 --- a/Firebase/Core/FIROptions.m +++ b/Firebase/Core/FIROptions.m @@ -194,45 +194,6 @@ static NSDictionary *sDefaultOptionsDictionary = nil; #pragma mark - Public instance methods -- (instancetype)initWithGoogleAppID:(NSString *)googleAppID - bundleID:(NSString *)bundleID - GCMSenderID:(NSString *)GCMSenderID - APIKey:(NSString *)APIKey - clientID:(NSString *)clientID - trackingID:(NSString *)trackingID - androidClientID:(NSString *)androidClientID - databaseURL:(NSString *)databaseURL - storageBucket:(NSString *)storageBucket - deepLinkURLScheme:(NSString *)deepLinkURLScheme { - self = [super init]; - if (self) { - if (!googleAppID) { - [NSException raise:kFirebaseCoreErrorDomain format:@"Please specify a valid Google App ID."]; - } else if (!GCMSenderID) { - [NSException raise:kFirebaseCoreErrorDomain format:@"Please specify a valid GCM Sender ID."]; - } - - // `bundleID` is a required property, default to the main `bundleIdentifier` if it's `nil`. - if (!bundleID) { - bundleID = [[NSBundle mainBundle] bundleIdentifier]; - } - - NSMutableDictionary *mutableOptionsDict = [NSMutableDictionary dictionary]; - [mutableOptionsDict setValue:googleAppID forKey:kFIRGoogleAppID]; - [mutableOptionsDict setValue:bundleID forKey:kFIRBundleID]; - [mutableOptionsDict setValue:GCMSenderID forKey:kFIRGCMSenderID]; - [mutableOptionsDict setValue:APIKey forKey:kFIRAPIKey]; - [mutableOptionsDict setValue:clientID forKey:kFIRClientID]; - [mutableOptionsDict setValue:trackingID forKey:kFIRTrackingID]; - [mutableOptionsDict setValue:androidClientID forKey:kFIRAndroidClientID]; - [mutableOptionsDict setValue:databaseURL forKey:kFIRDatabaseURL]; - [mutableOptionsDict setValue:storageBucket forKey:kFIRStorageBucket]; - self.optionsDictionary = mutableOptionsDict; - self.deepLinkURLScheme = deepLinkURLScheme; - } - return self; -} - - (instancetype)initWithContentsOfFile:(NSString *)plistPath { self = [super init]; if (self) { diff --git a/Firebase/Core/Public/FIRApp.h b/Firebase/Core/Public/FIRApp.h index 9610455..fb18b75 100644 --- a/Firebase/Core/Public/FIRApp.h +++ b/Firebase/Core/Public/FIRApp.h @@ -16,11 +16,6 @@ #import <Foundation/Foundation.h> -#if TARGET_OS_IOS -// TODO: Remove UIKit import on next breaking change release -#import <UIKit/UIKit.h> -#endif - @class FIROptions; NS_ASSUME_NONNULL_BEGIN @@ -90,19 +85,11 @@ NS_SWIFT_NAME(FirebaseApp) */ + (nullable FIRApp *)appNamed:(NSString *)name NS_SWIFT_NAME(app(name:)); -#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 /** * Returns the set of all extant FIRApp instances, or nil if there are no FIRApp instances. This * method is thread safe. */ @property(class, readonly, nullable) NSDictionary<NSString *, FIRApp *> *allApps; -#else -/** - * Returns the set of all extant FIRApp instances, or nil if there are no FIRApp instances. This - * method is thread safe. - */ -+ (nullable NSDictionary<NSString *, FIRApp *> *)allApps NS_SWIFT_NAME(allApps()); -#endif // defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 /** * Cleans up the current FIRApp, freeing associated data and returning its name to the pool for diff --git a/Firebase/Core/Public/FIRConfiguration.h b/Firebase/Core/Public/FIRConfiguration.h index 05bd261..95bba5e 100644 --- a/Firebase/Core/Public/FIRConfiguration.h +++ b/Firebase/Core/Public/FIRConfiguration.h @@ -19,25 +19,6 @@ #import "FIRAnalyticsConfiguration.h" #import "FIRLoggerLevel.h" -/** - * The log levels used by FIRConfiguration. - */ -typedef NS_ENUM(NSInteger, FIRLogLevel) { - /** Error */ - kFIRLogLevelError __deprecated = 0, - /** Warning */ - kFIRLogLevelWarning __deprecated, - /** Info */ - kFIRLogLevelInfo __deprecated, - /** Debug */ - kFIRLogLevelDebug __deprecated, - /** Assert */ - kFIRLogLevelAssert __deprecated, - /** Max */ - kFIRLogLevelMax __deprecated = kFIRLogLevelAssert -} DEPRECATED_MSG_ATTRIBUTE( - "Use -FIRDebugEnabled and -FIRDebugDisabled or setLoggerLevel. See FIRApp.h for more details."); - NS_ASSUME_NONNULL_BEGIN /** @@ -47,21 +28,12 @@ NS_ASSUME_NONNULL_BEGIN NS_SWIFT_NAME(FirebaseConfiguration) @interface FIRConfiguration : NSObject -#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 /** Returns the shared configuration object. */ @property(class, nonatomic, readonly) FIRConfiguration *sharedInstance NS_SWIFT_NAME(shared); -#else -/** Returns the shared configuration object. */ -+ (FIRConfiguration *)sharedInstance NS_SWIFT_NAME(shared()); -#endif // defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 /** The configuration class for Firebase Analytics. */ @property(nonatomic, readwrite) FIRAnalyticsConfiguration *analyticsConfiguration; -/** Global log level. Defaults to kFIRLogLevelError. */ -@property(nonatomic, readwrite, assign) FIRLogLevel logLevel DEPRECATED_MSG_ATTRIBUTE( - "Use -FIRDebugEnabled and -FIRDebugDisabled or setLoggerLevel. See FIRApp.h for more details."); - /** * Sets the logging level for internal Firebase logging. Firebase will only log messages * that are logged at or below loggerLevel. The messages are logged both to the Xcode diff --git a/Firebase/Core/Public/FIROptions.h b/Firebase/Core/Public/FIROptions.h index b4e3b3b..87a01dd 100644 --- a/Firebase/Core/Public/FIROptions.h +++ b/Firebase/Core/Public/FIROptions.h @@ -91,25 +91,6 @@ NS_SWIFT_NAME(FirebaseOptions) @property(nonatomic, copy, nullable) NSString *storageBucket; /** - * Initializes a customized instance of FIROptions with keys. googleAppID, bundleID and GCMSenderID - * are required. Other keys may required for configuring specific services. - */ -- (instancetype)initWithGoogleAppID:(NSString *)googleAppID - bundleID:(NSString *)bundleID - GCMSenderID:(NSString *)GCMSenderID - APIKey:(NSString *)APIKey - clientID:(NSString *)clientID - trackingID:(NSString *)trackingID - androidClientID:(NSString *)androidClientID - databaseURL:(NSString *)databaseURL - storageBucket:(NSString *)storageBucket - deepLinkURLScheme:(NSString *)deepLinkURLScheme - DEPRECATED_MSG_ATTRIBUTE( - "Use `-[[FIROptions alloc] initWithGoogleAppID:GCMSenderID:]` " - "(`FirebaseOptions(googleAppID:gcmSenderID:)` in Swift)` and property " - "setters instead."); - -/** * Initializes a customized instance of FIROptions from the file at the given plist file path. This * will read the file synchronously from disk. * For example, diff --git a/Firebase/Core/third_party/FIRAppEnvironmentUtil.m b/Firebase/Core/third_party/FIRAppEnvironmentUtil.m index 90e66f0..faee38b 100644 --- a/Firebase/Core/third_party/FIRAppEnvironmentUtil.m +++ b/Firebase/Core/third_party/FIRAppEnvironmentUtil.m @@ -13,9 +13,6 @@ // limitations under the License. #import <Foundation/Foundation.h> -#if TARGET_OS_IOS || TARGET_OS_TV -#import <UIKit/UIKit.h> -#endif #import "FIRAppEnvironmentUtil.h" @@ -207,11 +204,7 @@ static BOOL isAppEncrypted() { } + (NSString *)systemVersion { - #if TARGET_OS_IOS || TARGET_OS_TV - return [UIDevice currentDevice].systemVersion; - #elif TARGET_OS_OSX return [NSProcessInfo processInfo].operatingSystemVersionString; - #endif } + (BOOL)isAppExtension { diff --git a/Firebase/Database/Core/FPersistentConnection.m b/Firebase/Database/Core/FPersistentConnection.m index 870727c..c32d18c 100644 --- a/Firebase/Database/Core/FPersistentConnection.m +++ b/Firebase/Database/Core/FPersistentConnection.m @@ -735,18 +735,22 @@ static void reachabilityCallback(SCNetworkReachabilityRef ref, SCNetworkReachabi } - (void) cancelSentTransactions { - NSMutableArray* toPrune = [[NSMutableArray alloc] init]; + NSMutableDictionary<NSNumber*, FOutstandingPut*>* cancelledOutstandingPuts = [[NSMutableDictionary alloc] init]; + for (NSNumber* index in self.outstandingPuts) { FOutstandingPut* put = self.outstandingPuts[index]; if (put.request[kFWPRequestHash] && put.sent) { - // This is a sent transaction put - put.onCompleteBlock(kFTransactionDisconnect, @"Client was disconnected while running a transaction"); - [toPrune addObject:index]; + // This is a sent transaction put. + cancelledOutstandingPuts[index] = put; } } - for (NSNumber* index in toPrune) { + + [cancelledOutstandingPuts enumerateKeysAndObjectsUsingBlock:^(NSNumber *index, FOutstandingPut *outstandingPut, BOOL *stop) { + // `onCompleteBlock:` may invoke `rerunTransactionsForPath:` and enqueue new writes. We defer calling + // it until we have finished enumerating all existing writes. + outstandingPut.onCompleteBlock(kFTransactionDisconnect, @"Client was disconnected while running a transaction"); [self.outstandingPuts removeObjectForKey:index]; - } + }]; } - (void) onDataPushWithAction:(NSString *)action andBody:(NSDictionary *)body { diff --git a/Firebase/Database/Core/FRepo.m b/Firebase/Database/Core/FRepo.m index 1c4b956..ae1d8e8 100644 --- a/Firebase/Database/Core/FRepo.m +++ b/Firebase/Database/Core/FRepo.m @@ -513,11 +513,11 @@ } - (void)onConnect:(FPersistentConnection *)fpconnection { - [self updateInfo:kDotInfoConnected withValue:@true]; + [self updateInfo:kDotInfoConnected withValue:@YES]; } - (void)onDisconnect:(FPersistentConnection *)fpconnection { - [self updateInfo:kDotInfoConnected withValue:@false]; + [self updateInfo:kDotInfoConnected withValue:@NO]; [self runOnDisconnectEvents]; } diff --git a/Firebase/Messaging/FIRMMessageCode.h b/Firebase/Messaging/FIRMMessageCode.h index 1ec3a39..c1b8ffb 100644 --- a/Firebase/Messaging/FIRMMessageCode.h +++ b/Firebase/Messaging/FIRMMessageCode.h @@ -37,7 +37,7 @@ typedef NS_ENUM(NSInteger, FIRMessagingMessageCode) { kFIRMessagingMessageCodeMessaging013 = 2013, // I-FCM002013 kFIRMessagingMessageCodeMessaging014 = 2014, // I-FCM002014 kFIRMessagingMessageCodeMessaging015 = 2015, // I-FCM002015 - kFIRMessagingMessageCodeMessaging016 = 2016, // I-FCM002016 + kFIRMessagingMessageCodeMessaging016 = 2016, // I-FCM002016 - no longer used kFIRMessagingMessageCodeMessaging017 = 2017, // I-FCM002017 kFIRMessagingMessageCodeMessaging018 = 2018, // I-FCM002018 kFIRMessagingMessageCodeRemoteMessageDelegateMethodNotImplemented = 2019, // I-FCM002019 diff --git a/Firebase/Messaging/FIRMessaging.m b/Firebase/Messaging/FIRMessaging.m index e02a125..8fafab8 100644 --- a/Firebase/Messaging/FIRMessaging.m +++ b/Firebase/Messaging/FIRMessaging.m @@ -73,29 +73,11 @@ NSString * const FIRMessagingRegistrationTokenRefreshedNotification = NSString *const kFIRMessagingUserDefaultsKeyAutoInitEnabled = @"com.firebase.messaging.auto-init.enabled"; // Auto Init Enabled key stored in NSUserDefaults +NSString *const kFIRMessagingAPNSTokenType = @"APNSTokenType"; // APNS Token type key stored in user info. + static NSString *const kFIRMessagingPlistAutoInitEnabled = @"FirebaseMessagingAutoInitEnabled"; // Auto Init Enabled key stored in Info.plist -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -/// A private helper to convert enum values without depending on their numerical values -/// (avoid casting). This can be removed when InstanceID's deprecated APNS type enum is -/// removed. -FIRInstanceIDAPNSTokenType FIRIIDAPNSTokenTypeFromAPNSTokenType(FIRMessagingAPNSTokenType type) { - switch (type) { - case FIRMessagingAPNSTokenTypeProd: - return FIRInstanceIDAPNSTokenTypeProd; - case FIRMessagingAPNSTokenTypeSandbox: - return FIRInstanceIDAPNSTokenTypeSandbox; - case FIRMessagingAPNSTokenTypeUnknown: - return FIRInstanceIDAPNSTokenTypeUnknown; - - default: - return FIRInstanceIDAPNSTokenTypeUnknown; - } -} -#pragma clang diagnostic pop - @interface FIRMessagingMessageInfo () @property(nonatomic, readwrite, assign) FIRMessagingMessageStatus status; @@ -142,7 +124,9 @@ FIRInstanceIDAPNSTokenType FIRIIDAPNSTokenTypeFromAPNSTokenType(FIRMessagingAPNS @end @interface FIRMessaging ()<FIRMessagingClientDelegate, FIRMessagingReceiverDelegate, - FIRReachabilityDelegate> + FIRReachabilityDelegate> { + BOOL _shouldEstablishDirectChannel; +} // FIRApp properties @property(nonatomic, readwrite, copy) NSString *fcmSenderID; @@ -206,14 +190,6 @@ FIRInstanceIDAPNSTokenType FIRIIDAPNSTokenTypeFromAPNSTokenType(FIRMessagingAPNS [self teardown]; } -- (void)setRemoteMessageDelegate:(id<FIRMessagingDelegate>)delegate { - _delegate = delegate; -} - -- (id<FIRMessagingDelegate>)remoteMessageDelegate { - return self.delegate; -} - #pragma mark - Config - (void)start { @@ -264,11 +240,6 @@ FIRInstanceIDAPNSTokenType FIRIIDAPNSTokenTypeFromAPNSTokenType(FIRMessagingAPNS name:kFIRMessagingRegistrationTokenRefreshNotification object:nil]; [center addObserver:self - selector:@selector(didReceiveAPNSToken:) - name:kFIRMessagingAPNSTokenNotification - object:nil]; - - [center addObserver:self selector:@selector(applicationStateChanged) name:UIApplicationDidBecomeActiveNotification object:nil]; @@ -477,11 +448,13 @@ FIRInstanceIDAPNSTokenType FIRIIDAPNSTokenTypeFromAPNSTokenType(FIRMessagingAPNS } self.apnsTokenData = apnsToken; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [self.instanceID setAPNSToken:apnsToken - type:FIRIIDAPNSTokenTypeFromAPNSTokenType(type)]; -#pragma clang diagnostic pop + // Notify InstanceID that APNS Token has been set. + NSDictionary *userInfo = @{kFIRMessagingAPNSTokenType : @(type)}; + NSNotification *notification = + [NSNotification notificationWithName:kFIRMessagingAPNSTokenNotification + object:[apnsToken copy] + userInfo:userInfo]; + [[NSNotificationQueue defaultQueue] enqueueNotification:notification postingStyle:NSPostASAP]; } #pragma mark - FCM @@ -578,18 +551,16 @@ FIRInstanceIDAPNSTokenType FIRIIDAPNSTokenTypeFromAPNSTokenType(FIRMessagingAPNS [self validateDelegateConformsToTokenAvailabilityMethods]; } -// Check if the delegate conforms to either |didReceiveRegistrationToken:| or -// |didRefreshRegistrationToken:|, and display a warning to the developer if not. +// Check if the delegate conforms to |didReceiveRegistrationToken:| +// and display a warning to the developer if not. // NOTE: Once |didReceiveRegistrationToken:| can be made a required method, this // check can be removed. - (void)validateDelegateConformsToTokenAvailabilityMethods { if (self.delegate && - ![self.delegate respondsToSelector:@selector(messaging:didReceiveRegistrationToken:)] && - ![self.delegate respondsToSelector:@selector(messaging:didRefreshRegistrationToken:)]) { + ![self.delegate respondsToSelector:@selector(messaging:didReceiveRegistrationToken:)]) { FIRMessagingLoggerWarn(kFIRMessagingMessageCodeTokenDelegateMethodsNotImplemented, @"The object %@ does not respond to " - @"-messaging:didReceiveRegistrationToken:, nor " - @"-messaging:didRefreshRegistrationToken:. Please implement " + @"-messaging:didReceiveRegistrationToken:. Please implement " @"-messaging:didReceiveRegistrationToken: to be provided with an FCM " @"token.", self.delegate.description); } @@ -792,12 +763,6 @@ FIRInstanceIDAPNSTokenType FIRIIDAPNSTokenTypeFromAPNSTokenType(FIRMessagingAPNS return [self currentLocale]; } -- (void)setAPNSToken:(NSData *)apnsToken error:(NSError *)error { - if (apnsToken) { - self.apnsTokenData = [apnsToken copy]; - } -} - #pragma mark - FIRMessagingReceiverDelegate - (void)receiver:(FIRMessagingReceiver *)receiver @@ -807,11 +772,6 @@ FIRInstanceIDAPNSTokenType FIRIIDAPNSTokenTypeFromAPNSTokenType(FIRMessagingAPNS #pragma clang diagnostic ignored "-Wunguarded-availability" [self.delegate messaging:self didReceiveMessage:remoteMessage]; #pragma pop - } else if ([self.delegate respondsToSelector:@selector(applicationReceivedRemoteMessage:)]) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [self.delegate applicationReceivedRemoteMessage:remoteMessage]; -#pragma clang diagnostic pop } else { // Delegate methods weren't implemented, so messages are being dropped, log a warning FIRMessagingLoggerWarn(kFIRMessagingMessageCodeRemoteMessageDelegateMethodNotImplemented, @@ -888,29 +848,11 @@ FIRInstanceIDAPNSTokenType FIRIIDAPNSTokenTypeFromAPNSTokenType(FIRMessagingAPNS if (self.defaultFcmToken && ![self.defaultFcmToken isEqualToString:oldToken]) { [self notifyDelegateOfFCMTokenAvailability]; } - // Call deprecated refresh method, because it should still work (until it is removed). -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - if ([self.delegate respondsToSelector:@selector(messaging:didRefreshRegistrationToken:)]) { - [self.delegate messaging:self didRefreshRegistrationToken:token]; - } -#pragma clang diagnostic pop NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; [center postNotificationName:FIRMessagingRegistrationTokenRefreshedNotification object:nil]; } } -- (void)didReceiveAPNSToken:(NSNotification *)notification { - NSData *apnsToken = notification.object; - if (![apnsToken isKindOfClass:[NSData class]]) { - FIRMessagingLoggerDebug(kFIRMessagingMessageCodeMessaging016, @"Invalid APNS token type %@", - NSStringFromClass([notification.object class])); - return; - } - // Set this value directly, and since this came from InstanceID, don't set it back to InstanceID - self.apnsTokenData = [apnsToken copy]; -} - #pragma mark - Application Support Directory + (BOOL)hasApplicationSupportSubDirectory:(NSString *)subDirectoryName { diff --git a/Firebase/Messaging/FIRMessagingPubSub.h b/Firebase/Messaging/FIRMessagingPubSub.h index a2141e4..1c615d1 100644 --- a/Firebase/Messaging/FIRMessagingPubSub.h +++ b/Firebase/Messaging/FIRMessagingPubSub.h @@ -16,6 +16,8 @@ #import "FIRMessaging.h" +NS_ASSUME_NONNULL_BEGIN + @class FIRMessagingClient; @class FIRMessagingPubSubCache; @@ -68,7 +70,7 @@ */ - (void)subscribeWithToken:(NSString *)token topic:(NSString *)topic - options:(NSDictionary *)options + options:(nullable NSDictionary *)options handler:(FIRMessagingTopicOperationCompletion)handler; /** @@ -90,7 +92,7 @@ */ - (void)unsubscribeWithToken:(NSString *)token topic:(NSString *)topic - options:(NSDictionary *)options + options:(nullable NSDictionary *)options handler:(FIRMessagingTopicOperationCompletion)handler; /** @@ -155,3 +157,5 @@ + (BOOL)isValidTopicWithPrefix:(NSString *)topic; @end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.m b/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.m index 0b8d92b..e9d4791 100644 --- a/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.m +++ b/Firebase/Messaging/FIRMessagingRemoteNotificationsProxy.m @@ -30,6 +30,7 @@ static NSString *kUserNotificationWillPresentSelectorString = @"userNotificationCenter:willPresentNotification:withCompletionHandler:"; static NSString *kUserNotificationDidReceiveResponseSelectorString = @"userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:"; +static NSString *kReceiveDataMessageSelectorString = @"messaging:didReceiveMessage:"; @interface FIRMessagingRemoteNotificationsProxy () @@ -141,9 +142,6 @@ static NSString *kUserNotificationDidReceiveResponseSelectorString = SEL remoteNotificationWithFetchHandlerSelector = @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); - // For data message from MCS. - SEL receiveDataMessageSelector = NSSelectorFromString(@"applicationReceivedRemoteMessage:"); - // For recording when APNS tokens are registered (or fail to register) SEL registerForAPNSFailSelector = @selector(application:didFailToRegisterForRemoteNotificationsWithError:); @@ -172,11 +170,13 @@ static NSString *kUserNotificationDidReceiveResponseSelectorString = didSwizzleAppDelegate = YES; } + // For data message from MCS. + SEL receiveDataMessageSelector = NSSelectorFromString(kReceiveDataMessageSelectorString); if ([appDelegate respondsToSelector:receiveDataMessageSelector]) { [self swizzleSelector:receiveDataMessageSelector - inClass:appDelegateClass - withImplementation:(IMP)FCM_swizzle_applicationReceivedRemoteMessage - inProtocol:@protocol(UIApplicationDelegate)]; + inClass:appDelegateClass + withImplementation:(IMP)FCM_swizzle_messagingDidReceiveMessage + inProtocol:@protocol(UIApplicationDelegate)]; didSwizzleAppDelegate = YES; } @@ -669,14 +669,15 @@ id userInfoFromNotification(id notification) { return notificationUserInfo; } -void FCM_swizzle_applicationReceivedRemoteMessage( - id self, SEL _cmd, FIRMessagingRemoteMessage *remoteMessage) { +void FCM_swizzle_messagingDidReceiveMessage(id self, SEL _cmd, FIRMessaging *message, + FIRMessagingRemoteMessage *remoteMessage) { [[FIRMessaging messaging] appDidReceiveMessage:remoteMessage.appData]; IMP original_imp = [[FIRMessagingRemoteNotificationsProxy sharedProxy] originalImplementationForSelector:_cmd]; if (original_imp) { - ((void (*)(id, SEL, FIRMessagingRemoteMessage *))original_imp)(self, _cmd, remoteMessage); + ((void (*)(id, SEL, FIRMessaging *, FIRMessagingRemoteMessage *))original_imp)( + self, _cmd, message, remoteMessage); } } diff --git a/Firebase/Messaging/FIRMessaging_Private.h b/Firebase/Messaging/FIRMessaging_Private.h index 496fea4..46daee0 100644 --- a/Firebase/Messaging/FIRMessaging_Private.h +++ b/Firebase/Messaging/FIRMessaging_Private.h @@ -52,7 +52,4 @@ FOUNDATION_EXPORT NSString *const kFIRMessagingUserDefaultsKeyAutoInitEnabled; - (BOOL)isNetworkAvailable; - (FIRMessagingNetworkStatus)networkType; -// Set the APNS token for FCM. -- (void)setAPNSToken:(NSData *)apnsToken error:(NSError *)error; - @end diff --git a/Firebase/Messaging/Public/FIRMessaging.h b/Firebase/Messaging/Public/FIRMessaging.h index 479f68e..3ad15da 100644 --- a/Firebase/Messaging/Public/FIRMessaging.h +++ b/Firebase/Messaging/Public/FIRMessaging.h @@ -16,6 +16,8 @@ #import <Foundation/Foundation.h> +NS_ASSUME_NONNULL_BEGIN + /** * @related FIRMessaging * @@ -78,7 +80,7 @@ typedef void(^FIRMessagingConnectCompletion)(NSError * __nullable error) * successfully to the server. The notification object will be the messageID * of the successfully delivered message. */ -FOUNDATION_EXPORT const NSNotificationName __nonnull FIRMessagingSendSuccessNotification +FOUNDATION_EXPORT const NSNotificationName FIRMessagingSendSuccessNotification NS_SWIFT_NAME(MessagingSendSuccess); /** @@ -87,7 +89,7 @@ FOUNDATION_EXPORT const NSNotificationName __nonnull FIRMessagingSendSuccessNoti * message. The userInfo dictionary will contain the relevant error * information for the failure. */ -FOUNDATION_EXPORT const NSNotificationName __nonnull FIRMessagingSendErrorNotification +FOUNDATION_EXPORT const NSNotificationName FIRMessagingSendErrorNotification NS_SWIFT_NAME(MessagingSendError); /** @@ -98,7 +100,7 @@ FOUNDATION_EXPORT const NSNotificationName __nonnull FIRMessagingSendErrorNotifi * It is recommended to retrieve any missing messages directly from the * server. */ -FOUNDATION_EXPORT const NSNotificationName __nonnull FIRMessagingMessagesDeletedNotification +FOUNDATION_EXPORT const NSNotificationName FIRMessagingMessagesDeletedNotification NS_SWIFT_NAME(MessagingMessagesDeleted); /** @@ -106,7 +108,7 @@ FOUNDATION_EXPORT const NSNotificationName __nonnull FIRMessagingMessagesDeleted * an FCM socket connection. You can query the connection state in this * notification by checking the `isDirectChannelEstablished` property of FIRMessaging. */ -FOUNDATION_EXPORT const NSNotificationName __nonnull FIRMessagingConnectionStateChangedNotification +FOUNDATION_EXPORT const NSNotificationName FIRMessagingConnectionStateChangedNotification NS_SWIFT_NAME(MessagingConnectionStateChanged); /** @@ -114,7 +116,7 @@ FOUNDATION_EXPORT const NSNotificationName __nonnull FIRMessagingConnectionState * FIRMessaging delegate method `messaging:didReceiveRegistrationToken:` to receive current and * updated tokens. */ -FOUNDATION_EXPORT const NSNotificationName __nonnull +FOUNDATION_EXPORT const NSNotificationName FIRMessagingRegistrationTokenRefreshedNotification NS_SWIFT_NAME(MessagingRegistrationTokenRefreshed); #else @@ -123,7 +125,7 @@ FOUNDATION_EXPORT const NSNotificationName __nonnull * successfully to the server. The notification object will be the messageID * of the successfully delivered message. */ -FOUNDATION_EXPORT NSString * __nonnull const FIRMessagingSendSuccessNotification +FOUNDATION_EXPORT NSString *const FIRMessagingSendSuccessNotification NS_SWIFT_NAME(MessagingSendSuccessNotification); /** @@ -132,7 +134,7 @@ FOUNDATION_EXPORT NSString * __nonnull const FIRMessagingSendSuccessNotification * message. The userInfo dictionary will contain the relevant error * information for the failure. */ -FOUNDATION_EXPORT NSString * __nonnull const FIRMessagingSendErrorNotification +FOUNDATION_EXPORT NSString *const FIRMessagingSendErrorNotification NS_SWIFT_NAME(MessagingSendErrorNotification); /** @@ -143,7 +145,7 @@ FOUNDATION_EXPORT NSString * __nonnull const FIRMessagingSendErrorNotification * It is recommended to retrieve any missing messages directly from the * server. */ -FOUNDATION_EXPORT NSString * __nonnull const FIRMessagingMessagesDeletedNotification +FOUNDATION_EXPORT NSString *const FIRMessagingMessagesDeletedNotification NS_SWIFT_NAME(MessagingMessagesDeletedNotification); /** @@ -151,7 +153,7 @@ FOUNDATION_EXPORT NSString * __nonnull const FIRMessagingMessagesDeletedNotifica * an FCM socket connection. You can query the connection state in this * notification by checking the `isDirectChannelEstablished` property of FIRMessaging. */ -FOUNDATION_EXPORT NSString * __nonnull const FIRMessagingConnectionStateChangedNotification +FOUNDATION_EXPORT NSString *const FIRMessagingConnectionStateChangedNotification NS_SWIFT_NAME(MessagingConnectionStateChangedNotification); /** @@ -159,7 +161,7 @@ FOUNDATION_EXPORT NSString * __nonnull const FIRMessagingConnectionStateChangedN * FIRMessaging delegate method `messaging:didReceiveRegistrationToken:` to receive current and * updated tokens. */ -FOUNDATION_EXPORT NSString * __nonnull const FIRMessagingRegistrationTokenRefreshedNotification +FOUNDATION_EXPORT NSString *const FIRMessagingRegistrationTokenRefreshedNotification NS_SWIFT_NAME(MessagingRegistrationTokenRefreshedNotification); #endif // defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 @@ -232,15 +234,13 @@ NS_SWIFT_NAME(MessagingRemoteMessage) @interface FIRMessagingRemoteMessage : NSObject /// The downstream message received by the application. -@property(nonatomic, readonly, strong, nonnull) NSDictionary *appData; +@property(nonatomic, readonly, strong) NSDictionary *appData; @end @class FIRMessaging; /** - * A protocol to handle events from FCM for devices running iOS 10 or above. + * A protocol to handle token update or data message delivery from FCM. * - * To support devices running iOS 9 or below, use the local and remote notifications handlers - * defined in UIApplicationDelegate protocol. */ NS_SWIFT_NAME(MessagingDelegate) @protocol FIRMessagingDelegate <NSObject> @@ -253,32 +253,18 @@ NS_SWIFT_NAME(MessagingDelegate) /// * Uploading the FCM token to your application server, so targeted notifications can be sent. /// /// * Subscribing to any topics. -- (void)messaging:(nonnull FIRMessaging *)messaging - didReceiveRegistrationToken:(nonnull NSString *)fcmToken +- (void)messaging:(FIRMessaging *)messaging + didReceiveRegistrationToken:(NSString *)fcmToken NS_SWIFT_NAME(messaging(_:didReceiveRegistrationToken:)); -/// This method will be called whenever FCM receives a new, default FCM token for your -/// Firebase project's Sender ID. This method is deprecated. Please use -/// `messaging:didReceiveRegistrationToken:`. -- (void)messaging:(nonnull FIRMessaging *)messaging - didRefreshRegistrationToken:(nonnull NSString *)fcmToken - NS_SWIFT_NAME(messaging(_:didRefreshRegistrationToken:)) - __deprecated_msg("Please use messaging:didReceiveRegistrationToken:, which is called for both \ - current and refreshed tokens."); - /// This method is called on iOS 10 devices to handle data messages received via FCM through its /// direct channel (not via APNS). For iOS 9 and below, the FCM data message is delivered via the /// UIApplicationDelegate's -application:didReceiveRemoteNotification: method. -- (void)messaging:(nonnull FIRMessaging *)messaging - didReceiveMessage:(nonnull FIRMessagingRemoteMessage *)remoteMessage +- (void)messaging:(FIRMessaging *)messaging + didReceiveMessage:(FIRMessagingRemoteMessage *)remoteMessage NS_SWIFT_NAME(messaging(_:didReceive:)) __IOS_AVAILABLE(10.0); -/// The callback to handle data message received via FCM for devices running iOS 10 or above. -- (void)applicationReceivedRemoteMessage:(nonnull FIRMessagingRemoteMessage *)remoteMessage - NS_SWIFT_NAME(application(received:)) - __deprecated_msg("Use FIRMessagingDelegate’s -messaging:didReceiveMessage:"); - @end /** @@ -300,35 +286,24 @@ NS_SWIFT_NAME(Messaging) @property(nonatomic, weak, nullable) id<FIRMessagingDelegate> delegate; /** - * Delegate to handle remote data messages received via FCM for devices running iOS 10 or above. - */ -@property(nonatomic, weak, nullable) id<FIRMessagingDelegate> remoteMessageDelegate - __deprecated_msg("Use 'delegate' property"); - -/** * When set to `YES`, Firebase Messaging will automatically establish a socket-based, direct * channel to the FCM server. Enable this only if you are sending upstream messages or * receiving non-APNS, data-only messages in foregrounded apps. * Default is `NO`. */ -@property(nonatomic) BOOL shouldEstablishDirectChannel; - -/** - * Returns `YES` if the direct channel to the FCM server is active, and `NO` otherwise. - */ -@property(nonatomic, readonly) BOOL isDirectChannelEstablished; +@property(nonatomic, assign, getter=isDirectChannelEstablished) BOOL shouldEstablishDirectChannel; /** * FIRMessaging * * @return An instance of FIRMessaging. */ -+ (nonnull instancetype)messaging NS_SWIFT_NAME(messaging()); ++ (instancetype)messaging NS_SWIFT_NAME(messaging()); /** * Unavailable. Use +messaging instead. */ -- (nonnull instancetype)init __attribute__((unavailable("Use +messaging instead."))); +- (instancetype)init __attribute__((unavailable("Use +messaging instead."))); #pragma mark - APNS @@ -358,7 +333,7 @@ NS_SWIFT_NAME(Messaging) * FIRMessagingAPNSTokenTypeUnknown to have the type automatically * detected based on your provisioning profile. */ -- (void)setAPNSToken:(nonnull NSData *)apnsToken type:(FIRMessagingAPNSTokenType)type; +- (void)setAPNSToken:(NSData *)apnsToken type:(FIRMessagingAPNSTokenType)type; #pragma mark - FCM Tokens @@ -413,8 +388,8 @@ NS_SWIFT_NAME(Messaging) * @param senderID The Sender ID for a particular Firebase project. * @param completion The completion handler to handle the token request. */ -- (void)retrieveFCMTokenForSenderID:(nonnull NSString *)senderID - completion:(nonnull FIRMessagingFCMTokenFetchCompletion)completion +- (void)retrieveFCMTokenForSenderID:(NSString *)senderID + completion:(FIRMessagingFCMTokenFetchCompletion)completion NS_SWIFT_NAME(retrieveFCMToken(forSenderID:completion:)); @@ -425,8 +400,8 @@ NS_SWIFT_NAME(Messaging) * @param senderID The senderID for a particular Firebase project. * @param completion The completion handler to handle the token deletion. */ -- (void)deleteFCMTokenForSenderID:(nonnull NSString *)senderID - completion:(nonnull FIRMessagingDeleteFCMTokenCompletion)completion +- (void)deleteFCMTokenForSenderID:(NSString *)senderID + completion:(FIRMessagingDeleteFCMTokenCompletion)completion NS_SWIFT_NAME(deleteFCMToken(forSenderID:completion:)); @@ -444,7 +419,7 @@ NS_SWIFT_NAME(Messaging) * the same time, FIRMessaging performs exponential backoff to retry * establishing a connection and invoke the handler when successful. */ -- (void)connectWithCompletion:(nonnull FIRMessagingConnectCompletion)handler +- (void)connectWithCompletion:(FIRMessagingConnectCompletion)handler NS_SWIFT_NAME(connect(handler:)) __deprecated_msg("Please use the shouldEstablishDirectChannel property instead."); @@ -466,7 +441,7 @@ NS_SWIFT_NAME(Messaging) * * @param topic The name of the topic, for example, @"sports". */ -- (void)subscribeToTopic:(nonnull NSString *)topic NS_SWIFT_NAME(subscribe(toTopic:)); +- (void)subscribeToTopic:(NSString *)topic NS_SWIFT_NAME(subscribe(toTopic:)); /** * Asynchronously subscribe to the provided topic, retrying on failure. @@ -484,7 +459,7 @@ NS_SWIFT_NAME(Messaging) * * @param topic The name of the topic, for example @"sports". */ -- (void)unsubscribeFromTopic:(nonnull NSString *)topic NS_SWIFT_NAME(unsubscribe(fromTopic:)); +- (void)unsubscribeFromTopic:(NSString *)topic NS_SWIFT_NAME(unsubscribe(fromTopic:)); /** * Asynchronously unsubscribe from the provided topic, retrying on failure. @@ -522,9 +497,9 @@ NS_SWIFT_NAME(Messaging) * if the message has been dropped because of TTL; this can happen * on the server side, and it would require extra communication. */ -- (void)sendMessage:(nonnull NSDictionary *)message - to:(nonnull NSString *)receiver - withMessageID:(nonnull NSString *)messageID +- (void)sendMessage:(NSDictionary *)message + to:(NSString *)receiver + withMessageID:(NSString *)messageID timeToLive:(int64_t)ttl; #pragma mark - Analytics @@ -540,6 +515,8 @@ NS_SWIFT_NAME(Messaging) * * @return Information about the downstream message. */ -- (nonnull FIRMessagingMessageInfo *)appDidReceiveMessage:(nonnull NSDictionary *)message; +- (FIRMessagingMessageInfo *)appDidReceiveMessage:(NSDictionary *)message; @end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Storage/CHANGELOG.md b/Firebase/Storage/CHANGELOG.md index 5054cf2..135de40 100644 --- a/Firebase/Storage/CHANGELOG.md +++ b/Firebase/Storage/CHANGELOG.md @@ -1,5 +1,9 @@ +# v3.0.0 +- [removed] Removed `downloadURLs` property on `StorageMetadata`. Use `StorageReference.downloadURL(completion:)` to obtain a current download URL. +- [changed] The `maxOperationRetryTime` timeout now applies to calls to `StorageReference.getMetadata(completion:)` and `StorageReference.updateMetadata(completion:)`. These calls previously used the `maxDownloadRetryTime` and `maxUploadRetryTime` timeouts. + # v2.2.0 -- [changed] Deprecated `downloadURLs` property on `StorageMetadata`. Use `StorageReference.downloadURLWithCompletion()` to obtain a current download URL. +- [changed] Deprecated `downloadURLs` property on `StorageMetadata`. Use `StorageReference.downloadURL(completion:)` to obtain a current download URL. # v2.1.3 - [changed] Addresses CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF warnings that surface in newer versions of Xcode and CocoaPods. diff --git a/Firebase/Storage/FIRStorageErrors.m b/Firebase/Storage/FIRStorageErrors.m index ecfae02..651bfd1 100644 --- a/Firebase/Storage/FIRStorageErrors.m +++ b/Firebase/Storage/FIRStorageErrors.m @@ -170,4 +170,21 @@ return clientError; } ++ (NSError *)errorWithInvalidRequest:(NSData *)request { + NSString *requestString = [[NSString alloc] initWithData:request encoding:NSUTF8StringEncoding]; + NSString *invalidDataString = + [NSString stringWithFormat:kFIRStorageInvalidDataFormat, requestString]; + NSDictionary *dict; + if (invalidDataString.length > 0) { + dict = @{NSLocalizedFailureReasonErrorKey : invalidDataString}; + } + return [FIRStorageErrors errorWithCode:FIRStorageErrorCodeUnknown infoDictionary:dict]; +} + ++ (NSError *)errorWithCustomMessage:(NSString *)errorMessage { + return [NSError errorWithDomain:FIRStorageErrorDomain + code:FIRStorageErrorCodeUnknown + userInfo:@{NSLocalizedDescriptionKey : errorMessage}]; +} + @end diff --git a/Firebase/Storage/FIRStorageGetDownloadURLTask.m b/Firebase/Storage/FIRStorageGetDownloadURLTask.m new file mode 100644 index 0000000..02d202e --- /dev/null +++ b/Firebase/Storage/FIRStorageGetDownloadURLTask.m @@ -0,0 +1,117 @@ +// Copyright 2018 Google +// +// Licensed 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 "FIRStorageGetDownloadURLTask.h" + +#import "FIRStorageTask_Private.h" + +@implementation FIRStorageGetDownloadURLTask { + @private + FIRStorageVoidURLError _completion; +} + +@synthesize fetcher = _fetcher; +@synthesize fetcherCompletion = _fetcherCompletion; + +- (instancetype)initWithReference:(FIRStorageReference *)reference + fetcherService:(GTMSessionFetcherService *)service + completion:(FIRStorageVoidURLError)completion { + self = [super initWithReference:reference fetcherService:service]; + if (self) { + _completion = [completion copy]; + } + return self; +} + +- (void)dealloc { + [_fetcher stopFetching]; +} + ++ (NSURL *)downloadURLFromMetadataDictionary:(NSDictionary *)dictionary { + NSString *downloadTokens = dictionary[kFIRStorageMetadataDownloadTokens]; + + if (downloadTokens && downloadTokens.length > 0) { + NSArray<NSString *> *downloadTokenArray = [downloadTokens componentsSeparatedByString:@","]; + NSString *bucket = dictionary[kFIRStorageMetadataBucket]; + NSString *path = dictionary[kFIRStorageMetadataName]; + NSString *fullPath = [NSString stringWithFormat:kFIRStorageFullPathFormat, bucket, + [FIRStorageUtils GCSEscapedString:path]]; + + NSURLComponents *components = [[NSURLComponents alloc] init]; + components.scheme = kFIRStorageScheme; + components.host = kFIRStorageHost; + components.percentEncodedPath = fullPath; + + // The backend can return an arbitrary number of download tokens, but we only expose the first + // token via the download URL. + NSURLQueryItem *altItem = [[NSURLQueryItem alloc] initWithName:@"alt" value:@"media"]; + NSURLQueryItem *tokenItem = + [[NSURLQueryItem alloc] initWithName:@"token" value:downloadTokenArray[0]]; + components.queryItems = @[ altItem, tokenItem ]; + + return [components URL]; + } + + return nil; +} + +- (void)enqueue { + NSMutableURLRequest *request = [self.baseRequest mutableCopy]; + request.HTTPMethod = @"GET"; + request.timeoutInterval = self.reference.storage.maxOperationRetryTime; + + FIRStorageVoidURLError callback = _completion; + _completion = nil; + + GTMSessionFetcher *fetcher = [self.fetcherService fetcherWithRequest:request]; + _fetcher = fetcher; + fetcher.comment = @"GetDownloadURLTask"; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-retain-cycles" + _fetcherCompletion = ^(NSData *data, NSError *error) { + NSURL *downloadURL; + if (error) { + if (!self.error) { + self.error = [FIRStorageErrors errorWithServerError:error reference:self.reference]; + } + } else { + NSDictionary *responseDictionary = [NSDictionary frs_dictionaryFromJSONData:data]; + if (responseDictionary != nil) { + downloadURL = + [FIRStorageGetDownloadURLTask downloadURLFromMetadataDictionary:responseDictionary]; + if (!downloadURL) { + self.error = + [FIRStorageErrors errorWithCustomMessage:@"Failed to retrieve a download URL."]; + } + } else { + self.error = [FIRStorageErrors errorWithInvalidRequest:data]; + } + } + + if (callback) { + callback(downloadURL, self.error); + } + + self->_fetcherCompletion = nil; + }; +#pragma clang diagnostic pop + + __weak FIRStorageGetDownloadURLTask *weakSelf = self; + [fetcher beginFetchWithCompletionHandler:^(NSData *data, NSError *error) { + weakSelf.fetcherCompletion(data, error); + }]; +} + +@end diff --git a/Firebase/Storage/FIRStorageGetMetadataTask.m b/Firebase/Storage/FIRStorageGetMetadataTask.m index 2623652..752c410 100644 --- a/Firebase/Storage/FIRStorageGetMetadataTask.m +++ b/Firebase/Storage/FIRStorageGetMetadataTask.m @@ -46,7 +46,7 @@ - (void)enqueue { NSMutableURLRequest *request = [self.baseRequest mutableCopy]; request.HTTPMethod = @"GET"; - request.timeoutInterval = self.reference.storage.maxDownloadRetryTime; + request.timeoutInterval = self.reference.storage.maxOperationRetryTime; FIRStorageVoidMetadataError callback = _completion; _completion = nil; @@ -58,37 +58,23 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-retain-cycles" _fetcherCompletion = ^(NSData *data, NSError *error) { + FIRStorageMetadata *metadata; if (error) { if (!self.error) { self.error = [FIRStorageErrors errorWithServerError:error reference:self.reference]; } - if (callback) { - callback(nil, self.error); + } else { + NSDictionary *responseDictionary = [NSDictionary frs_dictionaryFromJSONData:data]; + if (responseDictionary != nil) { + metadata = [[FIRStorageMetadata alloc] initWithDictionary:responseDictionary]; + [metadata setType:FIRStorageMetadataTypeFile]; + } else { + self.error = [FIRStorageErrors errorWithInvalidRequest:data]; } - self->_fetcherCompletion = nil; - return; } - NSDictionary *responseDictionary = [NSDictionary frs_dictionaryFromJSONData:data]; - if (responseDictionary != nil) { - FIRStorageMetadata *metadata = - [[FIRStorageMetadata alloc] initWithDictionary:responseDictionary]; - [metadata setType:FIRStorageMetadataTypeFile]; - if (callback) { - callback(metadata, nil); - } - } else { - NSString *returnedData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - NSString *invalidDataString = - [NSString stringWithFormat:kFIRStorageInvalidDataFormat, returnedData]; - NSDictionary *dict; - if (invalidDataString.length > 0) { - dict = @{NSLocalizedFailureReasonErrorKey : invalidDataString}; - } - self.error = [FIRStorageErrors errorWithCode:FIRStorageErrorCodeUnknown infoDictionary:dict]; - if (callback) { - callback(nil, self.error); - } + if (callback) { + callback(metadata, self.error); } self->_fetcherCompletion = nil; }; diff --git a/Firebase/Storage/FIRStorageMetadata.m b/Firebase/Storage/FIRStorageMetadata.m index ab25076..764907c 100644 --- a/Firebase/Storage/FIRStorageMetadata.m +++ b/Firebase/Storage/FIRStorageMetadata.m @@ -41,7 +41,6 @@ _contentType = dictionary[kFIRStorageMetadataContentType]; _customMetadata = dictionary[kFIRStorageMetadataCustomMetadata]; _size = [dictionary[kFIRStorageMetadataSize] longLongValue]; - _downloadURLs = dictionary[kFIRStorageMetadataDownloadURLs]; _generation = [dictionary[kFIRStorageMetadataGeneration] longLongValue]; _metageneration = [dictionary[kFIRStorageMetadataMetageneration] longLongValue]; _timeCreated = [self dateFromRFC3339String:dictionary[kFIRStorageMetadataTimeCreated]]; @@ -50,26 +49,6 @@ // GCS "name" is our path, our "name" is just the last path component of the path _path = dictionary[kFIRStorageMetadataName]; _name = [_path lastPathComponent]; - NSString *downloadTokens = dictionary[kFIRStorageMetadataDownloadTokens]; - if (downloadTokens) { - NSArray<NSString *> *downloadStringArray = [downloadTokens componentsSeparatedByString:@","]; - NSMutableArray<NSURL *> *downloadURLArray = - [[NSMutableArray alloc] initWithCapacity:[downloadStringArray count]]; - [downloadStringArray enumerateObjectsUsingBlock:^(NSString *_Nonnull token, NSUInteger idx, - BOOL *_Nonnull stop) { - NSURLComponents *components = [[NSURLComponents alloc] init]; - components.scheme = kFIRStorageScheme; - components.host = kFIRStorageHost; - NSString *path = [FIRStorageUtils GCSEscapedString:self->_path]; - NSString *fullPath = - [NSString stringWithFormat:kFIRStorageFullPathFormat, self->_bucket, path]; - components.percentEncodedPath = fullPath; - components.query = [NSString stringWithFormat:@"alt=media&token=%@", token]; - - [downloadURLArray insertObject:[components URL] atIndex:idx]; - }]; - _downloadURLs = downloadURLArray; - } } return self; } @@ -147,29 +126,6 @@ metadataDictionary[kFIRStorageMetadataCustomMetadata] = _customMetadata; } - if (_downloadURLs) { - NSMutableArray *downloadTokens = [[NSMutableArray alloc] init]; - [_downloadURLs - enumerateObjectsUsingBlock:^(NSURL *_Nonnull URL, NSUInteger idx, BOOL *_Nonnull stop) { - NSArray *queryItems = [URL.query componentsSeparatedByString:@"&"]; - [queryItems enumerateObjectsUsingBlock:^(NSString *queryString, NSUInteger idx, - BOOL *_Nonnull stop) { - NSString *key; - NSString *value; - NSScanner *scanner = [NSScanner scannerWithString:queryString]; - [scanner scanUpToString:@"=" intoString:&key]; - [scanner scanString:@"=" intoString:NULL]; - [scanner scanUpToString:@"\n" intoString:&value]; - if ([key isEqual:@"token"]) { - [downloadTokens addObject:value]; - *stop = YES; - } - }]; - }]; - NSString *downloadTokenString = [downloadTokens componentsJoinedByString:@","]; - metadataDictionary[kFIRStorageMetadataDownloadTokens] = downloadTokenString; - } - if (_generation) { NSString *generationString = [NSString stringWithFormat:@"%lld", _generation]; metadataDictionary[kFIRStorageMetadataGeneration] = generationString; @@ -207,10 +163,6 @@ return _type == FIRStorageMetadataTypeFolder; } -- (nullable NSURL *)downloadURL { - return [_downloadURLs firstObject]; -} - #pragma mark - Private methods + (void)removeMatchingMetadata:(NSMutableDictionary *)metadata diff --git a/Firebase/Storage/FIRStorageReference.m b/Firebase/Storage/FIRStorageReference.m index 7bc1934..5b70a9c 100644 --- a/Firebase/Storage/FIRStorageReference.m +++ b/Firebase/Storage/FIRStorageReference.m @@ -17,6 +17,7 @@ #import "FIRStorageConstants_Private.h" #import "FIRStorageDeleteTask.h" #import "FIRStorageDownloadTask_Private.h" +#import "FIRStorageGetDownloadURLTask.h" #import "FIRStorageGetMetadataTask.h" #import "FIRStorageMetadata_Private.h" #import "FIRStorageReference_Private.h" @@ -319,16 +320,11 @@ } - (void)downloadURLWithCompletion:(FIRStorageVoidURLError)completion { - dispatch_queue_t callbackQueue = _storage.fetcherServiceForApp.callbackQueue; - if (!callbackQueue) { - callbackQueue = dispatch_get_main_queue(); - } - - return [self metadataWithCompletion:^(FIRStorageMetadata *metadata, NSError *error) { - dispatch_async(callbackQueue, ^{ - completion(metadata.downloadURL, error); - }); - }]; + FIRStorageGetDownloadURLTask *task = + [[FIRStorageGetDownloadURLTask alloc] initWithReference:self + fetcherService:_storage.fetcherServiceForApp + completion:completion]; + [task enqueue]; } #pragma mark - Metadata Operations diff --git a/Firebase/Storage/FIRStorageUpdateMetadataTask.m b/Firebase/Storage/FIRStorageUpdateMetadataTask.m index fa5955a..cea4e7d 100644 --- a/Firebase/Storage/FIRStorageUpdateMetadataTask.m +++ b/Firebase/Storage/FIRStorageUpdateMetadataTask.m @@ -48,7 +48,7 @@ NSDictionary *updateDictionary = [_updateMetadata updatedMetadata]; NSData *updateData = [NSData frs_dataFromJSONDictionary:updateDictionary]; request.HTTPMethod = @"PATCH"; - request.timeoutInterval = self.reference.storage.maxUploadRetryTime; + request.timeoutInterval = self.reference.storage.maxOperationRetryTime; request.HTTPBody = updateData; NSString *typeString = @"application/json; charset=UTF-8"; [request setValue:typeString forHTTPHeaderField:@"Content-Type"]; @@ -64,37 +64,23 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-retain-cycles" _fetcherCompletion = ^(NSData *data, NSError *error) { + FIRStorageMetadata *metadata; if (error) { if (!self.error) { self.error = [FIRStorageErrors errorWithServerError:error reference:self.reference]; } - if (callback) { - callback(nil, self.error); + } else { + NSDictionary *responseDictionary = [NSDictionary frs_dictionaryFromJSONData:data]; + if (responseDictionary) { + metadata = [[FIRStorageMetadata alloc] initWithDictionary:responseDictionary]; + [metadata setType:FIRStorageMetadataTypeFile]; + } else { + self.error = [FIRStorageErrors errorWithInvalidRequest:data]; } - self->_fetcherCompletion = nil; - return; } - NSDictionary *responseDictionary = [NSDictionary frs_dictionaryFromJSONData:data]; - if (responseDictionary) { - FIRStorageMetadata *metadata = - [[FIRStorageMetadata alloc] initWithDictionary:responseDictionary]; - [metadata setType:FIRStorageMetadataTypeFile]; - if (callback) { - callback(metadata, nil); - } - } else { - NSString *returnedData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - NSString *invalidDataString = - [NSString stringWithFormat:kFIRStorageInvalidDataFormat, returnedData]; - NSDictionary *dict; - if (invalidDataString.length > 0) { - dict = @{NSLocalizedFailureReasonErrorKey : invalidDataString}; - } - self.error = [FIRStorageErrors errorWithCode:FIRStorageErrorCodeUnknown infoDictionary:dict]; - if (callback) { - callback(nil, self.error); - } + if (callback) { + callback(metadata, self.error); } self->_fetcherCompletion = nil; }; diff --git a/Firebase/Storage/FIRStorageUploadTask.m b/Firebase/Storage/FIRStorageUploadTask.m index f84c2c7..2c4daa9 100644 --- a/Firebase/Storage/FIRStorageUploadTask.m +++ b/Firebase/Storage/FIRStorageUploadTask.m @@ -154,14 +154,7 @@ [metadata setType:FIRStorageMetadataTypeFile]; self.metadata = metadata; } else { - NSString *returnedData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - NSString *invalidDataString = - [NSString stringWithFormat:kFIRStorageInvalidDataFormat, returnedData]; - NSDictionary *dict; - if (invalidDataString.length > 0) { - dict = @{NSLocalizedFailureReasonErrorKey : invalidDataString}; - } - self.error = [FIRStorageErrors errorWithCode:FIRStorageErrorCodeUnknown infoDictionary:dict]; + self.error = [FIRStorageErrors errorWithInvalidRequest:data]; } [self fireHandlersForStatus:FIRStorageTaskStatusSuccess snapshot:self.snapshot]; diff --git a/Firebase/Storage/Private/FIRStorageConstants_Private.h b/Firebase/Storage/Private/FIRStorageConstants_Private.h index 498c687..cf12337 100644 --- a/Firebase/Storage/Private/FIRStorageConstants_Private.h +++ b/Firebase/Storage/Private/FIRStorageConstants_Private.h @@ -55,7 +55,6 @@ FOUNDATION_EXPORT NSString *const kFIRStorageMetadataContentLanguage; FOUNDATION_EXPORT NSString *const kFIRStorageMetadataContentType; FOUNDATION_EXPORT NSString *const kFIRStorageMetadataCustomMetadata; FOUNDATION_EXPORT NSString *const kFIRStorageMetadataSize; -FOUNDATION_EXPORT NSString *const kFIRStorageMetadataDownloadURLs; FOUNDATION_EXPORT NSString *const kFIRStorageMetadataGeneration; FOUNDATION_EXPORT NSString *const kFIRStorageMetadataMetageneration; FOUNDATION_EXPORT NSString *const kFIRStorageMetadataTimeCreated; diff --git a/Firebase/Storage/Private/FIRStorageErrors.h b/Firebase/Storage/Private/FIRStorageErrors.h index 7c236d9..a76a6aa 100644 --- a/Firebase/Storage/Private/FIRStorageErrors.h +++ b/Firebase/Storage/Private/FIRStorageErrors.h @@ -44,11 +44,27 @@ NS_ASSUME_NONNULL_BEGIN * Creates a Firebase Storage error from a specific GCS error and FIRStorageReference. * @param error Server error to wrap and return as a Firebase Storage error. * @param reference FIRStorageReference which provides context about the request being made. - * @return Returns an Firebase Storage error, or nil if no error is provided. + * @return Returns a Firebase Storage error, or nil if no error is provided. */ + (nullable NSError *)errorWithServerError:(nullable NSError *)error reference:(nullable FIRStorageReference *)reference; +/** + * Creates a Firebase Storage error from an invalid request. + * + * @param request The NSData representation of the invalid user request. + * @return Returns the corresponding Firebase Storage error. + */ ++ (NSError *)errorWithInvalidRequest:(NSData *)request; + +/** + * Creates a Firebase Storage error with a custom error message. + * + * @param errorMessage A custom error message. + * @return Returns the corresponding Firebase Storage error. + */ ++ (NSError *)errorWithCustomMessage:(NSString *)errorMessage; + @end NS_ASSUME_NONNULL_END diff --git a/Firebase/Storage/Private/FIRStorageGetDownloadURLTask.h b/Firebase/Storage/Private/FIRStorageGetDownloadURLTask.h new file mode 100644 index 0000000..8cd9eb3 --- /dev/null +++ b/Firebase/Storage/Private/FIRStorageGetDownloadURLTask.h @@ -0,0 +1,34 @@ +/* + * Copyright 2018 Google + * + * Licensed 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 "FIRStorageTask.h" + +@class GTMSessionFetcherService; + +NS_ASSUME_NONNULL_BEGIN + +/** + * Task which provides the ability to get a download URL for an object in Firebase Storage. + */ +@interface FIRStorageGetDownloadURLTask : FIRStorageTask <FIRStorageTaskManagement> + +- (instancetype)initWithReference:(FIRStorageReference *)reference + fetcherService:(GTMSessionFetcherService *)service + completion:(FIRStorageVoidURLError)completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Storage/Private/FIRStorageGetDownloadURLTask_Private.h b/Firebase/Storage/Private/FIRStorageGetDownloadURLTask_Private.h new file mode 100644 index 0000000..0da04a4 --- /dev/null +++ b/Firebase/Storage/Private/FIRStorageGetDownloadURLTask_Private.h @@ -0,0 +1,31 @@ +/* + * Copyright 2018 Google + * + * Licensed 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 "FIRStorageGetDownloadURLTask.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Task which provides the ability to get a download URL for an object in Firebase Storage. + */ +@interface FIRStorageGetDownloadURLTask () + +/** Extracts a download URL from the StorageMetadata dictonary representation. */ ++ (nullable NSURL *)downloadURLFromMetadataDictionary:(NSDictionary *)dictionary; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Storage/Public/FIRStorage.h b/Firebase/Storage/Public/FIRStorage.h index f70b875..baa8122 100644 --- a/Firebase/Storage/Public/FIRStorage.h +++ b/Firebase/Storage/Public/FIRStorage.h @@ -24,7 +24,8 @@ NS_ASSUME_NONNULL_BEGIN /** Project version string for FirebaseStorage. */ -FOUNDATION_EXPORT const unsigned char *const FIRStorageVersionString; +FOUNDATION_EXPORT const unsigned char *const FIRStorageVersionString + NS_SWIFT_NAME(StorageVersionString); /** * FirebaseStorage is a service that supports uploading and downloading binary objects, diff --git a/Firebase/Storage/Public/FIRStorageMetadata.h b/Firebase/Storage/Public/FIRStorageMetadata.h index 2e3cc13..63b8798 100644 --- a/Firebase/Storage/Public/FIRStorageMetadata.h +++ b/Firebase/Storage/Public/FIRStorageMetadata.h @@ -113,12 +113,6 @@ NS_SWIFT_NAME(StorageMetadata) @property(strong, nonatomic, readonly, nullable) FIRStorageReference *storageReference; /** - * An array containing all download URLs available for the object. - */ -@property(strong, nonatomic, readonly, nullable) NSArray<NSURL *> *downloadURLs __deprecated_msg( - "Use `StorageReference.downloadURLWithCompletion()` to obtain a current download URL."); - -/** * Creates an instanece of FIRStorageMetadata from the contents of a dictionary. * @return An instance of FIRStorageMetadata that represents the contents of a dictionary. */ @@ -141,14 +135,6 @@ NS_SWIFT_NAME(StorageMetadata) */ @property(readonly, getter=isFolder) BOOL folder; -/** - * Retrieves a download URL for the given object, or nil if none exist. - * Note that if there are many valid download tokens, this will always return the first - * valid token created. - */ -- (nullable NSURL *)downloadURL __deprecated_msg( - "Use `StorageReference.downloadURLWithCompletion()` to obtain a current download URL."); - @end NS_ASSUME_NONNULL_END diff --git a/FirebaseCore.podspec b/FirebaseCore.podspec index c1bf223..d25a979 100644 --- a/FirebaseCore.podspec +++ b/FirebaseCore.podspec @@ -26,7 +26,7 @@ Firebase Core includes FIRApp and FIROptions which provide central configuration s.source_files = 'Firebase/Core/**/*.[mh]' s.public_header_files = 'Firebase/Core/Public/*.h', 'Firebase/Core/Private/*.h' - s.private_header_files = 'Firebase/Core/Private/*.h' + s.private_header_files = 'Firebase/Core/Private/*.h', 'Firebase/Core/third_party/*.h' s.frameworks = [ 'Foundation', 'SystemConfiguration' diff --git a/Firestore/CMakeLists.txt b/Firestore/CMakeLists.txt index d605411..80308bb 100644 --- a/Firestore/CMakeLists.txt +++ b/Firestore/CMakeLists.txt @@ -15,6 +15,48 @@ cmake_minimum_required(VERSION 2.8.11) project(firestore C CXX) +option(WITH_ASAN "Build with Address Sanitizer" OFF) +# TODO(varconst): msan +# Memory sanitizer is more complicated: +# - it requires all dependencies to be compiled with msan enabled (see +# https://github.com/google/sanitizers/wiki/MemorySanitizerLibcxxHowTo); +# - AppleClang doesn't support it. +option(WITH_TSAN "Build with Thread Sanitizer (mutually exculsive with other sanitizers)" OFF) +option(WITH_UBSAN "Build with Undefined Behavior sanitizer" OFF) + +macro(add_to_compile_and_link_flags flag) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${flag}") +endmacro() + +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if(WITH_ASAN) + add_to_compile_and_link_flags("-fsanitize=address") + endif() + + if(WITH_TSAN) + if(WITH_ASAN OR WITH_UBSAN) + message(FATAL_ERROR "Cannot combine TSan with other santizers") + endif() + add_to_compile_and_link_flags("-fsanitize=thread") + endif() + + if(WITH_UBSAN) + add_to_compile_and_link_flags("-fsanitize=undefined") + endif() + + if (WITH_ASAN OR WITH_TSAN OR WITH_UBSAN) + # Recommended to "get nicer stack traces in error messages" + # TODO(varconst): double-check that TSan actually needs this flag (it's + # explicitly recommended in the docs for ASan and UBSan) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer") + endif() +else() + if(WITH_ASAN OR WITH_TSAN OR WITH_UBSAN) + message(FATAL_ERROR "Only Clang and GCC support sanitizers") + endif() +endif() + set(FIREBASE_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/..) # CMAKE_INSTALL_PREFIX should be passed in to this build so that it can find diff --git a/Firestore/Example/SwiftBuildTest/main.swift b/Firestore/Example/SwiftBuildTest/main.swift index 3087085..691adf6 100644 --- a/Firestore/Example/SwiftBuildTest/main.swift +++ b/Firestore/Example/SwiftBuildTest/main.swift @@ -100,6 +100,10 @@ func writeDocument(at docRef: DocumentReference) { let updateData = [ "bar.baz": 42, FieldPath(["foobar"]): 42, + "server_timestamp": FieldValue.serverTimestamp(), + "array_union": FieldValue.arrayUnion(["a", "b"]), + "array_remove": FieldValue.arrayRemove(["a", "b"]), + "field_delete": FieldValue.delete(), ] as [AnyHashable: Any] docRef.setData(setData) diff --git a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm index bc253d0..0454152 100644 --- a/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm +++ b/Firestore/Example/Tests/Core/FSTQueryListenerTests.mm @@ -14,10 +14,9 @@ * limitations under the License. */ -#import "Firestore/Source/Core/FSTEventManager.h" - #import <XCTest/XCTest.h> +#import "Firestore/Source/Core/FSTEventManager.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTView.h" #import "Firestore/Source/Model/FSTDocument.h" diff --git a/Firestore/Example/Tests/Core/FSTSyncEngine+Testing.h b/Firestore/Example/Tests/Core/FSTSyncEngine+Testing.h index 6e7c45b..5d5c981 100644 --- a/Firestore/Example/Tests/Core/FSTSyncEngine+Testing.h +++ b/Firestore/Example/Tests/Core/FSTSyncEngine+Testing.h @@ -16,6 +16,8 @@ #import <Foundation/Foundation.h> +#include <map> + #import "Firestore/Source/Core/FSTSyncEngine.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" diff --git a/Firestore/Example/Tests/GoogleTest/FSTGoogleTestTests.mm b/Firestore/Example/Tests/GoogleTest/FSTGoogleTestTests.mm index 18dfaf3..ec14880 100644 --- a/Firestore/Example/Tests/GoogleTest/FSTGoogleTestTests.mm +++ b/Firestore/Example/Tests/GoogleTest/FSTGoogleTestTests.mm @@ -17,9 +17,9 @@ #import <XCTest/XCTest.h> #import <objc/runtime.h> -#include <gtest/gtest.h> +#import "Firestore/Source/Util/FSTAssert.h" -#include "Firestore/Source/Util/FSTAssert.h" +#include "gtest/gtest.h" /** * An XCTest test case that finds C++ test cases written in the GoogleTest framework, runs them, and diff --git a/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm index 0ad30fd..82c57c3 100644 --- a/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm @@ -282,7 +282,7 @@ [self expectSet:@{@"foo" : [FIRFieldValue fieldValueForDelete]} toFailWithReason: @"FieldValue.delete() can only be used with updateData() and setData() with " - @"SetOptions.merge()."]; + @"SetOptions.merge() (found in field foo)"]; } - (void)testUpdatesWithNestedFieldValueDeleteFail { diff --git a/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm b/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm index 430366f..ad911ce 100644 --- a/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm +++ b/Firestore/Example/Tests/Integration/FSTDatastoreTests.mm @@ -42,12 +42,14 @@ #include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/model/precondition.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; using firebase::firestore::auth::EmptyCredentialsProvider; using firebase::firestore::core::DatabaseInfo; using firebase::firestore::model::DatabaseId; +using firebase::firestore::model::Precondition; using firebase::firestore::model::TargetId; NS_ASSUME_NONNULL_BEGIN @@ -238,7 +240,7 @@ NS_ASSUME_NONNULL_BEGIN initWithKey:[FSTDocumentKey keyWithPathString:@"rooms/eros"] value:[[FSTObjectValue alloc] initWithDictionary:@{@"name" : [FSTStringValue stringValue:@"Eros"]}] - precondition:[FSTPrecondition none]]; + precondition:Precondition::None()]; } @end diff --git a/Firestore/Example/Tests/Local/FSTEagerGarbageCollectorTests.mm b/Firestore/Example/Tests/Local/FSTEagerGarbageCollectorTests.mm index 2cf530c..615b3f6 100644 --- a/Firestore/Example/Tests/Local/FSTEagerGarbageCollectorTests.mm +++ b/Firestore/Example/Tests/Local/FSTEagerGarbageCollectorTests.mm @@ -16,10 +16,10 @@ #import "Firestore/Source/Local/FSTEagerGarbageCollector.h" -#include <set> - #import <XCTest/XCTest.h> +#include <set> + #import "Firestore/Source/Local/FSTReferenceSet.h" #import "Firestore/Source/Model/FSTDocumentKey.h" diff --git a/Firestore/Example/Tests/Local/FSTLevelDBKeyTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBKeyTests.mm index f23b7c5..c4b7e29 100644 --- a/Firestore/Example/Tests/Local/FSTLevelDBKeyTests.mm +++ b/Firestore/Example/Tests/Local/FSTLevelDBKeyTests.mm @@ -18,6 +18,8 @@ #import <XCTest/XCTest.h> +#include <string> + #import "Firestore/Example/Tests/Util/FSTHelpers.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" diff --git a/Firestore/Example/Tests/Local/FSTLevelDBLocalStoreTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBLocalStoreTests.mm index 97e3c5b..29caa84 100644 --- a/Firestore/Example/Tests/Local/FSTLevelDBLocalStoreTests.mm +++ b/Firestore/Example/Tests/Local/FSTLevelDBLocalStoreTests.mm @@ -14,13 +14,10 @@ * limitations under the License. */ -#import "Firestore/Source/Local/FSTLocalStore.h" - -#import <XCTest/XCTest.h> +#import "Firestore/Example/Tests/Local/FSTLocalStoreTests.h" #import "Firestore/Source/Local/FSTLevelDB.h" -#import "Firestore/Example/Tests/Local/FSTLocalStoreTests.h" #import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h" NS_ASSUME_NONNULL_BEGIN diff --git a/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm index 14910ce..3da8083 100644 --- a/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm +++ b/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm @@ -16,11 +16,15 @@ #import <XCTest/XCTest.h> +#include <memory> +#include <string> + #import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h" #import "Firestore/Source/Local/FSTLevelDB.h" #import "Firestore/Source/Local/FSTLevelDBKey.h" #import "Firestore/Source/Local/FSTLevelDBMigrations.h" #import "Firestore/Source/Local/FSTLevelDBQueryCache.h" + #include "Firestore/core/src/firebase/firestore/util/ordered_code.h" #include "leveldb/db.h" diff --git a/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm index cd8176e..60d9800 100644 --- a/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm +++ b/Firestore/Example/Tests/Local/FSTLevelDBMutationQueueTests.mm @@ -17,7 +17,8 @@ #import "Firestore/Source/Local/FSTLevelDBMutationQueue.h" #import <XCTest/XCTest.h> -#include <leveldb/db.h> + +#include <string> #import "Firestore/Protos/objc/firestore/local/Mutation.pbobjc.h" #import "Firestore/Source/Local/FSTLevelDB.h" @@ -28,6 +29,7 @@ #include "Firestore/core/src/firebase/firestore/auth/user.h" #include "Firestore/core/src/firebase/firestore/util/ordered_code.h" +#include "leveldb/db.h" NS_ASSUME_NONNULL_BEGIN diff --git a/Firestore/Example/Tests/Local/FSTLevelDBRemoteDocumentCacheTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBRemoteDocumentCacheTests.mm index d69e613..7baf1ef 100644 --- a/Firestore/Example/Tests/Local/FSTLevelDBRemoteDocumentCacheTests.mm +++ b/Firestore/Example/Tests/Local/FSTLevelDBRemoteDocumentCacheTests.mm @@ -14,16 +14,15 @@ * limitations under the License. */ -#import "Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.h" - -#include <leveldb/db.h> +#include <string> +#import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h" +#import "Firestore/Example/Tests/Local/FSTRemoteDocumentCacheTests.h" #import "Firestore/Source/Local/FSTLevelDB.h" #import "Firestore/Source/Local/FSTLevelDBKey.h" -#import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h" - #include "Firestore/core/src/firebase/firestore/util/ordered_code.h" +#include "leveldb/db.h" NS_ASSUME_NONNULL_BEGIN diff --git a/Firestore/Example/Tests/Local/FSTLevelDBTransactionTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBTransactionTests.mm index a32ce9f..29f5d6c 100644 --- a/Firestore/Example/Tests/Local/FSTLevelDBTransactionTests.mm +++ b/Firestore/Example/Tests/Local/FSTLevelDBTransactionTests.mm @@ -14,15 +14,23 @@ * limitations under the License. */ +#import <XCTest/XCTest.h> + +#include <memory> +#include <string> + +// This is out of order to satisfy the linter, which doesn't realize this is +// the header corresponding to this test. +// TODO(wilhuff): move this to the top once the test filename matches #include "Firestore/core/src/firebase/firestore/local/leveldb_transaction.h" -#import <XCTest/XCTest.h> -#include <absl/strings/string_view.h> -#include <leveldb/db.h> #import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h" #import "Firestore/Protos/objc/firestore/local/Mutation.pbobjc.h" #import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h" + #include "Firestore/core/src/firebase/firestore/local/leveldb_key.h" +#include "absl/strings/string_view.h" +#include "leveldb/db.h" NS_ASSUME_NONNULL_BEGIN diff --git a/Firestore/Example/Tests/Local/FSTLocalSerializerTests.mm b/Firestore/Example/Tests/Local/FSTLocalSerializerTests.mm index d94925d..362f46f 100644 --- a/Firestore/Example/Tests/Local/FSTLocalSerializerTests.mm +++ b/Firestore/Example/Tests/Local/FSTLocalSerializerTests.mm @@ -41,11 +41,15 @@ #import "Firestore/Example/Tests/Util/FSTHelpers.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/model/field_mask.h" +#include "Firestore/core/src/firebase/firestore/model/precondition.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" #include "Firestore/core/test/firebase/firestore/testutil/testutil.h" namespace testutil = firebase::firestore::testutil; using firebase::firestore::model::DatabaseId; +using firebase::firestore::model::FieldMask; +using firebase::firestore::model::Precondition; NS_ASSUME_NONNULL_BEGIN @@ -76,13 +80,12 @@ NS_ASSUME_NONNULL_BEGIN - (void)testEncodesMutationBatch { FSTMutation *set = FSTTestSetMutation(@"foo/bar", @{ @"a" : @"b", @"num" : @1 }); - FSTMutation *patch = [[FSTPatchMutation alloc] - initWithKey:FSTTestDocKey(@"bar/baz") - fieldMask:[[FSTFieldMask alloc] initWithFields:{testutil::Field("a")}] - value:FSTTestObjectValue( - @{ @"a" : @"b", - @"num" : @1 }) - precondition:[FSTPrecondition preconditionWithExists:YES]]; + FSTMutation *patch = [[FSTPatchMutation alloc] initWithKey:FSTTestDocKey(@"bar/baz") + fieldMask:FieldMask{testutil::Field("a")} + value:FSTTestObjectValue( + @{ @"a" : @"b", + @"num" : @1 }) + precondition:Precondition::Exists(true)]; FSTMutation *del = FSTTestDeleteMutation(@"baz/quux"); FIRTimestamp *writeTime = [FIRTimestamp timestamp]; FSTMutationBatch *model = [[FSTMutationBatch alloc] initWithBatchID:42 diff --git a/Firestore/Example/Tests/Local/FSTLocalStoreTests.h b/Firestore/Example/Tests/Local/FSTLocalStoreTests.h index 8e06d82..e514954 100644 --- a/Firestore/Example/Tests/Local/FSTLocalStoreTests.h +++ b/Firestore/Example/Tests/Local/FSTLocalStoreTests.h @@ -16,7 +16,9 @@ #import <XCTest/XCTest.h> -@class FSTLocalStore; +#import "Firestore/Source/Local/FSTLocalStore.h" + +@protocol FSTPersistence; NS_ASSUME_NONNULL_BEGIN diff --git a/Firestore/Example/Tests/Local/FSTMemoryLocalStoreTests.mm b/Firestore/Example/Tests/Local/FSTMemoryLocalStoreTests.mm index b78239e..7708eed 100644 --- a/Firestore/Example/Tests/Local/FSTMemoryLocalStoreTests.mm +++ b/Firestore/Example/Tests/Local/FSTMemoryLocalStoreTests.mm @@ -14,13 +14,10 @@ * limitations under the License. */ -#import "Firestore/Source/Local/FSTLocalStore.h" - -#import <XCTest/XCTest.h> +#import "Firestore/Example/Tests/Local/FSTLocalStoreTests.h" #import "Firestore/Source/Local/FSTMemoryPersistence.h" -#import "Firestore/Example/Tests/Local/FSTLocalStoreTests.h" #import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h" NS_ASSUME_NONNULL_BEGIN diff --git a/Firestore/Example/Tests/Local/FSTMutationQueueTests.mm b/Firestore/Example/Tests/Local/FSTMutationQueueTests.mm index b228657..5b5bdd1 100644 --- a/Firestore/Example/Tests/Local/FSTMutationQueueTests.mm +++ b/Firestore/Example/Tests/Local/FSTMutationQueueTests.mm @@ -17,6 +17,9 @@ #import "Firestore/Example/Tests/Local/FSTMutationQueueTests.h" #import <FirebaseFirestore/FIRTimestamp.h> + +#include <set> + #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Local/FSTEagerGarbageCollector.h" #import "Firestore/Source/Local/FSTMutationQueue.h" @@ -246,7 +249,6 @@ NS_ASSUME_NONNULL_BEGIN return newBatches; }); self.persistence.run("testNextMutationBatchAfterBatchIDSkipsAcknowledgedBatches", [&]() { - [self.mutationQueue acknowledgeBatch:batches[0] streamToken:nil]; XCTAssertEqualObjects([self.mutationQueue nextMutationBatchAfterBatchID:kFSTBatchIDUnknown], batches[1]); diff --git a/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm b/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm index 956afa9..429a83a 100644 --- a/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm +++ b/Firestore/Example/Tests/Local/FSTQueryCacheTests.mm @@ -16,6 +16,8 @@ #import "Firestore/Example/Tests/Local/FSTQueryCacheTests.h" +#include <set> + #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" #import "Firestore/Source/Local/FSTEagerGarbageCollector.h" diff --git a/Firestore/Example/Tests/Local/StringViewTests.mm b/Firestore/Example/Tests/Local/StringViewTests.mm index b30b43d..7c86924 100644 --- a/Firestore/Example/Tests/Local/StringViewTests.mm +++ b/Firestore/Example/Tests/Local/StringViewTests.mm @@ -17,7 +17,8 @@ #import "Firestore/Source/Local/StringView.h" #import <XCTest/XCTest.h> -#include <leveldb/slice.h> + +#include "leveldb/slice.h" using Firestore::StringView; diff --git a/Firestore/Example/Tests/Model/FSTFieldValueTests.mm b/Firestore/Example/Tests/Model/FSTFieldValueTests.mm index 98504b5..d16a01d 100644 --- a/Firestore/Example/Tests/Model/FSTFieldValueTests.mm +++ b/Firestore/Example/Tests/Model/FSTFieldValueTests.mm @@ -22,7 +22,6 @@ #import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/API/FSTUserDataConverter.h" -#import "Firestore/Source/Model/FSTFieldValue.h" #import "Firestore/Example/Tests/API/FSTAPIHelpers.h" #import "Firestore/Example/Tests/Util/FSTHelpers.h" diff --git a/Firestore/Example/Tests/Model/FSTMutationTests.mm b/Firestore/Example/Tests/Model/FSTMutationTests.mm index 1f9193e..56bf1c2 100644 --- a/Firestore/Example/Tests/Model/FSTMutationTests.mm +++ b/Firestore/Example/Tests/Model/FSTMutationTests.mm @@ -16,19 +16,32 @@ #import "Firestore/Source/Model/FSTMutation.h" +#import <FirebaseFirestore/FIRFieldValue.h> #import <FirebaseFirestore/FIRTimestamp.h> #import <XCTest/XCTest.h> +#include <vector> + #import "Firestore/Source/Model/FSTDocument.h" #import "Firestore/Source/Model/FSTFieldValue.h" #import "Firestore/Example/Tests/Util/FSTHelpers.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/field_mask.h" +#include "Firestore/core/src/firebase/firestore/model/field_transform.h" +#include "Firestore/core/src/firebase/firestore/model/precondition.h" +#include "Firestore/core/src/firebase/firestore/model/transform_operations.h" #include "Firestore/core/test/firebase/firestore/testutil/testutil.h" namespace testutil = firebase::firestore::testutil; +using firebase::firestore::model::ArrayTransform; using firebase::firestore::model::DocumentKey; +using firebase::firestore::model::FieldMask; +using firebase::firestore::model::FieldPath; +using firebase::firestore::model::FieldTransform; +using firebase::firestore::model::Precondition; +using firebase::firestore::model::TransformOperation; @interface FSTMutationTests : XCTestCase @end @@ -69,11 +82,10 @@ using firebase::firestore::model::DocumentKey; FSTDocument *baseDoc = FSTTestDoc("collection/key", 0, docData, NO); DocumentKey key = testutil::Key("collection/key"); - FSTFieldMask *mask = [[FSTFieldMask alloc] initWithFields:{testutil::Field("foo.bar")}]; FSTMutation *patch = [[FSTPatchMutation alloc] initWithKey:key - fieldMask:mask + fieldMask:{testutil::Field("foo.bar")} value:[FSTObjectValue objectValue] - precondition:[FSTPrecondition none]]; + precondition:Precondition::None()]; FSTMaybeDocument *patchedDoc = [patch applyTo:baseDoc baseDocument:baseDoc localWriteTime:_timestamp]; @@ -101,11 +113,12 @@ using firebase::firestore::model::DocumentKey; XCTAssertEqualObjects(patchedDoc, baseDoc); } -- (void)testAppliesLocalTransformsToDocuments { +- (void)testAppliesLocalServerTimestampTransformToDocuments { NSDictionary *docData = @{ @"foo" : @{@"bar" : @"bar-value"}, @"baz" : @"baz-value" }; FSTDocument *baseDoc = FSTTestDoc("collection/key", 0, docData, NO); - FSTMutation *transform = FSTTestTransformMutation(@"collection/key", @[ @"foo.bar" ]); + FSTMutation *transform = FSTTestTransformMutation( + @"collection/key", @{@"foo.bar" : [FIRFieldValue fieldValueForServerTimestamp]}); FSTMaybeDocument *transformedDoc = [transform applyTo:baseDoc baseDocument:baseDoc localWriteTime:_timestamp]; @@ -127,11 +140,57 @@ using firebase::firestore::model::DocumentKey; XCTAssertEqualObjects(transformedDoc, expectedDoc); } +// NOTE: This is more a test of FSTUserDataConverter code than FSTMutation code but we don't have +// unit tests for it currently. We could consider removing this test once we have integration tests. +- (void)testCreateArrayUnionTransform { + FSTTransformMutation *transform = FSTTestTransformMutation(@"collection/key", @{ + @"foo" : [FIRFieldValue fieldValueForArrayUnion:@[ @"tag" ]], + @"bar.baz" : [FIRFieldValue fieldValueForArrayUnion:@[ @YES, @[ @1, @2 ], @{@"a" : @"b"} ]] + }); + XCTAssertEqual(transform.fieldTransforms.size(), 2); + + const FieldTransform &first = transform.fieldTransforms[0]; + XCTAssertEqual(first.path(), FieldPath({"foo"})); + { + std::vector<FSTFieldValue *> expectedElements{FSTTestFieldValue(@"tag")}; + ArrayTransform expected(TransformOperation::Type::ArrayUnion, expectedElements); + XCTAssertEqual(static_cast<const ArrayTransform &>(first.transformation()), expected); + } + + const FieldTransform &second = transform.fieldTransforms[1]; + XCTAssertEqual(second.path(), FieldPath({"bar", "baz"})); + { + std::vector<FSTFieldValue *> expectedElements { + FSTTestFieldValue(@YES), FSTTestFieldValue(@[ @1, @2 ]), FSTTestFieldValue(@{@"a" : @"b"}) + }; + ArrayTransform expected(TransformOperation::Type::ArrayUnion, expectedElements); + XCTAssertEqual(static_cast<const ArrayTransform &>(second.transformation()), expected); + } +} + +// NOTE: This is more a test of FSTUserDataConverter code than FSTMutation code but we don't have +// unit tests for it currently. We could consider removing this test once we have integration tests. +- (void)testCreateArrayRemoveTransform { + FSTTransformMutation *transform = FSTTestTransformMutation(@"collection/key", @{ + @"foo" : [FIRFieldValue fieldValueForArrayRemove:@[ @"tag" ]], + }); + XCTAssertEqual(transform.fieldTransforms.size(), 1); + + const FieldTransform &first = transform.fieldTransforms[0]; + XCTAssertEqual(first.path(), FieldPath({"foo"})); + { + std::vector<FSTFieldValue *> expectedElements{FSTTestFieldValue(@"tag")}; + const ArrayTransform expected(TransformOperation::Type::ArrayRemove, expectedElements); + XCTAssertEqual(static_cast<const ArrayTransform &>(first.transformation()), expected); + } +} + - (void)testAppliesServerAckedTransformsToDocuments { NSDictionary *docData = @{ @"foo" : @{@"bar" : @"bar-value"}, @"baz" : @"baz-value" }; FSTDocument *baseDoc = FSTTestDoc("collection/key", 0, docData, NO); - FSTMutation *transform = FSTTestTransformMutation(@"collection/key", @[ @"foo.bar" ]); + FSTMutation *transform = FSTTestTransformMutation( + @"collection/key", @{@"foo.bar" : [FIRFieldValue fieldValueForServerTimestamp]}); FSTMutationResult *mutationResult = [[FSTMutationResult alloc] initWithVersion:FSTTestVersion(1) diff --git a/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm b/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm index 64f4777..a648cd8 100644 --- a/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm +++ b/Firestore/Example/Tests/Remote/FSTSerializerBetaTests.mm @@ -17,12 +17,15 @@ #import "Firestore/Source/Remote/FSTSerializerBeta.h" #import <FirebaseFirestore/FIRFieldPath.h> +#import <FirebaseFirestore/FIRFieldValue.h> #import <FirebaseFirestore/FIRFirestoreErrors.h> #import <FirebaseFirestore/FIRGeoPoint.h> #import <FirebaseFirestore/FIRTimestamp.h> #import <GRPCClient/GRPCCall.h> #import <XCTest/XCTest.h> +#include <vector> + #import "Firestore/Protos/objc/firestore/local/MaybeDocument.pbobjc.h" #import "Firestore/Protos/objc/firestore/local/Mutation.pbobjc.h" #import "Firestore/Protos/objc/google/firestore/v1beta1/Common.pbobjc.h" @@ -46,12 +49,18 @@ #import "Firestore/Example/Tests/Util/FSTHelpers.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" +#include "Firestore/core/src/firebase/firestore/model/field_mask.h" +#include "Firestore/core/src/firebase/firestore/model/field_transform.h" +#include "Firestore/core/src/firebase/firestore/model/precondition.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" #include "Firestore/core/test/firebase/firestore/testutil/testutil.h" namespace testutil = firebase::firestore::testutil; namespace util = firebase::firestore::util; using firebase::firestore::model::DatabaseId; +using firebase::firestore::model::FieldMask; +using firebase::firestore::model::FieldTransform; +using firebase::firestore::model::Precondition; NS_ASSUME_NONNULL_BEGIN @@ -63,9 +72,9 @@ NS_ASSUME_NONNULL_BEGIN - (GCFSValue *)encodedString:(NSString *)value; - (GCFSValue *)encodedDate:(NSDate *)value; -- (GCFSDocumentMask *)encodedFieldMask:(FSTFieldMask *)fieldMask; +- (GCFSDocumentMask *)encodedFieldMask:(const FieldMask &)fieldMask; - (NSMutableArray<GCFSDocumentTransform_FieldTransform *> *)encodedFieldTransforms: - (NSArray<FSTFieldTransform *> *)fieldTransforms; + (const std::vector<FieldTransform> &)fieldTransforms; - (GCFSStructuredQuery_Filter *)encodedRelationFilter:(FSTRelationFilter *)filter; @end @@ -359,7 +368,10 @@ NS_ASSUME_NONNULL_BEGIN } - (void)testEncodesTransformMutation { - FSTTransformMutation *mutation = FSTTestTransformMutation(@"docs/1", @[ @"a", @"bar.baz" ]); + FSTTransformMutation *mutation = FSTTestTransformMutation(@"docs/1", @{ + @"a" : [FIRFieldValue fieldValueForServerTimestamp], + @"bar.baz" : [FIRFieldValue fieldValueForServerTimestamp] + }); GCFSWrite *proto = [GCFSWrite message]; proto.transform = [GCFSDocumentTransform message]; proto.transform.document = [self.serializer encodedDocumentKey:mutation.key]; @@ -371,12 +383,12 @@ NS_ASSUME_NONNULL_BEGIN } - (void)testEncodesSetMutationWithPrecondition { - FSTSetMutation *mutation = [[FSTSetMutation alloc] - initWithKey:FSTTestDocKey(@"foo/bar") - value:FSTTestObjectValue( - @{ @"a" : @"b", - @"num" : @1 }) - precondition:[FSTPrecondition preconditionWithUpdateTime:FSTTestVersion(4)]]; + FSTSetMutation *mutation = + [[FSTSetMutation alloc] initWithKey:FSTTestDocKey(@"foo/bar") + value:FSTTestObjectValue( + @{ @"a" : @"b", + @"num" : @1 }) + precondition:Precondition::UpdateTime(testutil::Version(4))]; GCFSWrite *proto = [GCFSWrite message]; proto.update = [self.serializer encodedDocumentWithFields:mutation.value key:mutation.key]; proto.currentDocument.updateTime = diff --git a/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm index f66e6c7..128f825 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm +++ b/Firestore/Example/Tests/SpecTests/FSTSpecTests.mm @@ -16,12 +16,12 @@ #import "Firestore/Example/Tests/SpecTests/FSTSpecTests.h" -#include <map> -#include <utility> - #import <FirebaseFirestore/FIRFirestoreErrors.h> #import <GRPCClient/GRPCCall.h> +#include <map> +#include <utility> + #import "Firestore/Source/Core/FSTEventManager.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" diff --git a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm index 91140bf..f167ce5 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm +++ b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.mm @@ -16,12 +16,12 @@ #import "Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h" -#include <map> -#include <unordered_map> - #import <FirebaseFirestore/FIRFirestoreErrors.h> #import <GRPCClient/GRPCCall.h> +#include <map> +#include <unordered_map> + #import "Firestore/Source/Core/FSTEventManager.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" diff --git a/Firestore/Example/Tests/Util/FSTHelpers.h b/Firestore/Example/Tests/Util/FSTHelpers.h index d05c1f1..9b5f96a 100644 --- a/Firestore/Example/Tests/Util/FSTHelpers.h +++ b/Firestore/Example/Tests/Util/FSTHelpers.h @@ -245,8 +245,12 @@ FSTPatchMutation *FSTTestPatchMutation( NSDictionary<NSString *, id> *values, const std::vector<firebase::firestore::model::FieldPath> &updateMask); -FSTTransformMutation *FSTTestTransformMutation(NSString *path, - NSArray<NSString *> *serverTimestampFields); +/** + * Creates a FSTTransformMutation by parsing any FIRFieldValue sentinels in the provided data. The + * data is expected to use dotted-notation for nested fields (i.e. + * @{ @"foo.bar": [FIRFieldValue ...] } and must not contain any non-sentinel data. + */ +FSTTransformMutation *FSTTestTransformMutation(NSString *path, NSDictionary<NSString *, id> *data); /** Creates a delete mutation for the document key at the given path. */ FSTDeleteMutation *FSTTestDeleteMutation(NSString *path); diff --git a/Firestore/Example/Tests/Util/FSTHelpers.mm b/Firestore/Example/Tests/Util/FSTHelpers.mm index 5751739..e1ed18c 100644 --- a/Firestore/Example/Tests/Util/FSTHelpers.mm +++ b/Firestore/Example/Tests/Util/FSTHelpers.mm @@ -16,15 +16,16 @@ #import "Firestore/Example/Tests/Util/FSTHelpers.h" -#include <inttypes.h> -#include <list> -#include <map> -#include <vector> - #import <FirebaseFirestore/FIRFieldPath.h> #import <FirebaseFirestore/FIRGeoPoint.h> #import <FirebaseFirestore/FIRTimestamp.h> +#include <cinttypes> +#include <list> +#include <map> +#include <utility> +#include <vector> + #import "Firestore/Source/API/FIRFieldPath+Internal.h" #import "Firestore/Source/API/FSTUserDataConverter.h" #import "Firestore/Source/Core/FSTQuery.h" @@ -44,18 +45,28 @@ #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/field_mask.h" +#include "Firestore/core/src/firebase/firestore/model/field_transform.h" #include "Firestore/core/src/firebase/firestore/model/field_value.h" +#include "Firestore/core/src/firebase/firestore/model/precondition.h" #include "Firestore/core/src/firebase/firestore/model/resource_path.h" +#include "Firestore/core/src/firebase/firestore/model/transform_operations.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" #include "Firestore/core/test/firebase/firestore/testutil/testutil.h" +#include "absl/memory/memory.h" namespace util = firebase::firestore::util; namespace testutil = firebase::firestore::testutil; using firebase::firestore::model::DatabaseId; using firebase::firestore::model::DocumentKey; +using firebase::firestore::model::FieldMask; using firebase::firestore::model::FieldPath; +using firebase::firestore::model::FieldTransform; using firebase::firestore::model::FieldValue; +using firebase::firestore::model::Precondition; using firebase::firestore::model::ResourcePath; +using firebase::firestore::model::ServerTimestampTransform; +using firebase::firestore::model::TransformOperation; NS_ASSUME_NONNULL_BEGIN @@ -111,7 +122,7 @@ NSDateComponents *FSTTestDateComponents( return comps; } -FSTFieldValue *FSTTestFieldValue(id _Nullable value) { +FSTUserDataConverter *FSTTestUserDataConverter() { // This owns the DatabaseIds since we do not have FirestoreClient instance to own them. static DatabaseId database_id{"project", DatabaseId::kDefault}; FSTUserDataConverter *converter = @@ -119,6 +130,11 @@ FSTFieldValue *FSTTestFieldValue(id _Nullable value) { preConverter:^id _Nullable(id _Nullable input) { return input; }]; + return converter; +} + +FSTFieldValue *FSTTestFieldValue(id _Nullable value) { + FSTUserDataConverter *converter = FSTTestUserDataConverter(); // HACK: We use parsedQueryValue: since it accepts scalars as well as arrays / objects, and // our tests currently use FSTTestFieldValue() pretty generically so we don't know the intent. return [converter parsedQueryValue:value]; @@ -241,7 +257,7 @@ FSTDocumentSet *FSTTestDocSet(NSComparator comp, NSArray<FSTDocument *> *docs) { FSTSetMutation *FSTTestSetMutation(NSString *path, NSDictionary<NSString *, id> *values) { return [[FSTSetMutation alloc] initWithKey:FSTTestDocKey(path) value:FSTTestObjectValue(values) - precondition:[FSTPrecondition none]]; + precondition:Precondition::None()]; } FSTPatchMutation *FSTTestPatchMutation(const absl::string_view path, @@ -260,32 +276,27 @@ FSTPatchMutation *FSTTestPatchMutation(const absl::string_view path, } }]; - FSTDocumentKey *key = [FSTDocumentKey keyWithPath:testutil::Resource(path)]; - FSTFieldMask *mask = [[FSTFieldMask alloc] initWithFields:merge ? updateMask : fieldMaskPaths]; + DocumentKey key = testutil::Key(path); + FieldMask mask(merge ? updateMask : fieldMaskPaths); return [[FSTPatchMutation alloc] initWithKey:key fieldMask:mask value:objectValue - precondition:[FSTPrecondition preconditionWithExists:YES]]; + precondition:Precondition::Exists(true)]; } -// For now this only creates TransformMutations with server timestamps. -FSTTransformMutation *FSTTestTransformMutation(NSString *path, - NSArray<NSString *> *serverTimestampFields) { +FSTTransformMutation *FSTTestTransformMutation(NSString *path, NSDictionary<NSString *, id> *data) { FSTDocumentKey *key = [FSTDocumentKey keyWithPath:testutil::Resource(util::MakeStringView(path))]; - NSMutableArray<FSTFieldTransform *> *fieldTransforms = [NSMutableArray array]; - for (NSString *field in serverTimestampFields) { - const FieldPath fieldPath = testutil::Field(util::MakeStringView(field)); - id<FSTTransformOperation> transformOp = [FSTServerTimestampTransform serverTimestampTransform]; - FSTFieldTransform *transform = - [[FSTFieldTransform alloc] initWithPath:fieldPath transform:transformOp]; - [fieldTransforms addObject:transform]; - } - return [[FSTTransformMutation alloc] initWithKey:key fieldTransforms:fieldTransforms]; + FSTUserDataConverter *converter = FSTTestUserDataConverter(); + FSTParsedUpdateData *result = [converter parsedUpdateData:data]; + FSTCAssert(result.data.value.count == 0, + @"FSTTestTransformMutation() only expects transforms; no other data"); + return [[FSTTransformMutation alloc] initWithKey:key + fieldTransforms:std::move(result.fieldTransforms)]; } FSTDeleteMutation *FSTTestDeleteMutation(NSString *path) { - return [[FSTDeleteMutation alloc] initWithKey:FSTTestDocKey(path) - precondition:[FSTPrecondition none]]; + return + [[FSTDeleteMutation alloc] initWithKey:FSTTestDocKey(path) precondition:Precondition::None()]; } FSTMaybeDocumentDictionary *FSTTestDocUpdates(NSArray<FSTMaybeDocument *> *docs) { diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm index 611bcc8..79163da 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm @@ -16,14 +16,15 @@ #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" -#include <memory> -#include <utility> - #import <FirebaseCore/FIRLogger.h> #import <FirebaseFirestore/FirebaseFirestore-umbrella.h> #import <GRPCClient/GRPCCall+ChannelArg.h> #import <GRPCClient/GRPCCall+Tests.h> +#include <memory> +#include <string> +#include <utility> + #include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/util/autoid.h" diff --git a/Firestore/Protos/FrameworkMaker.xcodeproj/project.pbxproj b/Firestore/Protos/FrameworkMaker.xcodeproj/project.pbxproj index 2efcb21..4a1e9d4 100644 --- a/Firestore/Protos/FrameworkMaker.xcodeproj/project.pbxproj +++ b/Firestore/Protos/FrameworkMaker.xcodeproj/project.pbxproj @@ -205,7 +205,7 @@ ); name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; diff --git a/Firestore/Protos/README.md b/Firestore/Protos/README.md index 6137ff6..fbede5b 100644 --- a/Firestore/Protos/README.md +++ b/Firestore/Protos/README.md @@ -1,6 +1,11 @@ ## Usage -First, build protobuf and nanopb +First, make sure you have necessary prereqs for building: +``` +brew install automake libtool protobuf +``` + +Take a nap while that completes. Then, build protobuf and nanopb: ``` cd firebase-ios-sdk mkdir -p build @@ -15,7 +20,8 @@ cd firebase-ios-sdk/Firestore/Protos ./build-protos.sh ``` -Verify diffs, tests and make PR +Verify diffs (you'll likely need to re-add copyright notices, etc.), make sure +tests still pass, and create a PR. ### Script Details diff --git a/Firestore/Protos/nanopb/firestore/local/target.pb.c b/Firestore/Protos/nanopb/firestore/local/target.pb.c index 2805ceb..d00d4a6 100644 --- a/Firestore/Protos/nanopb/firestore/local/target.pb.c +++ b/Firestore/Protos/nanopb/firestore/local/target.pb.c @@ -15,7 +15,7 @@ */ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ +/* Generated by nanopb-0.3.8 at Mon Apr 9 15:08:47 2018. */ #include "target.pb.h" @@ -36,10 +36,11 @@ const pb_field_t firestore_client_Target_fields[7] = { PB_LAST_FIELD }; -const pb_field_t firestore_client_TargetGlobal_fields[4] = { +const pb_field_t firestore_client_TargetGlobal_fields[5] = { PB_FIELD( 1, INT32 , SINGULAR, STATIC , FIRST, firestore_client_TargetGlobal, highest_target_id, highest_target_id, 0), PB_FIELD( 2, INT64 , SINGULAR, STATIC , OTHER, firestore_client_TargetGlobal, highest_listen_sequence_number, highest_target_id, 0), PB_FIELD( 3, MESSAGE , SINGULAR, STATIC , OTHER, firestore_client_TargetGlobal, last_remote_snapshot_version, highest_listen_sequence_number, &google_protobuf_Timestamp_fields), + PB_FIELD( 4, INT32 , SINGULAR, STATIC , OTHER, firestore_client_TargetGlobal, target_count, last_remote_snapshot_version, 0), PB_LAST_FIELD }; diff --git a/Firestore/Protos/nanopb/firestore/local/target.pb.h b/Firestore/Protos/nanopb/firestore/local/target.pb.h index 8cc146f..37b64a2 100644 --- a/Firestore/Protos/nanopb/firestore/local/target.pb.h +++ b/Firestore/Protos/nanopb/firestore/local/target.pb.h @@ -15,7 +15,7 @@ */ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ +/* Generated by nanopb-0.3.8 at Mon Apr 9 15:08:47 2018. */ #ifndef PB_FIRESTORE_CLIENT_TARGET_PB_H_INCLUDED #define PB_FIRESTORE_CLIENT_TARGET_PB_H_INCLUDED @@ -52,6 +52,7 @@ typedef struct _firestore_client_TargetGlobal { int32_t highest_target_id; int64_t highest_listen_sequence_number; google_protobuf_Timestamp last_remote_snapshot_version; + int32_t target_count; /* @@protoc_insertion_point(struct:firestore_client_TargetGlobal) */ } firestore_client_TargetGlobal; @@ -59,9 +60,9 @@ typedef struct _firestore_client_TargetGlobal { /* Initializer values for message structs */ #define firestore_client_Target_init_default {0, google_protobuf_Timestamp_init_default, {{NULL}, NULL}, 0, 0, {google_firestore_v1beta1_Target_QueryTarget_init_default}} -#define firestore_client_TargetGlobal_init_default {0, 0, google_protobuf_Timestamp_init_default} +#define firestore_client_TargetGlobal_init_default {0, 0, google_protobuf_Timestamp_init_default, 0} #define firestore_client_Target_init_zero {0, google_protobuf_Timestamp_init_zero, {{NULL}, NULL}, 0, 0, {google_firestore_v1beta1_Target_QueryTarget_init_zero}} -#define firestore_client_TargetGlobal_init_zero {0, 0, google_protobuf_Timestamp_init_zero} +#define firestore_client_TargetGlobal_init_zero {0, 0, google_protobuf_Timestamp_init_zero, 0} /* Field tags (for use in manual encoding/decoding) */ #define firestore_client_Target_query_tag 5 @@ -73,14 +74,15 @@ typedef struct _firestore_client_TargetGlobal { #define firestore_client_TargetGlobal_highest_target_id_tag 1 #define firestore_client_TargetGlobal_highest_listen_sequence_number_tag 2 #define firestore_client_TargetGlobal_last_remote_snapshot_version_tag 3 +#define firestore_client_TargetGlobal_target_count_tag 4 /* Struct field encoding specification for nanopb */ extern const pb_field_t firestore_client_Target_fields[7]; -extern const pb_field_t firestore_client_TargetGlobal_fields[4]; +extern const pb_field_t firestore_client_TargetGlobal_fields[5]; /* Maximum encoded size of messages (where known) */ /* firestore_client_Target_size depends on runtime parameters */ -#define firestore_client_TargetGlobal_size 46 +#define firestore_client_TargetGlobal_size 57 /* Message IDs (where set with "msgid" option) */ #ifdef PB_MSGID diff --git a/Firestore/Protos/nanopb/google/api/http.pb.c b/Firestore/Protos/nanopb/google/api/http.pb.c index 5219163..7a2cd21 100644 --- a/Firestore/Protos/nanopb/google/api/http.pb.c +++ b/Firestore/Protos/nanopb/google/api/http.pb.c @@ -15,7 +15,7 @@ */ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ +/* Generated by nanopb-0.3.8 at Thu Apr 12 07:27:15 2018. */ #include "http.pb.h" @@ -26,8 +26,9 @@ -const pb_field_t google_api_Http_fields[2] = { +const pb_field_t google_api_Http_fields[3] = { PB_FIELD( 1, MESSAGE , REPEATED, CALLBACK, FIRST, google_api_Http, rules, rules, &google_api_HttpRule_fields), + PB_FIELD( 2, BOOL , SINGULAR, STATIC , OTHER, google_api_Http, fully_decode_reserved_expansion, rules, 0), PB_LAST_FIELD }; diff --git a/Firestore/Protos/nanopb/google/api/http.pb.h b/Firestore/Protos/nanopb/google/api/http.pb.h index e29758c..a7bbc46 100644 --- a/Firestore/Protos/nanopb/google/api/http.pb.h +++ b/Firestore/Protos/nanopb/google/api/http.pb.h @@ -15,7 +15,7 @@ */ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ +/* Generated by nanopb-0.3.8 at Thu Apr 12 07:27:15 2018. */ #ifndef PB_GOOGLE_API_HTTP_PB_H_INCLUDED #define PB_GOOGLE_API_HTTP_PB_H_INCLUDED @@ -39,6 +39,7 @@ typedef struct _google_api_CustomHttpPattern { typedef struct _google_api_Http { pb_callback_t rules; + bool fully_decode_reserved_expansion; /* @@protoc_insertion_point(struct:google_api_Http) */ } google_api_Http; @@ -58,10 +59,10 @@ typedef struct _google_api_HttpRule { /* Default values for struct fields */ /* Initializer values for message structs */ -#define google_api_Http_init_default {{{NULL}, NULL}} +#define google_api_Http_init_default {{{NULL}, NULL}, 0} #define google_api_HttpRule_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, google_api_CustomHttpPattern_init_default, {{NULL}, NULL}} #define google_api_CustomHttpPattern_init_default {{{NULL}, NULL}, {{NULL}, NULL}} -#define google_api_Http_init_zero {{{NULL}, NULL}} +#define google_api_Http_init_zero {{{NULL}, NULL}, 0} #define google_api_HttpRule_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, google_api_CustomHttpPattern_init_zero, {{NULL}, NULL}} #define google_api_CustomHttpPattern_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} @@ -69,6 +70,7 @@ typedef struct _google_api_HttpRule { #define google_api_CustomHttpPattern_kind_tag 1 #define google_api_CustomHttpPattern_path_tag 2 #define google_api_Http_rules_tag 1 +#define google_api_Http_fully_decode_reserved_expansion_tag 2 #define google_api_HttpRule_selector_tag 1 #define google_api_HttpRule_get_tag 2 #define google_api_HttpRule_put_tag 3 @@ -80,7 +82,7 @@ typedef struct _google_api_HttpRule { #define google_api_HttpRule_additional_bindings_tag 11 /* Struct field encoding specification for nanopb */ -extern const pb_field_t google_api_Http_fields[2]; +extern const pb_field_t google_api_Http_fields[3]; extern const pb_field_t google_api_HttpRule_fields[10]; extern const pb_field_t google_api_CustomHttpPattern_fields[3]; diff --git a/Firestore/Protos/nanopb/google/firestore/v1beta1/query.pb.h b/Firestore/Protos/nanopb/google/firestore/v1beta1/query.pb.h index faad2a2..75b8168 100644 --- a/Firestore/Protos/nanopb/google/firestore/v1beta1/query.pb.h +++ b/Firestore/Protos/nanopb/google/firestore/v1beta1/query.pb.h @@ -15,7 +15,7 @@ */ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ +/* Generated by nanopb-0.3.8 at Thu Apr 12 07:27:15 2018. */ #ifndef PB_GOOGLE_FIRESTORE_V1BETA1_QUERY_PB_H_INCLUDED #define PB_GOOGLE_FIRESTORE_V1BETA1_QUERY_PB_H_INCLUDED @@ -60,11 +60,12 @@ typedef enum _google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator { google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_LESS_THAN_OR_EQUAL = 2, google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_GREATER_THAN = 3, google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_GREATER_THAN_OR_EQUAL = 4, - google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_EQUAL = 5 + google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_EQUAL = 5, + google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_ARRAY_CONTAINS = 7 } google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator; #define _google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_MIN google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_OPERATOR_UNSPECIFIED -#define _google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_MAX google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_EQUAL -#define _google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_ARRAYSIZE ((google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator)(google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_EQUAL+1)) +#define _google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_MAX google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_ARRAY_CONTAINS +#define _google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_ARRAYSIZE ((google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator)(google_firestore_v1beta1_StructuredQuery_FieldFilter_Operator_ARRAY_CONTAINS+1)) typedef enum _google_firestore_v1beta1_StructuredQuery_UnaryFilter_Operator { google_firestore_v1beta1_StructuredQuery_UnaryFilter_Operator_OPERATOR_UNSPECIFIED = 0, diff --git a/Firestore/Protos/nanopb/google/firestore/v1beta1/write.pb.c b/Firestore/Protos/nanopb/google/firestore/v1beta1/write.pb.c index 462eca6..26542e0 100644 --- a/Firestore/Protos/nanopb/google/firestore/v1beta1/write.pb.c +++ b/Firestore/Protos/nanopb/google/firestore/v1beta1/write.pb.c @@ -15,7 +15,7 @@ */ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ +/* Generated by nanopb-0.3.8 at Thu Apr 12 07:27:15 2018. */ #include "write.pb.h" @@ -26,13 +26,12 @@ -const pb_field_t google_firestore_v1beta1_Write_fields[7] = { +const pb_field_t google_firestore_v1beta1_Write_fields[6] = { PB_FIELD( 1, MESSAGE , SINGULAR, STATIC , FIRST, google_firestore_v1beta1_Write, update, update, &google_firestore_v1beta1_Document_fields), PB_FIELD( 2, STRING , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_Write, delete_, update, 0), PB_FIELD( 3, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Write, update_mask, delete_, &google_firestore_v1beta1_DocumentMask_fields), PB_FIELD( 4, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Write, current_document, update_mask, &google_firestore_v1beta1_Precondition_fields), - PB_FIELD( 5, STRING , SINGULAR, CALLBACK, OTHER, google_firestore_v1beta1_Write, verify, current_document, 0), - PB_FIELD( 6, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Write, transform, verify, &google_firestore_v1beta1_DocumentTransform_fields), + PB_FIELD( 6, MESSAGE , SINGULAR, STATIC , OTHER, google_firestore_v1beta1_Write, transform, current_document, &google_firestore_v1beta1_DocumentTransform_fields), PB_LAST_FIELD }; @@ -42,9 +41,11 @@ const pb_field_t google_firestore_v1beta1_DocumentTransform_fields[3] = { PB_LAST_FIELD }; -const pb_field_t google_firestore_v1beta1_DocumentTransform_FieldTransform_fields[3] = { +const pb_field_t google_firestore_v1beta1_DocumentTransform_FieldTransform_fields[5] = { PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_firestore_v1beta1_DocumentTransform_FieldTransform, field_path, field_path, 0), PB_ONEOF_FIELD(transform_type, 2, ENUM , ONEOF, STATIC , OTHER, google_firestore_v1beta1_DocumentTransform_FieldTransform, set_to_server_value, field_path, 0), + PB_ONEOF_FIELD(transform_type, 6, MESSAGE , ONEOF, STATIC , UNION, google_firestore_v1beta1_DocumentTransform_FieldTransform, append_missing_elements, field_path, &google_firestore_v1beta1_ArrayValue_fields), + PB_ONEOF_FIELD(transform_type, 7, MESSAGE , ONEOF, STATIC , UNION, google_firestore_v1beta1_DocumentTransform_FieldTransform, remove_all_from_array, field_path, &google_firestore_v1beta1_ArrayValue_fields), PB_LAST_FIELD }; @@ -92,7 +93,7 @@ const pb_field_t google_firestore_v1beta1_ExistenceFilter_fields[3] = { * numbers or field sizes that are larger than what can fit in 8 or 16 bit * field descriptors. */ -PB_STATIC_ASSERT((pb_membersize(google_firestore_v1beta1_Write, update) < 65536 && pb_membersize(google_firestore_v1beta1_Write, transform) < 65536 && pb_membersize(google_firestore_v1beta1_Write, update_mask) < 65536 && pb_membersize(google_firestore_v1beta1_Write, current_document) < 65536 && pb_membersize(google_firestore_v1beta1_WriteResult, update_time) < 65536 && pb_membersize(google_firestore_v1beta1_DocumentChange, document) < 65536 && pb_membersize(google_firestore_v1beta1_DocumentDelete, read_time) < 65536 && pb_membersize(google_firestore_v1beta1_DocumentRemove, read_time) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_google_firestore_v1beta1_Write_google_firestore_v1beta1_DocumentTransform_google_firestore_v1beta1_DocumentTransform_FieldTransform_google_firestore_v1beta1_WriteResult_google_firestore_v1beta1_DocumentChange_google_firestore_v1beta1_DocumentDelete_google_firestore_v1beta1_DocumentRemove_google_firestore_v1beta1_ExistenceFilter) +PB_STATIC_ASSERT((pb_membersize(google_firestore_v1beta1_Write, update) < 65536 && pb_membersize(google_firestore_v1beta1_Write, transform) < 65536 && pb_membersize(google_firestore_v1beta1_Write, update_mask) < 65536 && pb_membersize(google_firestore_v1beta1_Write, current_document) < 65536 && pb_membersize(google_firestore_v1beta1_DocumentTransform_FieldTransform, transform_type.append_missing_elements) < 65536 && pb_membersize(google_firestore_v1beta1_DocumentTransform_FieldTransform, transform_type.remove_all_from_array) < 65536 && pb_membersize(google_firestore_v1beta1_WriteResult, update_time) < 65536 && pb_membersize(google_firestore_v1beta1_DocumentChange, document) < 65536 && pb_membersize(google_firestore_v1beta1_DocumentDelete, read_time) < 65536 && pb_membersize(google_firestore_v1beta1_DocumentRemove, read_time) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_google_firestore_v1beta1_Write_google_firestore_v1beta1_DocumentTransform_google_firestore_v1beta1_DocumentTransform_FieldTransform_google_firestore_v1beta1_WriteResult_google_firestore_v1beta1_DocumentChange_google_firestore_v1beta1_DocumentDelete_google_firestore_v1beta1_DocumentRemove_google_firestore_v1beta1_ExistenceFilter) #endif #if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) @@ -103,7 +104,7 @@ PB_STATIC_ASSERT((pb_membersize(google_firestore_v1beta1_Write, update) < 65536 * numbers or field sizes that are larger than what can fit in the default * 8 bit descriptors. */ -PB_STATIC_ASSERT((pb_membersize(google_firestore_v1beta1_Write, update) < 256 && pb_membersize(google_firestore_v1beta1_Write, transform) < 256 && pb_membersize(google_firestore_v1beta1_Write, update_mask) < 256 && pb_membersize(google_firestore_v1beta1_Write, current_document) < 256 && pb_membersize(google_firestore_v1beta1_WriteResult, update_time) < 256 && pb_membersize(google_firestore_v1beta1_DocumentChange, document) < 256 && pb_membersize(google_firestore_v1beta1_DocumentDelete, read_time) < 256 && pb_membersize(google_firestore_v1beta1_DocumentRemove, read_time) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_google_firestore_v1beta1_Write_google_firestore_v1beta1_DocumentTransform_google_firestore_v1beta1_DocumentTransform_FieldTransform_google_firestore_v1beta1_WriteResult_google_firestore_v1beta1_DocumentChange_google_firestore_v1beta1_DocumentDelete_google_firestore_v1beta1_DocumentRemove_google_firestore_v1beta1_ExistenceFilter) +PB_STATIC_ASSERT((pb_membersize(google_firestore_v1beta1_Write, update) < 256 && pb_membersize(google_firestore_v1beta1_Write, transform) < 256 && pb_membersize(google_firestore_v1beta1_Write, update_mask) < 256 && pb_membersize(google_firestore_v1beta1_Write, current_document) < 256 && pb_membersize(google_firestore_v1beta1_DocumentTransform_FieldTransform, transform_type.append_missing_elements) < 256 && pb_membersize(google_firestore_v1beta1_DocumentTransform_FieldTransform, transform_type.remove_all_from_array) < 256 && pb_membersize(google_firestore_v1beta1_WriteResult, update_time) < 256 && pb_membersize(google_firestore_v1beta1_DocumentChange, document) < 256 && pb_membersize(google_firestore_v1beta1_DocumentDelete, read_time) < 256 && pb_membersize(google_firestore_v1beta1_DocumentRemove, read_time) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_google_firestore_v1beta1_Write_google_firestore_v1beta1_DocumentTransform_google_firestore_v1beta1_DocumentTransform_FieldTransform_google_firestore_v1beta1_WriteResult_google_firestore_v1beta1_DocumentChange_google_firestore_v1beta1_DocumentDelete_google_firestore_v1beta1_DocumentRemove_google_firestore_v1beta1_ExistenceFilter) #endif diff --git a/Firestore/Protos/nanopb/google/firestore/v1beta1/write.pb.h b/Firestore/Protos/nanopb/google/firestore/v1beta1/write.pb.h index a44b6d4..5a272b2 100644 --- a/Firestore/Protos/nanopb/google/firestore/v1beta1/write.pb.h +++ b/Firestore/Protos/nanopb/google/firestore/v1beta1/write.pb.h @@ -15,7 +15,7 @@ */ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.3.8 at Fri Feb 2 17:48:02 2018. */ +/* Generated by nanopb-0.3.8 at Thu Apr 12 07:27:15 2018. */ #ifndef PB_GOOGLE_FIRESTORE_V1BETA1_WRITE_PB_H_INCLUDED #define PB_GOOGLE_FIRESTORE_V1BETA1_WRITE_PB_H_INCLUDED @@ -80,6 +80,8 @@ typedef struct _google_firestore_v1beta1_DocumentTransform_FieldTransform { pb_size_t which_transform_type; union { google_firestore_v1beta1_DocumentTransform_FieldTransform_ServerValue set_to_server_value; + google_firestore_v1beta1_ArrayValue append_missing_elements; + google_firestore_v1beta1_ArrayValue remove_all_from_array; } transform_type; /* @@protoc_insertion_point(struct:google_firestore_v1beta1_DocumentTransform_FieldTransform) */ } google_firestore_v1beta1_DocumentTransform_FieldTransform; @@ -95,7 +97,6 @@ typedef struct _google_firestore_v1beta1_Write { pb_callback_t delete_; google_firestore_v1beta1_DocumentMask update_mask; google_firestore_v1beta1_Precondition current_document; - pb_callback_t verify; google_firestore_v1beta1_DocumentTransform transform; /* @@protoc_insertion_point(struct:google_firestore_v1beta1_Write) */ } google_firestore_v1beta1_Write; @@ -109,7 +110,7 @@ typedef struct _google_firestore_v1beta1_WriteResult { /* Default values for struct fields */ /* Initializer values for message structs */ -#define google_firestore_v1beta1_Write_init_default {google_firestore_v1beta1_Document_init_default, {{NULL}, NULL}, google_firestore_v1beta1_DocumentMask_init_default, google_firestore_v1beta1_Precondition_init_default, {{NULL}, NULL}, google_firestore_v1beta1_DocumentTransform_init_default} +#define google_firestore_v1beta1_Write_init_default {google_firestore_v1beta1_Document_init_default, {{NULL}, NULL}, google_firestore_v1beta1_DocumentMask_init_default, google_firestore_v1beta1_Precondition_init_default, google_firestore_v1beta1_DocumentTransform_init_default} #define google_firestore_v1beta1_DocumentTransform_init_default {{{NULL}, NULL}, {{NULL}, NULL}} #define google_firestore_v1beta1_DocumentTransform_FieldTransform_init_default {{{NULL}, NULL}, 0, {(google_firestore_v1beta1_DocumentTransform_FieldTransform_ServerValue)0}} #define google_firestore_v1beta1_WriteResult_init_default {google_protobuf_Timestamp_init_default, {{NULL}, NULL}} @@ -117,7 +118,7 @@ typedef struct _google_firestore_v1beta1_WriteResult { #define google_firestore_v1beta1_DocumentDelete_init_default {{{NULL}, NULL}, google_protobuf_Timestamp_init_default, {{NULL}, NULL}} #define google_firestore_v1beta1_DocumentRemove_init_default {{{NULL}, NULL}, {{NULL}, NULL}, google_protobuf_Timestamp_init_default} #define google_firestore_v1beta1_ExistenceFilter_init_default {0, 0} -#define google_firestore_v1beta1_Write_init_zero {google_firestore_v1beta1_Document_init_zero, {{NULL}, NULL}, google_firestore_v1beta1_DocumentMask_init_zero, google_firestore_v1beta1_Precondition_init_zero, {{NULL}, NULL}, google_firestore_v1beta1_DocumentTransform_init_zero} +#define google_firestore_v1beta1_Write_init_zero {google_firestore_v1beta1_Document_init_zero, {{NULL}, NULL}, google_firestore_v1beta1_DocumentMask_init_zero, google_firestore_v1beta1_Precondition_init_zero, google_firestore_v1beta1_DocumentTransform_init_zero} #define google_firestore_v1beta1_DocumentTransform_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} #define google_firestore_v1beta1_DocumentTransform_FieldTransform_init_zero {{{NULL}, NULL}, 0, {(google_firestore_v1beta1_DocumentTransform_FieldTransform_ServerValue)0}} #define google_firestore_v1beta1_WriteResult_init_zero {google_protobuf_Timestamp_init_zero, {{NULL}, NULL}} @@ -139,12 +140,13 @@ typedef struct _google_firestore_v1beta1_WriteResult { #define google_firestore_v1beta1_DocumentRemove_removed_target_ids_tag 2 #define google_firestore_v1beta1_DocumentRemove_read_time_tag 4 #define google_firestore_v1beta1_DocumentTransform_FieldTransform_set_to_server_value_tag 2 +#define google_firestore_v1beta1_DocumentTransform_FieldTransform_append_missing_elements_tag 6 +#define google_firestore_v1beta1_DocumentTransform_FieldTransform_remove_all_from_array_tag 7 #define google_firestore_v1beta1_DocumentTransform_FieldTransform_field_path_tag 1 #define google_firestore_v1beta1_ExistenceFilter_target_id_tag 1 #define google_firestore_v1beta1_ExistenceFilter_count_tag 2 #define google_firestore_v1beta1_Write_update_tag 1 #define google_firestore_v1beta1_Write_delete_tag 2 -#define google_firestore_v1beta1_Write_verify_tag 5 #define google_firestore_v1beta1_Write_transform_tag 6 #define google_firestore_v1beta1_Write_update_mask_tag 3 #define google_firestore_v1beta1_Write_current_document_tag 4 @@ -152,9 +154,9 @@ typedef struct _google_firestore_v1beta1_WriteResult { #define google_firestore_v1beta1_WriteResult_transform_results_tag 2 /* Struct field encoding specification for nanopb */ -extern const pb_field_t google_firestore_v1beta1_Write_fields[7]; +extern const pb_field_t google_firestore_v1beta1_Write_fields[6]; extern const pb_field_t google_firestore_v1beta1_DocumentTransform_fields[3]; -extern const pb_field_t google_firestore_v1beta1_DocumentTransform_FieldTransform_fields[3]; +extern const pb_field_t google_firestore_v1beta1_DocumentTransform_FieldTransform_fields[5]; extern const pb_field_t google_firestore_v1beta1_WriteResult_fields[3]; extern const pb_field_t google_firestore_v1beta1_DocumentChange_fields[4]; extern const pb_field_t google_firestore_v1beta1_DocumentDelete_fields[4]; diff --git a/Firestore/Protos/objc/google/api/HTTP.pbobjc.h b/Firestore/Protos/objc/google/api/HTTP.pbobjc.h index 9cc00dc..c9e5bb5 100644 --- a/Firestore/Protos/objc/google/api/HTTP.pbobjc.h +++ b/Firestore/Protos/objc/google/api/HTTP.pbobjc.h @@ -67,10 +67,11 @@ NS_ASSUME_NONNULL_BEGIN typedef GPB_ENUM(GAPIHttp_FieldNumber) { GAPIHttp_FieldNumber_RulesArray = 1, + GAPIHttp_FieldNumber_FullyDecodeReservedExpansion = 2, }; /** - * Defines the HTTP configuration for a service. It contains a list of + * Defines the HTTP configuration for an API service. It contains a list of * [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method * to one or more HTTP REST API methods. **/ @@ -85,6 +86,16 @@ typedef GPB_ENUM(GAPIHttp_FieldNumber) { /** The number of items in @c rulesArray without causing the array to be created. */ @property(nonatomic, readonly) NSUInteger rulesArray_Count; +/** + * When set to true, URL path parmeters will be fully URI-decoded except in + * cases of single segment matches in reserved expansion, where "%2F" will be + * left encoded. + * + * The default behavior is to not decode RFC 6570 reserved characters in multi + * segment matches. + **/ +@property(nonatomic, readwrite) BOOL fullyDecodeReservedExpansion; + @end #pragma mark - GAPIHttpRule @@ -113,11 +124,11 @@ typedef GPB_ENUM(GAPIHttpRule_Pattern_OneOfCase) { /** * `HttpRule` defines the mapping of an RPC method to one or more HTTP - * REST APIs. The mapping determines what portions of the request - * message are populated from the path, query parameters, or body of - * the HTTP request. The mapping is typically specified as an - * `google.api.http` annotation, see "google/api/annotations.proto" - * for details. + * REST API methods. The mapping specifies how different portions of the RPC + * request message are mapped to URL path, URL query parameters, and + * HTTP request body. The mapping is typically specified as an + * `google.api.http` annotation on the RPC method, + * see "google/api/annotations.proto" for details. * * The mapping consists of a field specifying the path template and * method kind. The path template can refer to fields in the request @@ -165,6 +176,11 @@ typedef GPB_ENUM(GAPIHttpRule_Pattern_OneOfCase) { * parameters. Assume the following definition of the request message: * * + * service Messaging { + * rpc GetMessage(GetMessageRequest) returns (Message) { + * option (google.api.http).get = "/v1/messages/{message_id}"; + * } + * } * message GetMessageRequest { * message SubMessage { * string subfield = 1; @@ -277,7 +293,7 @@ typedef GPB_ENUM(GAPIHttpRule_Pattern_OneOfCase) { * to the request message are as follows: * * 1. The `body` field specifies either `*` or a field path, or is - * omitted. If omitted, it assumes there is no HTTP body. + * omitted. If omitted, it indicates there is no HTTP request body. * 2. Leaf fields (recursive expansion of nested messages in the * request) can be classified into three types: * (a) Matched in the URL template. @@ -296,28 +312,34 @@ typedef GPB_ENUM(GAPIHttpRule_Pattern_OneOfCase) { * FieldPath = IDENT { "." IDENT } ; * Verb = ":" LITERAL ; * - * The syntax `*` matches a single path segment. It follows the semantics of - * [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String - * Expansion. + * The syntax `*` matches a single path segment. The syntax `**` matches zero + * or more path segments, which must be the last part of the path except the + * `Verb`. The syntax `LITERAL` matches literal text in the path. * - * The syntax `**` matches zero or more path segments. It follows the semantics - * of [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.3 Reserved - * Expansion. NOTE: it must be the last segment in the path except the Verb. - * - * The syntax `LITERAL` matches literal text in the URL path. - * - * The syntax `Variable` matches the entire path as specified by its template; - * this nested template must not contain further variables. If a variable + * The syntax `Variable` matches part of the URL path as specified by its + * template. A variable template must not contain other variables. If a variable * matches a single path segment, its template may be omitted, e.g. `{var}` * is equivalent to `{var=*}`. * + * If a variable contains exactly one path segment, such as `"{var}"` or + * `"{var=*}"`, when such a variable is expanded into a URL path, all characters + * except `[-_.~0-9a-zA-Z]` are percent-encoded. Such variables show up in the + * Discovery Document as `{var}`. + * + * If a variable contains one or more path segments, such as `"{var=foo/\*}"` + * or `"{var=**}"`, when such a variable is expanded into a URL path, all + * characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. Such variables + * show up in the Discovery Document as `{+var}`. + * + * NOTE: While the single segment variable matches the semantics of + * [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 + * Simple String Expansion, the multi segment variable **does not** match + * RFC 6570 Reserved Expansion. The reason is that the Reserved Expansion + * does not expand special characters like `?` and `#`, which would lead + * to invalid URLs. + * * NOTE: the field paths in variables and in the `body` must not refer to * repeated fields or map fields. - * - * Use CustomHttpPattern to specify any HTTP method that is not included in the - * `pattern` field, such as HEAD, or "*" to leave the HTTP method unspecified for - * a given URL path rule. The wild-card rule is useful for services that provide - * content to Web (HTML) clients. **/ @interface GAPIHttpRule : GPBMessage @@ -350,7 +372,12 @@ typedef GPB_ENUM(GAPIHttpRule_Pattern_OneOfCase) { /** Used for updating a resource. */ @property(nonatomic, readwrite, copy, null_resettable) NSString *patch; -/** Custom pattern is used for defining custom verbs. */ +/** + * The custom pattern is used for specifying an HTTP method that is not + * included in the `pattern` field, such as HEAD, or "*" to leave the + * HTTP method unspecified for this rule. The wild-card rule is useful + * for services that provide content to Web (HTML) clients. + **/ @property(nonatomic, readwrite, strong, null_resettable) GAPICustomHttpPattern *custom; /** diff --git a/Firestore/Protos/objc/google/api/HTTP.pbobjc.m b/Firestore/Protos/objc/google/api/HTTP.pbobjc.m index 5adf41c..67d8732 100644 --- a/Firestore/Protos/objc/google/api/HTTP.pbobjc.m +++ b/Firestore/Protos/objc/google/api/HTTP.pbobjc.m @@ -65,6 +65,7 @@ static GPBFileDescriptor *GAPIHTTPRoot_FileDescriptor(void) { @implementation GAPIHttp @dynamic rulesArray, rulesArray_Count; +@dynamic fullyDecodeReservedExpansion; typedef struct GAPIHttp__storage_ { uint32_t _has_storage_[1]; @@ -86,6 +87,15 @@ typedef struct GAPIHttp__storage_ { .flags = GPBFieldRepeated, .dataType = GPBDataTypeMessage, }, + { + .name = "fullyDecodeReservedExpansion", + .dataTypeSpecific.className = NULL, + .number = GAPIHttp_FieldNumber_FullyDecodeReservedExpansion, + .hasIndex = 0, + .offset = 1, // Stored in _has_storage_ to save space. + .flags = GPBFieldOptional, + .dataType = GPBDataTypeBool, + }, }; GPBDescriptor *localDescriptor = [GPBDescriptor allocDescriptorForClass:[GAPIHttp class] diff --git a/Firestore/Protos/objc/google/firestore/v1beta1/Document.pbobjc.h b/Firestore/Protos/objc/google/firestore/v1beta1/Document.pbobjc.h index 3c5bfb1..b960b00 100644 --- a/Firestore/Protos/objc/google/firestore/v1beta1/Document.pbobjc.h +++ b/Firestore/Protos/objc/google/firestore/v1beta1/Document.pbobjc.h @@ -95,7 +95,7 @@ typedef GPB_ENUM(GCFSDocument_FieldNumber) { * The map keys represent field names. * * A simple field name contains only characters `a` to `z`, `A` to `Z`, - * `0` to `9`, or `_`, and must not start with `0` to `9` or `_`. For example, + * `0` to `9`, or `_`, and must not start with `0` to `9`. For example, * `foo_bar_17`. * * Field names matching the regular expression `__.*__` are reserved. Reserved @@ -133,7 +133,7 @@ typedef GPB_ENUM(GCFSDocument_FieldNumber) { /** * Output only. The time at which the document was last changed. * - * This value is initally set to the `create_time` then increases + * This value is initially set to the `create_time` then increases * monotonically with each change to the document. It can also be * compared to values from other documents and the `read_time` of a query. **/ diff --git a/Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbobjc.h b/Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbobjc.h index 0acd8c0..11f55a9 100644 --- a/Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbobjc.h +++ b/Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbobjc.h @@ -642,10 +642,7 @@ typedef GPB_ENUM(GCFSCommitRequest_FieldNumber) { /** The number of items in @c writesArray without causing the array to be created. */ @property(nonatomic, readonly) NSUInteger writesArray_Count; -/** - * If non-empty, applies all writes in this transaction, and commits it. - * Otherwise, applies the writes as if they were in their own transaction. - **/ +/** If set, applies all writes in this transaction, and commits it. */ @property(nonatomic, readwrite, copy, null_resettable) NSData *transaction; @end diff --git a/Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbrpc.h b/Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbrpc.h index d7f127b..991c3f0 100644 --- a/Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbrpc.h +++ b/Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbrpc.h @@ -14,29 +14,57 @@ * limitations under the License. */ -#import "Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbobjc.h" +#if !GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO +#import "Firestore.pbobjc.h" +#endif #import <ProtoRPC/ProtoService.h> #import <ProtoRPC/ProtoRPC.h> #import <RxLibrary/GRXWriteable.h> #import <RxLibrary/GRXWriter.h> -#import "Firestore/Protos/objc/google/api/Annotations.pbobjc.h" -#import "Firestore/Protos/objc/google/firestore/v1beta1/Common.pbobjc.h" -#import "Firestore/Protos/objc/google/firestore/v1beta1/Document.pbobjc.h" -#import "Firestore/Protos/objc/google/firestore/v1beta1/Query.pbobjc.h" -#import "Firestore/Protos/objc/google/firestore/v1beta1/Write.pbobjc.h" -#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS - #import <Protobuf/Empty.pbobjc.h> -#else - #import "Empty.pbobjc.h" -#endif -#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS - #import <Protobuf/Timestamp.pbobjc.h> +#if GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO + @class GCFSBatchGetDocumentsRequest; + @class GCFSBatchGetDocumentsResponse; + @class GCFSBeginTransactionRequest; + @class GCFSBeginTransactionResponse; + @class GCFSCommitRequest; + @class GCFSCommitResponse; + @class GCFSCreateDocumentRequest; + @class GCFSDeleteDocumentRequest; + @class GCFSDocument; + @class GCFSGetDocumentRequest; + @class GCFSListCollectionIdsRequest; + @class GCFSListCollectionIdsResponse; + @class GCFSListDocumentsRequest; + @class GCFSListDocumentsResponse; + @class GCFSListenRequest; + @class GCFSListenResponse; + @class GCFSRollbackRequest; + @class GCFSRunQueryRequest; + @class GCFSRunQueryResponse; + @class GCFSUpdateDocumentRequest; + @class GCFSWriteRequest; + @class GCFSWriteResponse; + @class GPBEmpty; #else - #import "Timestamp.pbobjc.h" + #import "Annotations.pbobjc.h" + #import "Common.pbobjc.h" + #import "Document.pbobjc.h" + #import "Query.pbobjc.h" + #import "Write.pbobjc.h" + #if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS + #import <Protobuf/Empty.pbobjc.h> + #else + #import "Empty.pbobjc.h" + #endif + #if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS + #import <Protobuf/Timestamp.pbobjc.h> + #else + #import "Timestamp.pbobjc.h" + #endif + #import "Status.pbobjc.h" #endif -#import "Firestore/Protos/objc/google/rpc/Status.pbobjc.h" NS_ASSUME_NONNULL_BEGIN diff --git a/Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbrpc.m b/Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbrpc.m index eec4c9a..29359b6 100644 --- a/Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbrpc.m +++ b/Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbrpc.m @@ -14,10 +14,27 @@ * limitations under the License. */ -#import "Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbrpc.h" +#import "Firestore.pbrpc.h" +#import "Firestore.pbobjc.h" #import <ProtoRPC/ProtoRPC.h> #import <RxLibrary/GRXWriter+Immediate.h> +#import "Annotations.pbobjc.h" +#import "Common.pbobjc.h" +#import "Document.pbobjc.h" +#import "Query.pbobjc.h" +#import "Write.pbobjc.h" +#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS + #import <Protobuf/Empty.pbobjc.h> +#else + #import "Empty.pbobjc.h" +#endif +#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS + #import <Protobuf/Timestamp.pbobjc.h> +#else + #import "Timestamp.pbobjc.h" +#endif +#import "Status.pbobjc.h" @implementation GCFSFirestore diff --git a/Firestore/Protos/objc/google/firestore/v1beta1/Query.pbobjc.h b/Firestore/Protos/objc/google/firestore/v1beta1/Query.pbobjc.h index c2d80e7..7e32934 100644 --- a/Firestore/Protos/objc/google/firestore/v1beta1/Query.pbobjc.h +++ b/Firestore/Protos/objc/google/firestore/v1beta1/Query.pbobjc.h @@ -140,6 +140,9 @@ typedef GPB_ENUM(GCFSStructuredQuery_FieldFilter_Operator) { /** Equal. */ GCFSStructuredQuery_FieldFilter_Operator_Equal = 5, + + /** Contains. Requires that the field is an array. */ + GCFSStructuredQuery_FieldFilter_Operator_ArrayContains = 7, }; GPBEnumDescriptor *GCFSStructuredQuery_FieldFilter_Operator_EnumDescriptor(void); diff --git a/Firestore/Protos/objc/google/firestore/v1beta1/Query.pbobjc.m b/Firestore/Protos/objc/google/firestore/v1beta1/Query.pbobjc.m index 804a5d0..124930a 100644 --- a/Firestore/Protos/objc/google/firestore/v1beta1/Query.pbobjc.m +++ b/Firestore/Protos/objc/google/firestore/v1beta1/Query.pbobjc.m @@ -542,7 +542,7 @@ GPBEnumDescriptor *GCFSStructuredQuery_FieldFilter_Operator_EnumDescriptor(void) static const char *valueNames = "OperatorUnspecified\000LessThan\000LessThanOrE" "qual\000GreaterThan\000GreaterThanOrEqual\000Equa" - "l\000"; + "l\000ArrayContains\000"; static const int32_t values[] = { GCFSStructuredQuery_FieldFilter_Operator_OperatorUnspecified, GCFSStructuredQuery_FieldFilter_Operator_LessThan, @@ -550,6 +550,7 @@ GPBEnumDescriptor *GCFSStructuredQuery_FieldFilter_Operator_EnumDescriptor(void) GCFSStructuredQuery_FieldFilter_Operator_GreaterThan, GCFSStructuredQuery_FieldFilter_Operator_GreaterThanOrEqual, GCFSStructuredQuery_FieldFilter_Operator_Equal, + GCFSStructuredQuery_FieldFilter_Operator_ArrayContains, }; GPBEnumDescriptor *worker = [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GCFSStructuredQuery_FieldFilter_Operator) @@ -572,6 +573,7 @@ BOOL GCFSStructuredQuery_FieldFilter_Operator_IsValidValue(int32_t value__) { case GCFSStructuredQuery_FieldFilter_Operator_GreaterThan: case GCFSStructuredQuery_FieldFilter_Operator_GreaterThanOrEqual: case GCFSStructuredQuery_FieldFilter_Operator_Equal: + case GCFSStructuredQuery_FieldFilter_Operator_ArrayContains: return YES; default: return NO; diff --git a/Firestore/Protos/objc/google/firestore/v1beta1/Write.pbobjc.h b/Firestore/Protos/objc/google/firestore/v1beta1/Write.pbobjc.h index c3c4498..539bfa3 100644 --- a/Firestore/Protos/objc/google/firestore/v1beta1/Write.pbobjc.h +++ b/Firestore/Protos/objc/google/firestore/v1beta1/Write.pbobjc.h @@ -43,6 +43,7 @@ CF_EXTERN_C_BEGIN +@class GCFSArrayValue; @class GCFSDocument; @class GCFSDocumentMask; @class GCFSDocumentTransform; @@ -66,7 +67,10 @@ typedef GPB_ENUM(GCFSDocumentTransform_FieldTransform_ServerValue) { /** Unspecified. This value must not be used. */ GCFSDocumentTransform_FieldTransform_ServerValue_ServerValueUnspecified = 0, - /** The time at which the server processed the request. */ + /** + * The time at which the server processed the request, with millisecond + * precision. + **/ GCFSDocumentTransform_FieldTransform_ServerValue_RequestTime = 1, }; @@ -100,7 +104,6 @@ typedef GPB_ENUM(GCFSWrite_FieldNumber) { GCFSWrite_FieldNumber_Delete_p = 2, GCFSWrite_FieldNumber_UpdateMask = 3, GCFSWrite_FieldNumber_CurrentDocument = 4, - GCFSWrite_FieldNumber_Verify = 5, GCFSWrite_FieldNumber_Transform = 6, }; @@ -108,7 +111,6 @@ typedef GPB_ENUM(GCFSWrite_Operation_OneOfCase) { GCFSWrite_Operation_OneOfCase_GPBUnsetOneOfCase = 0, GCFSWrite_Operation_OneOfCase_Update = 1, GCFSWrite_Operation_OneOfCase_Delete_p = 2, - GCFSWrite_Operation_OneOfCase_Verify = 5, GCFSWrite_Operation_OneOfCase_Transform = 6, }; @@ -130,13 +132,6 @@ typedef GPB_ENUM(GCFSWrite_Operation_OneOfCase) { @property(nonatomic, readwrite, copy, null_resettable) NSString *delete_p; /** - * The name of a document on which to verify the `current_document` - * precondition. - * This only requires read access to the document. - **/ -@property(nonatomic, readwrite, copy, null_resettable) NSString *verify; - -/** * Applies a tranformation to a document. * At most one `transform` per document is allowed in a given request. * An `update` cannot follow a `transform` on the same document in a given @@ -148,9 +143,10 @@ typedef GPB_ENUM(GCFSWrite_Operation_OneOfCase) { * The fields to update in this write. * * This field can be set only when the operation is `update`. - * None of the field paths in the mask may contain a reserved name. - * If the document exists on the server and has fields not referenced in the - * mask, they are left unchanged. + * If the mask is not set for an `update` and the document exists, any + * existing data will be overwritten. + * If the mask is set and the document on the server has fields not covered by + * the mask, they are left unchanged. * Fields referenced in the mask, but not present in the input document, are * deleted from the document on the server. * The field paths in this mask must not contain a reserved field name. @@ -193,6 +189,7 @@ typedef GPB_ENUM(GCFSDocumentTransform_FieldNumber) { /** * The list of transformations to apply to the fields of the document, in * order. + * This must not be empty. **/ @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GCFSDocumentTransform_FieldTransform*> *fieldTransformsArray; /** The number of items in @c fieldTransformsArray without causing the array to be created. */ @@ -205,11 +202,15 @@ typedef GPB_ENUM(GCFSDocumentTransform_FieldNumber) { typedef GPB_ENUM(GCFSDocumentTransform_FieldTransform_FieldNumber) { GCFSDocumentTransform_FieldTransform_FieldNumber_FieldPath = 1, GCFSDocumentTransform_FieldTransform_FieldNumber_SetToServerValue = 2, + GCFSDocumentTransform_FieldTransform_FieldNumber_AppendMissingElements = 6, + GCFSDocumentTransform_FieldTransform_FieldNumber_RemoveAllFromArray_p = 7, }; typedef GPB_ENUM(GCFSDocumentTransform_FieldTransform_TransformType_OneOfCase) { GCFSDocumentTransform_FieldTransform_TransformType_OneOfCase_GPBUnsetOneOfCase = 0, GCFSDocumentTransform_FieldTransform_TransformType_OneOfCase_SetToServerValue = 2, + GCFSDocumentTransform_FieldTransform_TransformType_OneOfCase_AppendMissingElements = 6, + GCFSDocumentTransform_FieldTransform_TransformType_OneOfCase_RemoveAllFromArray_p = 7, }; /** @@ -229,6 +230,36 @@ typedef GPB_ENUM(GCFSDocumentTransform_FieldTransform_TransformType_OneOfCase) { /** Sets the field to the given server value. */ @property(nonatomic, readwrite) GCFSDocumentTransform_FieldTransform_ServerValue setToServerValue; +/** + * Append the given elements in order if they are not already present in + * the current field value. + * If the field is not an array, or if the field does not yet exist, it is + * first set to the empty array. + * + * Equivalent numbers of different types (e.g. 3L and 3.0) are + * considered equal when checking if a value is missing. + * NaN is equal to NaN, and Null is equal to Null. + * If the input contains multiple equivalent values, only the first will + * be considered. + * + * The corresponding transform_result will be the null value. + **/ +@property(nonatomic, readwrite, strong, null_resettable) GCFSArrayValue *appendMissingElements; + +/** + * Remove all of the given elements from the array in the field. + * If the field is not an array, or if the field does not yet exist, it is + * set to the empty array. + * + * Equivalent numbers of the different types (e.g. 3L and 3.0) are + * considered equal when deciding whether an element should be removed. + * NaN is equal to NaN, and Null is equal to Null. + * This will remove all equivalent values if there are duplicates. + * + * The corresponding transform_result will be the null value. + **/ +@property(nonatomic, readwrite, strong, null_resettable) GCFSArrayValue *removeAllFromArray_p; + @end /** diff --git a/Firestore/Protos/objc/google/firestore/v1beta1/Write.pbobjc.m b/Firestore/Protos/objc/google/firestore/v1beta1/Write.pbobjc.m index e6fd0f4..591e634 100644 --- a/Firestore/Protos/objc/google/firestore/v1beta1/Write.pbobjc.m +++ b/Firestore/Protos/objc/google/firestore/v1beta1/Write.pbobjc.m @@ -74,7 +74,6 @@ static GPBFileDescriptor *GCFSWriteRoot_FileDescriptor(void) { @dynamic operationOneOfCase; @dynamic update; @dynamic delete_p; -@dynamic verify; @dynamic transform; @dynamic hasUpdateMask, updateMask; @dynamic hasCurrentDocument, currentDocument; @@ -85,7 +84,6 @@ typedef struct GCFSWrite__storage_ { NSString *delete_p; GCFSDocumentMask *updateMask; GCFSPrecondition *currentDocument; - NSString *verify; GCFSDocumentTransform *transform; } GCFSWrite__storage_; @@ -132,15 +130,6 @@ typedef struct GCFSWrite__storage_ { .dataType = GPBDataTypeMessage, }, { - .name = "verify", - .dataTypeSpecific.className = NULL, - .number = GCFSWrite_FieldNumber_Verify, - .hasIndex = -1, - .offset = (uint32_t)offsetof(GCFSWrite__storage_, verify), - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - }, - { .name = "transform", .dataTypeSpecific.className = GPBStringifySymbol(GCFSDocumentTransform), .number = GCFSWrite_FieldNumber_Transform, @@ -238,11 +227,15 @@ typedef struct GCFSDocumentTransform__storage_ { @dynamic transformTypeOneOfCase; @dynamic fieldPath; @dynamic setToServerValue; +@dynamic appendMissingElements; +@dynamic removeAllFromArray_p; typedef struct GCFSDocumentTransform_FieldTransform__storage_ { uint32_t _has_storage_[2]; GCFSDocumentTransform_FieldTransform_ServerValue setToServerValue; NSString *fieldPath; + GCFSArrayValue *appendMissingElements; + GCFSArrayValue *removeAllFromArray_p; } GCFSDocumentTransform_FieldTransform__storage_; // This method is threadsafe because it is initially called @@ -269,6 +262,24 @@ typedef struct GCFSDocumentTransform_FieldTransform__storage_ { .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor), .dataType = GPBDataTypeEnum, }, + { + .name = "appendMissingElements", + .dataTypeSpecific.className = GPBStringifySymbol(GCFSArrayValue), + .number = GCFSDocumentTransform_FieldTransform_FieldNumber_AppendMissingElements, + .hasIndex = -1, + .offset = (uint32_t)offsetof(GCFSDocumentTransform_FieldTransform__storage_, appendMissingElements), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, + { + .name = "removeAllFromArray_p", + .dataTypeSpecific.className = GPBStringifySymbol(GCFSArrayValue), + .number = GCFSDocumentTransform_FieldTransform_FieldNumber_RemoveAllFromArray_p, + .hasIndex = -1, + .offset = (uint32_t)offsetof(GCFSDocumentTransform_FieldTransform__storage_, removeAllFromArray_p), + .flags = GPBFieldOptional, + .dataType = GPBDataTypeMessage, + }, }; GPBDescriptor *localDescriptor = [GPBDescriptor allocDescriptorForClass:[GCFSDocumentTransform_FieldTransform class] diff --git a/Firestore/Protos/protos/google/api/http.proto b/Firestore/Protos/protos/google/api/http.proto index 5f8538a..78d515d 100644 --- a/Firestore/Protos/protos/google/api/http.proto +++ b/Firestore/Protos/protos/google/api/http.proto @@ -1,4 +1,4 @@ -// Copyright 2016 Google Inc. +// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ option java_package = "com.google.api"; option objc_class_prefix = "GAPI"; -// Defines the HTTP configuration for a service. It contains a list of +// Defines the HTTP configuration for an API service. It contains a list of // [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method // to one or more HTTP REST API methods. message Http { @@ -32,14 +32,22 @@ message Http { // // **NOTE:** All service configuration rules follow "last one wins" order. repeated HttpRule rules = 1; + + // When set to true, URL path parmeters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; } // `HttpRule` defines the mapping of an RPC method to one or more HTTP -// REST APIs. The mapping determines what portions of the request -// message are populated from the path, query parameters, or body of -// the HTTP request. The mapping is typically specified as an -// `google.api.http` annotation, see "google/api/annotations.proto" -// for details. +// REST API methods. The mapping specifies how different portions of the RPC +// request message are mapped to URL path, URL query parameters, and +// HTTP request body. The mapping is typically specified as an +// `google.api.http` annotation on the RPC method, +// see "google/api/annotations.proto" for details. // // The mapping consists of a field specifying the path template and // method kind. The path template can refer to fields in the request @@ -87,6 +95,11 @@ message Http { // parameters. Assume the following definition of the request message: // // +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http).get = "/v1/messages/{message_id}"; +// } +// } // message GetMessageRequest { // message SubMessage { // string subfield = 1; @@ -199,7 +212,7 @@ message Http { // to the request message are as follows: // // 1. The `body` field specifies either `*` or a field path, or is -// omitted. If omitted, it assumes there is no HTTP body. +// omitted. If omitted, it indicates there is no HTTP request body. // 2. Leaf fields (recursive expansion of nested messages in the // request) can be classified into three types: // (a) Matched in the URL template. @@ -218,28 +231,34 @@ message Http { // FieldPath = IDENT { "." IDENT } ; // Verb = ":" LITERAL ; // -// The syntax `*` matches a single path segment. It follows the semantics of -// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String -// Expansion. +// The syntax `*` matches a single path segment. The syntax `**` matches zero +// or more path segments, which must be the last part of the path except the +// `Verb`. The syntax `LITERAL` matches literal text in the path. // -// The syntax `**` matches zero or more path segments. It follows the semantics -// of [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.3 Reserved -// Expansion. NOTE: it must be the last segment in the path except the Verb. -// -// The syntax `LITERAL` matches literal text in the URL path. -// -// The syntax `Variable` matches the entire path as specified by its template; -// this nested template must not contain further variables. If a variable +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable // matches a single path segment, its template may be omitted, e.g. `{var}` // is equivalent to `{var=*}`. // +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path, all characters +// except `[-_.~0-9a-zA-Z]` are percent-encoded. Such variables show up in the +// Discovery Document as `{var}`. +// +// If a variable contains one or more path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path, all +// characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. Such variables +// show up in the Discovery Document as `{+var}`. +// +// NOTE: While the single segment variable matches the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 +// Simple String Expansion, the multi segment variable **does not** match +// RFC 6570 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. +// // NOTE: the field paths in variables and in the `body` must not refer to // repeated fields or map fields. -// -// Use CustomHttpPattern to specify any HTTP method that is not included in the -// `pattern` field, such as HEAD, or "*" to leave the HTTP method unspecified for -// a given URL path rule. The wild-card rule is useful for services that provide -// content to Web (HTML) clients. message HttpRule { // Selects methods to which this rule applies. // @@ -265,7 +284,10 @@ message HttpRule { // Used for updating a resource. string patch = 6; - // Custom pattern is used for defining custom verbs. + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. CustomHttpPattern custom = 8; } diff --git a/Firestore/Protos/protos/google/firestore/v1beta1/common.proto b/Firestore/Protos/protos/google/firestore/v1beta1/common.proto index e624323..5ceb7b9 100644 --- a/Firestore/Protos/protos/google/firestore/v1beta1/common.proto +++ b/Firestore/Protos/protos/google/firestore/v1beta1/common.proto @@ -1,4 +1,4 @@ -// Copyright 2017 Google Inc. +// Copyright 2018 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ option java_multiple_files = true; option java_outer_classname = "CommonProto"; option java_package = "com.google.firestore.v1beta1"; option objc_class_prefix = "GCFS"; +option php_namespace = "Google\\Cloud\\Firestore\\V1beta1"; // A set of field paths on a document. diff --git a/Firestore/Protos/protos/google/firestore/v1beta1/document.proto b/Firestore/Protos/protos/google/firestore/v1beta1/document.proto index cf6001d..cd84c7a 100644 --- a/Firestore/Protos/protos/google/firestore/v1beta1/document.proto +++ b/Firestore/Protos/protos/google/firestore/v1beta1/document.proto @@ -1,4 +1,4 @@ -// Copyright 2017 Google Inc. +// Copyright 2018 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ option java_multiple_files = true; option java_outer_classname = "DocumentProto"; option java_package = "com.google.firestore.v1beta1"; option objc_class_prefix = "GCFS"; +option php_namespace = "Google\\Cloud\\Firestore\\V1beta1"; // A Firestore document. @@ -42,7 +43,7 @@ message Document { // The map keys represent field names. // // A simple field name contains only characters `a` to `z`, `A` to `Z`, - // `0` to `9`, or `_`, and must not start with `0` to `9` or `_`. For example, + // `0` to `9`, or `_`, and must not start with `0` to `9`. For example, // `foo_bar_17`. // // Field names matching the regular expression `__.*__` are reserved. Reserved @@ -72,7 +73,7 @@ message Document { // Output only. The time at which the document was last changed. // - // This value is initally set to the `create_time` then increases + // This value is initially set to the `create_time` then increases // monotonically with each change to the document. It can also be // compared to values from other documents and the `read_time` of a query. google.protobuf.Timestamp update_time = 4; diff --git a/Firestore/Protos/protos/google/firestore/v1beta1/firestore.proto b/Firestore/Protos/protos/google/firestore/v1beta1/firestore.proto index 3939caa..c7e8561 100644 --- a/Firestore/Protos/protos/google/firestore/v1beta1/firestore.proto +++ b/Firestore/Protos/protos/google/firestore/v1beta1/firestore.proto @@ -1,4 +1,4 @@ -// Copyright 2017 Google Inc. +// Copyright 2018 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -31,6 +31,7 @@ option java_multiple_files = true; option java_outer_classname = "FirestoreProto"; option java_package = "com.google.firestore.v1beta1"; option objc_class_prefix = "GCFS"; +option php_namespace = "Google\\Cloud\\Firestore\\V1beta1"; // Specification of the Firestore API. @@ -55,27 +56,39 @@ option objc_class_prefix = "GCFS"; service Firestore { // Gets a single document. rpc GetDocument(GetDocumentRequest) returns (Document) { - option (google.api.http) = { get: "/v1beta1/{name=projects/*/databases/*/documents/*/**}" }; + option (google.api.http) = { + get: "/v1beta1/{name=projects/*/databases/*/documents/*/**}" + }; } // Lists documents. rpc ListDocuments(ListDocumentsRequest) returns (ListDocumentsResponse) { - option (google.api.http) = { get: "/v1beta1/{parent=projects/*/databases/*/documents/*/**}/{collection_id}" }; + option (google.api.http) = { + get: "/v1beta1/{parent=projects/*/databases/*/documents/*/**}/{collection_id}" + }; } // Creates a new document. rpc CreateDocument(CreateDocumentRequest) returns (Document) { - option (google.api.http) = { post: "/v1beta1/{parent=projects/*/databases/*/documents/**}/{collection_id}" body: "document" }; + option (google.api.http) = { + post: "/v1beta1/{parent=projects/*/databases/*/documents/**}/{collection_id}" + body: "document" + }; } // Updates or inserts a document. rpc UpdateDocument(UpdateDocumentRequest) returns (Document) { - option (google.api.http) = { patch: "/v1beta1/{document.name=projects/*/databases/*/documents/*/**}" body: "document" }; + option (google.api.http) = { + patch: "/v1beta1/{document.name=projects/*/databases/*/documents/*/**}" + body: "document" + }; } // Deletes a document. rpc DeleteDocument(DeleteDocumentRequest) returns (google.protobuf.Empty) { - option (google.api.http) = { delete: "/v1beta1/{name=projects/*/databases/*/documents/*/**}" }; + option (google.api.http) = { + delete: "/v1beta1/{name=projects/*/databases/*/documents/*/**}" + }; } // Gets multiple documents. @@ -83,42 +96,74 @@ service Firestore { // Documents returned by this method are not guaranteed to be returned in the // same order that they were requested. rpc BatchGetDocuments(BatchGetDocumentsRequest) returns (stream BatchGetDocumentsResponse) { - option (google.api.http) = { post: "/v1beta1/{database=projects/*/databases/*}/documents:batchGet" body: "*" }; + option (google.api.http) = { + post: "/v1beta1/{database=projects/*/databases/*}/documents:batchGet" + body: "*" + }; } // Starts a new transaction. rpc BeginTransaction(BeginTransactionRequest) returns (BeginTransactionResponse) { - option (google.api.http) = { post: "/v1beta1/{database=projects/*/databases/*}/documents:beginTransaction" body: "*" }; + option (google.api.http) = { + post: "/v1beta1/{database=projects/*/databases/*}/documents:beginTransaction" + body: "*" + }; } // Commits a transaction, while optionally updating documents. rpc Commit(CommitRequest) returns (CommitResponse) { - option (google.api.http) = { post: "/v1beta1/{database=projects/*/databases/*}/documents:commit" body: "*" }; + option (google.api.http) = { + post: "/v1beta1/{database=projects/*/databases/*}/documents:commit" + body: "*" + }; } // Rolls back a transaction. rpc Rollback(RollbackRequest) returns (google.protobuf.Empty) { - option (google.api.http) = { post: "/v1beta1/{database=projects/*/databases/*}/documents:rollback" body: "*" }; + option (google.api.http) = { + post: "/v1beta1/{database=projects/*/databases/*}/documents:rollback" + body: "*" + }; } // Runs a query. rpc RunQuery(RunQueryRequest) returns (stream RunQueryResponse) { - option (google.api.http) = { post: "/v1beta1/{parent=projects/*/databases/*/documents}:runQuery" body: "*" }; + option (google.api.http) = { + post: "/v1beta1/{parent=projects/*/databases/*/documents}:runQuery" + body: "*" + additional_bindings { + post: "/v1beta1/{parent=projects/*/databases/*/documents/*/**}:runQuery" + body: "*" + } + }; } // Streams batches of document updates and deletes, in order. rpc Write(stream WriteRequest) returns (stream WriteResponse) { - option (google.api.http) = { post: "/v1beta1/{database=projects/*/databases/*}/documents:write" body: "*" }; + option (google.api.http) = { + post: "/v1beta1/{database=projects/*/databases/*}/documents:write" + body: "*" + }; } // Listens to changes. rpc Listen(stream ListenRequest) returns (stream ListenResponse) { - option (google.api.http) = { post: "/v1beta1/{database=projects/*/databases/*}/documents:listen" body: "*" }; + option (google.api.http) = { + post: "/v1beta1/{database=projects/*/databases/*}/documents:listen" + body: "*" + }; } // Lists all the collection IDs underneath a document. rpc ListCollectionIds(ListCollectionIdsRequest) returns (ListCollectionIdsResponse) { - option (google.api.http) = { post: "/v1beta1/{parent=projects/*/databases/*/documents}:listCollectionIds" body: "*" }; + option (google.api.http) = { + post: "/v1beta1/{parent=projects/*/databases/*/documents}:listCollectionIds" + body: "*" + additional_bindings { + post: "/v1beta1/{parent=projects/*/databases/*/documents/*/**}:listCollectionIds" + body: "*" + } + }; } } @@ -356,8 +401,7 @@ message CommitRequest { // Always executed atomically and in order. repeated Write writes = 2; - // If non-empty, applies all writes in this transaction, and commits it. - // Otherwise, applies the writes as if they were in their own transaction. + // If set, applies all writes in this transaction, and commits it. bytes transaction = 3; } diff --git a/Firestore/Protos/protos/google/firestore/v1beta1/query.proto b/Firestore/Protos/protos/google/firestore/v1beta1/query.proto index d19b022..5f5b0cf 100644 --- a/Firestore/Protos/protos/google/firestore/v1beta1/query.proto +++ b/Firestore/Protos/protos/google/firestore/v1beta1/query.proto @@ -1,4 +1,4 @@ -// Copyright 2017 Google Inc. +// Copyright 2018 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ option java_multiple_files = true; option java_outer_classname = "QueryProto"; option java_package = "com.google.firestore.v1beta1"; option objc_class_prefix = "GCFS"; +option php_namespace = "Google\\Cloud\\Firestore\\V1beta1"; // A Firestore query. @@ -98,6 +99,9 @@ message StructuredQuery { // Equal. EQUAL = 5; + + // Contains. Requires that the field is an array. + ARRAY_CONTAINS = 7; } // The field to filter by. diff --git a/Firestore/Protos/protos/google/firestore/v1beta1/write.proto b/Firestore/Protos/protos/google/firestore/v1beta1/write.proto index b6e9d5f..3f4732b 100644 --- a/Firestore/Protos/protos/google/firestore/v1beta1/write.proto +++ b/Firestore/Protos/protos/google/firestore/v1beta1/write.proto @@ -1,4 +1,4 @@ -// Copyright 2017 Google Inc. +// Copyright 2018 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ option java_multiple_files = true; option java_outer_classname = "WriteProto"; option java_package = "com.google.firestore.v1beta1"; option objc_class_prefix = "GCFS"; +option php_namespace = "Google\\Cloud\\Firestore\\V1beta1"; // A write on a document. @@ -40,11 +41,6 @@ message Write { // `projects/{project_id}/databases/{database_id}/documents/{document_path}`. string delete = 2; - // The name of a document on which to verify the `current_document` - // precondition. - // This only requires read access to the document. - string verify = 5; - // Applies a tranformation to a document. // At most one `transform` per document is allowed in a given request. // An `update` cannot follow a `transform` on the same document in a given @@ -55,9 +51,10 @@ message Write { // The fields to update in this write. // // This field can be set only when the operation is `update`. - // None of the field paths in the mask may contain a reserved name. - // If the document exists on the server and has fields not referenced in the - // mask, they are left unchanged. + // If the mask is not set for an `update` and the document exists, any + // existing data will be overwritten. + // If the mask is set and the document on the server has fields not covered by + // the mask, they are left unchanged. // Fields referenced in the mask, but not present in the input document, are // deleted from the document on the server. // The field paths in this mask must not contain a reserved field name. @@ -78,7 +75,8 @@ message DocumentTransform { // Unspecified. This value must not be used. SERVER_VALUE_UNSPECIFIED = 0; - // The time at which the server processed the request. + // The time at which the server processed the request, with millisecond + // precision. REQUEST_TIME = 1; } @@ -90,6 +88,32 @@ message DocumentTransform { oneof transform_type { // Sets the field to the given server value. ServerValue set_to_server_value = 2; + + // Append the given elements in order if they are not already present in + // the current field value. + // If the field is not an array, or if the field does not yet exist, it is + // first set to the empty array. + // + // Equivalent numbers of different types (e.g. 3L and 3.0) are + // considered equal when checking if a value is missing. + // NaN is equal to NaN, and Null is equal to Null. + // If the input contains multiple equivalent values, only the first will + // be considered. + // + // The corresponding transform_result will be the null value. + ArrayValue append_missing_elements = 6; + + // Remove all of the given elements from the array in the field. + // If the field is not an array, or if the field does not yet exist, it is + // set to the empty array. + // + // Equivalent numbers of the different types (e.g. 3L and 3.0) are + // considered equal when deciding whether an element should be removed. + // NaN is equal to NaN, and Null is equal to Null. + // This will remove all equivalent values if there are duplicates. + // + // The corresponding transform_result will be the null value. + ArrayValue remove_all_from_array = 7; } } @@ -98,6 +122,7 @@ message DocumentTransform { // The list of transformations to apply to the fields of the document, in // order. + // This must not be empty. repeated FieldTransform field_transforms = 2; } diff --git a/Firestore/Protos/protos/google/protobuf/any.proto b/Firestore/Protos/protos/google/protobuf/any.proto index c748667..4932942 100644 --- a/Firestore/Protos/protos/google/protobuf/any.proto +++ b/Firestore/Protos/protos/google/protobuf/any.proto @@ -120,17 +120,18 @@ option objc_class_prefix = "GPB"; // } // message Any { - // A URL/resource name whose content describes the type of the - // serialized protocol buffer message. + // A URL/resource name that uniquely identifies the type of the serialized + // protocol buffer message. The last segment of the URL's path must represent + // the fully qualified name of the type (as in + // `path/google.protobuf.Duration`). The name should be in a canonical form + // (e.g., leading "." is not accepted). // - // For URLs which use the scheme `http`, `https`, or no scheme, the - // following restrictions and interpretations apply: + // In practice, teams usually precompile into the binary all types that they + // expect it to use in the context of Any. However, for URLs which use the + // scheme `http`, `https`, or no scheme, one can optionally set up a type + // server that maps type URLs to message definitions as follows: // // * If no scheme is provided, `https` is assumed. - // * The last segment of the URL's path must represent the fully - // qualified name of the type (as in `path/google.protobuf.Duration`). - // The name should be in a canonical form (e.g., leading "." is - // not accepted). // * An HTTP GET on the URL must yield a [google.protobuf.Type][] // value in binary format, or produce an error. // * Applications are allowed to cache lookup results based on the @@ -139,6 +140,10 @@ message Any { // on changes to types. (Use versioned type names to manage // breaking changes.) // + // Note: this functionality is not currently available in the official + // protobuf release, and it is not used for type URLs beginning with + // type.googleapis.com. + // // Schemes other than `http`, `https` (or the empty scheme) might be // used with implementation specific semantics. // diff --git a/Firestore/Protos/protos/google/protobuf/timestamp.proto b/Firestore/Protos/protos/google/protobuf/timestamp.proto index b7cbd17..eafb3fa 100644 --- a/Firestore/Protos/protos/google/protobuf/timestamp.proto +++ b/Firestore/Protos/protos/google/protobuf/timestamp.proto @@ -103,7 +103,9 @@ option objc_class_prefix = "GPB"; // {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional // seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), // are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone -// is required, though only UTC (as indicated by "Z") is presently supported. +// is required. A proto3 JSON serializer should always use UTC (as indicated by +// "Z") when printing the Timestamp type and a proto3 JSON parser should be +// able to accept both UTC and other timezones (as indicated by an offset). // // For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past // 01:30 UTC on January 15, 2017. @@ -114,8 +116,8 @@ option objc_class_prefix = "GPB"; // to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) // with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one // can use the Joda Time's [`ISODateTimeFormat.dateTime()`]( -// http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()) -// to obtain a formatter capable of generating timestamps in this format. +// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime-- +// ) to obtain a formatter capable of generating timestamps in this format. // // message Timestamp { diff --git a/Firestore/Source/API/FIRDocumentReference.mm b/Firestore/Source/API/FIRDocumentReference.mm index cc52d45..9fb4541 100644 --- a/Firestore/Source/API/FIRDocumentReference.mm +++ b/Firestore/Source/API/FIRDocumentReference.mm @@ -16,11 +16,11 @@ #import "FIRDocumentReference.h" +#import <GRPCClient/GRPCCall.h> + #include <memory> #include <utility> -#import <GRPCClient/GRPCCall.h> - #import "FIRFirestoreErrors.h" #import "FIRSnapshotMetadata.h" #import "Firestore/Source/API/FIRCollectionReference+Internal.h" @@ -41,11 +41,13 @@ #import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/precondition.h" #include "Firestore/core/src/firebase/firestore/model/resource_path.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; using firebase::firestore::model::DocumentKey; +using firebase::firestore::model::Precondition; using firebase::firestore::model::ResourcePath; NS_ASSUME_NONNULL_BEGIN @@ -168,7 +170,7 @@ NS_ASSUME_NONNULL_BEGIN ? [self.firestore.dataConverter parsedMergeData:documentData] : [self.firestore.dataConverter parsedSetData:documentData]; return [self.firestore.client - writeMutations:[parsed mutationsWithKey:self.key precondition:[FSTPrecondition none]] + writeMutations:[parsed mutationsWithKey:self.key precondition:Precondition::None()] completion:completion]; } @@ -180,8 +182,7 @@ NS_ASSUME_NONNULL_BEGIN completion:(nullable void (^)(NSError *_Nullable error))completion { FSTParsedUpdateData *parsed = [self.firestore.dataConverter parsedUpdateData:fields]; return [self.firestore.client - writeMutations:[parsed mutationsWithKey:self.key - precondition:[FSTPrecondition preconditionWithExists:YES]] + writeMutations:[parsed mutationsWithKey:self.key precondition:Precondition::Exists(true)] completion:completion]; } @@ -191,7 +192,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)deleteDocumentWithCompletion:(nullable void (^)(NSError *_Nullable error))completion { FSTDeleteMutation *mutation = - [[FSTDeleteMutation alloc] initWithKey:self.key precondition:[FSTPrecondition none]]; + [[FSTDeleteMutation alloc] initWithKey:self.key precondition:Precondition::None()]; return [self.firestore.client writeMutations:@[ mutation ] completion:completion]; } diff --git a/Firestore/Source/API/FIRFieldPath.mm b/Firestore/Source/API/FIRFieldPath.mm index c651160..d0d8714 100644 --- a/Firestore/Source/API/FIRFieldPath.mm +++ b/Firestore/Source/API/FIRFieldPath.mm @@ -14,13 +14,14 @@ * limitations under the License. */ -#import "Firestore/Source/API/FIRFieldPath+Internal.h" +#import "FIRFieldPath.h" #include <functional> #include <string> #include <utility> #include <vector> +#import "Firestore/Source/API/FIRFieldPath+Internal.h" #import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/model/field_path.h" diff --git a/Firestore/Source/API/FIRFieldValue+Internal.h b/Firestore/Source/API/FIRFieldValue+Internal.h index 1b4a99c..1618cd4 100644 --- a/Firestore/Source/API/FIRFieldValue+Internal.h +++ b/Firestore/Source/API/FIRFieldValue+Internal.h @@ -18,6 +18,14 @@ NS_ASSUME_NONNULL_BEGIN +@interface FIRFieldValue (Internal) +/** + * The method name (e.g. "FieldValue.delete()") that was used to create this FIRFieldValue + * instance, for use in error messages, etc. + */ +@property(nonatomic, strong, readonly) NSString *methodName; +@end + /** * FIRFieldValue class for field deletes. Exposed internally so code can do isKindOfClass checks on * it. @@ -34,4 +42,16 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; @end +/** FIRFieldValue class for array unions. */ +@interface FSTArrayUnionFieldValue : FIRFieldValue +- (instancetype)init NS_UNAVAILABLE; +@property(strong, nonatomic, readonly) NSArray<id> *elements; +@end + +/** FIRFieldValue class for array removes. */ +@interface FSTArrayRemoveFieldValue : FIRFieldValue +- (instancetype)init NS_UNAVAILABLE; +@property(strong, nonatomic, readonly) NSArray<id> *elements; +@end + NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/API/FIRFieldValue.mm b/Firestore/Source/API/FIRFieldValue.mm index 7ae4fb0..e0ed8c7 100644 --- a/Firestore/Source/API/FIRFieldValue.mm +++ b/Firestore/Source/API/FIRFieldValue.mm @@ -46,6 +46,10 @@ NS_ASSUME_NONNULL_BEGIN return sharedInstance; } +- (NSString *)methodName { + return @"FieldValue.delete()"; +} + @end #pragma mark - FSTServerTimestampFieldValue @@ -72,6 +76,40 @@ NS_ASSUME_NONNULL_BEGIN return sharedInstance; } +- (NSString *)methodName { + return @"FieldValue.serverTimestamp()"; +} + +@end + +#pragma mark - FSTArrayUnionFieldValue + +@interface FSTArrayUnionFieldValue () +- (instancetype)initWithElements:(NSArray<id> *)elements; +@end + +@implementation FSTArrayUnionFieldValue +- (instancetype)initWithElements:(NSArray<id> *)elements { + if (self = [super initPrivate]) { + _elements = elements; + } + return self; +} +@end + +#pragma mark - FSTArrayRemoveFieldValue + +@interface FSTArrayRemoveFieldValue () +- (instancetype)initWithElements:(NSArray<id> *)elements; +@end + +@implementation FSTArrayRemoveFieldValue +- (instancetype)initWithElements:(NSArray<id> *)elements { + if (self = [super initPrivate]) { + _elements = elements; + } + return self; +} @end #pragma mark - FIRFieldValue @@ -91,6 +129,14 @@ NS_ASSUME_NONNULL_BEGIN return [FSTServerTimestampFieldValue serverTimestampFieldValue]; } ++ (instancetype)fieldValueForArrayUnion:(NSArray<id> *)elements { + return [[FSTArrayUnionFieldValue alloc] initWithElements:elements]; +} + ++ (instancetype)fieldValueForArrayRemove:(NSArray<id> *)elements { + return [[FSTArrayRemoveFieldValue alloc] initWithElements:elements]; +} + @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/API/FIRFirestore.mm b/Firestore/Source/API/FIRFirestore.mm index 45d67cf..fe461d6 100644 --- a/Firestore/Source/API/FIRFirestore.mm +++ b/Firestore/Source/API/FIRFirestore.mm @@ -16,14 +16,14 @@ #import "FIRFirestore.h" -#include <memory> -#include <utility> - #import <FirebaseCore/FIRApp.h> #import <FirebaseCore/FIRAppInternal.h> #import <FirebaseCore/FIRLogger.h> #import <FirebaseCore/FIROptions.h> +#include <memory> +#include <utility> + #import "FIRFirestoreSettings.h" #import "Firestore/Source/API/FIRCollectionReference+Internal.h" #import "Firestore/Source/API/FIRDocumentReference+Internal.h" @@ -31,7 +31,6 @@ #import "Firestore/Source/API/FIRTransaction+Internal.h" #import "Firestore/Source/API/FIRWriteBatch+Internal.h" #import "Firestore/Source/API/FSTUserDataConverter.h" - #import "Firestore/Source/Core/FSTFirestoreClient.h" #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTDispatchQueue.h" diff --git a/Firestore/Source/API/FIRFirestoreVersion.mm b/Firestore/Source/API/FIRFirestoreVersion.mm index b1fe480..8ebe814 100644 --- a/Firestore/Source/API/FIRFirestoreVersion.mm +++ b/Firestore/Source/API/FIRFirestoreVersion.mm @@ -14,6 +14,8 @@ * limitations under the License. */ +#import "Firestore/Source/API/FIRFirestoreVersion.h" + #ifndef FIRFirestore_VERSION #error "FIRFirestore_VERSION is not defined: add -DFIRFirestore_VERSION=... to the build invocation" #endif diff --git a/Firestore/Source/API/FIRWriteBatch.mm b/Firestore/Source/API/FIRWriteBatch.mm index b1cfa09..df6e5e0 100644 --- a/Firestore/Source/API/FIRWriteBatch.mm +++ b/Firestore/Source/API/FIRWriteBatch.mm @@ -24,6 +24,10 @@ #import "Firestore/Source/Model/FSTMutation.h" #import "Firestore/Source/Util/FSTUsageValidation.h" +#include "Firestore/core/src/firebase/firestore/model/precondition.h" + +using firebase::firestore::model::Precondition; + NS_ASSUME_NONNULL_BEGIN #pragma mark - FIRWriteBatch @@ -69,8 +73,8 @@ NS_ASSUME_NONNULL_BEGIN [self validateReference:document]; FSTParsedSetData *parsed = options.isMerge ? [self.firestore.dataConverter parsedMergeData:data] : [self.firestore.dataConverter parsedSetData:data]; - [self.mutations addObjectsFromArray:[parsed mutationsWithKey:document.key - precondition:[FSTPrecondition none]]]; + [self.mutations + addObjectsFromArray:[parsed mutationsWithKey:document.key precondition:Precondition::None()]]; return self; } @@ -79,9 +83,8 @@ NS_ASSUME_NONNULL_BEGIN [self verifyNotCommitted]; [self validateReference:document]; FSTParsedUpdateData *parsed = [self.firestore.dataConverter parsedUpdateData:fields]; - [self.mutations - addObjectsFromArray:[parsed mutationsWithKey:document.key - precondition:[FSTPrecondition preconditionWithExists:YES]]]; + [self.mutations addObjectsFromArray:[parsed mutationsWithKey:document.key + precondition:Precondition::Exists(true)]]; return self; } @@ -89,7 +92,7 @@ NS_ASSUME_NONNULL_BEGIN [self verifyNotCommitted]; [self validateReference:document]; [self.mutations addObject:[[FSTDeleteMutation alloc] initWithKey:document.key - precondition:[FSTPrecondition none]]]; + precondition:Precondition::None()]]; return self; } diff --git a/Firestore/Source/API/FSTUserDataConverter.h b/Firestore/Source/API/FSTUserDataConverter.h index 3b178be..ea20b3e 100644 --- a/Firestore/Source/API/FSTUserDataConverter.h +++ b/Firestore/Source/API/FSTUserDataConverter.h @@ -16,16 +16,18 @@ #import <Foundation/Foundation.h> +#include <vector> + #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/field_mask.h" +#include "Firestore/core/src/firebase/firestore/model/field_transform.h" +#include "Firestore/core/src/firebase/firestore/model/precondition.h" @class FIRSetOptions; @class FSTObjectValue; -@class FSTFieldMask; @class FSTFieldValue; -@class FSTFieldTransform; @class FSTMutation; -@class FSTPrecondition; @class FSTSnapshotVersion; NS_ASSUME_NONNULL_BEGIN @@ -36,20 +38,28 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; - (instancetype)initWithData:(FSTObjectValue *)data - fieldMask:(nullable FSTFieldMask *)fieldMask - fieldTransforms:(NSArray<FSTFieldTransform *> *)fieldTransforms + fieldTransforms: + (std::vector<firebase::firestore::model::FieldTransform>)fieldTransforms + NS_DESIGNATED_INITIALIZER; + +- (instancetype)initWithData:(FSTObjectValue *)data + fieldMask:(firebase::firestore::model::FieldMask)fieldMask + fieldTransforms: + (std::vector<firebase::firestore::model::FieldTransform>)fieldTransforms NS_DESIGNATED_INITIALIZER; +- (const std::vector<firebase::firestore::model::FieldTransform> &)fieldTransforms; + @property(nonatomic, strong, readonly) FSTObjectValue *data; -@property(nonatomic, strong, readonly, nullable) FSTFieldMask *fieldMask; -@property(nonatomic, strong, readonly) NSArray<FSTFieldTransform *> *fieldTransforms; +@property(nonatomic, assign, readonly) BOOL isPatch; /** * Converts the parsed document data into 1 or 2 mutations (depending on whether there are any * field transforms) using the specified document key and precondition. */ - (NSArray<FSTMutation *> *)mutationsWithKey:(const firebase::firestore::model::DocumentKey &)key - precondition:(FSTPrecondition *)precondition; + precondition: + (const firebase::firestore::model::Precondition &)precondition; @end @@ -59,20 +69,23 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; - (instancetype)initWithData:(FSTObjectValue *)data - fieldMask:(FSTFieldMask *)fieldMask - fieldTransforms:(NSArray<FSTFieldTransform *> *)fieldTransforms + fieldMask:(firebase::firestore::model::FieldMask)fieldMask + fieldTransforms: + (std::vector<firebase::firestore::model::FieldTransform>)fieldTransforms NS_DESIGNATED_INITIALIZER; +- (const firebase::firestore::model::FieldMask &)fieldMask; +- (const std::vector<firebase::firestore::model::FieldTransform> &)fieldTransforms; + @property(nonatomic, strong, readonly) FSTObjectValue *data; -@property(nonatomic, strong, readonly) FSTFieldMask *fieldMask; -@property(nonatomic, strong, readonly) NSArray<FSTFieldTransform *> *fieldTransforms; /** * Converts the parsed update data into 1 or 2 mutations (depending on whether there are any * field transforms) using the specified document key and precondition. */ - (NSArray<FSTMutation *> *)mutationsWithKey:(const firebase::firestore::model::DocumentKey &)key - precondition:(FSTPrecondition *)precondition; + precondition: + (const firebase::firestore::model::Precondition &)precondition; @end diff --git a/Firestore/Source/API/FSTUserDataConverter.mm b/Firestore/Source/API/FSTUserDataConverter.mm index 7ee16de..90d68d8 100644 --- a/Firestore/Source/API/FSTUserDataConverter.mm +++ b/Firestore/Source/API/FSTUserDataConverter.mm @@ -35,14 +35,24 @@ #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/field_mask.h" #include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "Firestore/core/src/firebase/firestore/model/field_transform.h" +#include "Firestore/core/src/firebase/firestore/model/precondition.h" +#include "Firestore/core/src/firebase/firestore/model/transform_operations.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" #include "absl/memory/memory.h" namespace util = firebase::firestore::util; +using firebase::firestore::model::ArrayTransform; using firebase::firestore::model::DatabaseId; using firebase::firestore::model::DocumentKey; +using firebase::firestore::model::FieldMask; using firebase::firestore::model::FieldPath; +using firebase::firestore::model::FieldTransform; +using firebase::firestore::model::Precondition; +using firebase::firestore::model::ServerTimestampTransform; +using firebase::firestore::model::TransformOperation; NS_ASSUME_NONNULL_BEGIN @@ -50,25 +60,45 @@ static NSString *const RESERVED_FIELD_DESIGNATOR = @"__"; #pragma mark - FSTParsedSetData -@implementation FSTParsedSetData +@implementation FSTParsedSetData { + FieldMask _fieldMask; + std::vector<FieldTransform> _fieldTransforms; +} + - (instancetype)initWithData:(FSTObjectValue *)data - fieldMask:(nullable FSTFieldMask *)fieldMask - fieldTransforms:(NSArray<FSTFieldTransform *> *)fieldTransforms { + fieldTransforms:(std::vector<FieldTransform>)fieldTransforms { self = [super init]; if (self) { _data = data; - _fieldMask = fieldMask; - _fieldTransforms = fieldTransforms; + _fieldTransforms = std::move(fieldTransforms); + _isPatch = NO; } return self; } +- (instancetype)initWithData:(FSTObjectValue *)data + fieldMask:(FieldMask)fieldMask + fieldTransforms:(std::vector<FieldTransform>)fieldTransforms { + self = [super init]; + if (self) { + _data = data; + _fieldMask = std::move(fieldMask); + _fieldTransforms = std::move(fieldTransforms); + _isPatch = YES; + } + return self; +} + +- (const std::vector<FieldTransform> &)fieldTransforms { + return _fieldTransforms; +} + - (NSArray<FSTMutation *> *)mutationsWithKey:(const DocumentKey &)key - precondition:(FSTPrecondition *)precondition { + precondition:(const Precondition &)precondition { NSMutableArray<FSTMutation *> *mutations = [NSMutableArray array]; - if (self.fieldMask) { + if (self.isPatch) { [mutations addObject:[[FSTPatchMutation alloc] initWithKey:key - fieldMask:self.fieldMask + fieldMask:_fieldMask value:self.data precondition:precondition]]; } else { @@ -76,7 +106,7 @@ static NSString *const RESERVED_FIELD_DESIGNATOR = @"__"; value:self.data precondition:precondition]]; } - if (self.fieldTransforms.count > 0) { + if (!self.fieldTransforms.empty()) { [mutations addObject:[[FSTTransformMutation alloc] initWithKey:key fieldTransforms:self.fieldTransforms]]; } @@ -87,33 +117,45 @@ static NSString *const RESERVED_FIELD_DESIGNATOR = @"__"; #pragma mark - FSTParsedUpdateData -@implementation FSTParsedUpdateData +@implementation FSTParsedUpdateData { + FieldMask _fieldMask; + std::vector<FieldTransform> _fieldTransforms; +} + - (instancetype)initWithData:(FSTObjectValue *)data - fieldMask:(FSTFieldMask *)fieldMask - fieldTransforms:(NSArray<FSTFieldTransform *> *)fieldTransforms { + fieldMask:(FieldMask)fieldMask + fieldTransforms:(std::vector<FieldTransform>)fieldTransforms { self = [super init]; if (self) { _data = data; - _fieldMask = fieldMask; - _fieldTransforms = fieldTransforms; + _fieldMask = std::move(fieldMask); + _fieldTransforms = std::move(fieldTransforms); } return self; } - (NSArray<FSTMutation *> *)mutationsWithKey:(const DocumentKey &)key - precondition:(FSTPrecondition *)precondition { + precondition:(const Precondition &)precondition { NSMutableArray<FSTMutation *> *mutations = [NSMutableArray array]; [mutations addObject:[[FSTPatchMutation alloc] initWithKey:key fieldMask:self.fieldMask value:self.data precondition:precondition]]; - if (self.fieldTransforms.count > 0) { + if (!self.fieldTransforms.empty()) { [mutations addObject:[[FSTTransformMutation alloc] initWithKey:key fieldTransforms:self.fieldTransforms]]; } return mutations; } +- (const firebase::firestore::model::FieldMask &)fieldMask { + return _fieldMask; +} + +- (const std::vector<FieldTransform> &)fieldTransforms { + return _fieldTransforms; +} + @end /** @@ -124,7 +166,11 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { FSTUserDataSourceSet, FSTUserDataSourceMergeSet, FSTUserDataSourceUpdate, - FSTUserDataSourceQueryValue, // from a where clause or cursor bound. + /** + * Indicates the source is a where clause, cursor bound, arrayUnion() element, etc. In particular, + * this will result in [FSTParseContext isWrite] returning NO. + */ + FSTUserDataSourceArgument, }; #pragma mark - FSTParseContext @@ -142,7 +188,6 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { * conditions apply during parsing and providing better error messages. */ @property(nonatomic, assign) FSTUserDataSource dataSource; -@property(nonatomic, strong, readonly) NSMutableArray<FSTFieldTransform *> *fieldTransforms; - (instancetype)init NS_UNAVAILABLE; /** @@ -160,7 +205,7 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { - (instancetype)initWithSource:(FSTUserDataSource)dataSource path:(std::unique_ptr<FieldPath>)path arrayElement:(BOOL)arrayElement - fieldTransforms:(NSMutableArray<FSTFieldTransform *> *)fieldTransforms + fieldTransforms:(std::shared_ptr<std::vector<FieldTransform>>)fieldTransforms fieldMask:(std::shared_ptr<std::vector<FieldPath>>)fieldMask NS_DESIGNATED_INITIALIZER; @@ -178,16 +223,22 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { - (void)appendToFieldMaskWithFieldPath:(FieldPath)fieldPath; +- (const std::vector<FieldTransform> *)fieldTransforms; + +- (void)appendToFieldTransformsWithFieldPath:(FieldPath)fieldPath + transformOperation: + (std::unique_ptr<TransformOperation>)transformOperation; @end @implementation FSTParseContext { /** The current path being parsed. */ // TODO(b/34871131): path should never be nullptr, but we don't support array paths right now. std::unique_ptr<FieldPath> _path; - // _fieldMask is shared across all active context objects to accumulate the result. For example, - // the result of calling any of contextForField, contextForFieldPath and contextForArrayIndex - // shares the ownership of _fieldMask. + // _fieldMask and _fieldTransforms are shared across all active context objects to accumulate the + // result. For example, the result of calling any of contextForField, contextForFieldPath and + // contextForArrayIndex shares the ownership of _fieldMask and _fieldTransforms. std::shared_ptr<std::vector<FieldPath>> _fieldMask; + std::shared_ptr<std::vector<FieldTransform>> _fieldTransforms; } + (instancetype)contextWithSource:(FSTUserDataSource)dataSource @@ -196,7 +247,7 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { [[FSTParseContext alloc] initWithSource:dataSource path:std::move(path) arrayElement:NO - fieldTransforms:[NSMutableArray array] + fieldTransforms:std::make_shared<std::vector<FieldTransform>>() fieldMask:std::make_shared<std::vector<FieldPath>>()]; [context validatePath]; return context; @@ -205,13 +256,13 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { - (instancetype)initWithSource:(FSTUserDataSource)dataSource path:(std::unique_ptr<FieldPath>)path arrayElement:(BOOL)arrayElement - fieldTransforms:(NSMutableArray<FSTFieldTransform *> *)fieldTransforms + fieldTransforms:(std::shared_ptr<std::vector<FieldTransform>>)fieldTransforms fieldMask:(std::shared_ptr<std::vector<FieldPath>>)fieldMask { if (self = [super init]) { _dataSource = dataSource; _path = std::move(path); _arrayElement = arrayElement; - _fieldTransforms = fieldTransforms; + _fieldTransforms = std::move(fieldTransforms); _fieldMask = std::move(fieldMask); } return self; @@ -225,7 +276,7 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { FSTParseContext *context = [[FSTParseContext alloc] initWithSource:self.dataSource path:std::move(path) arrayElement:NO - fieldTransforms:self.fieldTransforms + fieldTransforms:_fieldTransforms fieldMask:_fieldMask]; [context validatePathSegment:fieldName]; return context; @@ -239,7 +290,7 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { FSTParseContext *context = [[FSTParseContext alloc] initWithSource:self.dataSource path:std::move(path) arrayElement:NO - fieldTransforms:self.fieldTransforms + fieldTransforms:_fieldTransforms fieldMask:_fieldMask]; [context validatePath]; return context; @@ -250,7 +301,7 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { return [[FSTParseContext alloc] initWithSource:self.dataSource path:nil arrayElement:YES - fieldTransforms:self.fieldTransforms + fieldTransforms:_fieldTransforms fieldMask:_fieldMask]; } @@ -272,7 +323,7 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { case FSTUserDataSourceMergeSet: // Falls through. case FSTUserDataSourceUpdate: return YES; - case FSTUserDataSourceQueryValue: + case FSTUserDataSourceArgument: return NO; default: FSTThrowInvalidArgument(@"Unexpected case for FSTUserDataSource: %d", self.dataSource); @@ -309,6 +360,16 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { _fieldMask->push_back(std::move(fieldPath)); } +- (const std::vector<FieldTransform> *)fieldTransforms { + return _fieldTransforms.get(); +} + +- (void)appendToFieldTransformsWithFieldPath:(FieldPath)fieldPath + transformOperation: + (std::unique_ptr<TransformOperation>)transformOperation { + _fieldTransforms->emplace_back(std::move(fieldPath), std::move(transformOperation)); +} + @end #pragma mark - FSTDocumentKeyReference @@ -364,10 +425,9 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { path:absl::make_unique<FieldPath>(FieldPath::EmptyPath())]; FSTObjectValue *updateData = (FSTObjectValue *)[self parseData:input context:context]; - return [[FSTParsedSetData alloc] - initWithData:updateData - fieldMask:[[FSTFieldMask alloc] initWithFields:*context.fieldMask] - fieldTransforms:context.fieldTransforms]; + return [[FSTParsedSetData alloc] initWithData:updateData + fieldMask:FieldMask{*context.fieldMask} + fieldTransforms:*context.fieldTransforms]; } - (FSTParsedSetData *)parsedSetData:(id)input { @@ -382,9 +442,8 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { path:absl::make_unique<FieldPath>(FieldPath::EmptyPath())]; FSTObjectValue *updateData = (FSTObjectValue *)[self parseData:input context:context]; - return [[FSTParsedSetData alloc] initWithData:updateData - fieldMask:nil - fieldTransforms:context.fieldTransforms]; + return + [[FSTParsedSetData alloc] initWithData:updateData fieldTransforms:*context.fieldTransforms]; } - (FSTParsedUpdateData *)parsedUpdateData:(id)input { @@ -428,19 +487,18 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { } }]; - FSTFieldMask *mask = [[FSTFieldMask alloc] initWithFields:fieldMaskPaths]; return [[FSTParsedUpdateData alloc] initWithData:updateData - fieldMask:mask - fieldTransforms:context.fieldTransforms]; + fieldMask:FieldMask{fieldMaskPaths} + fieldTransforms:*context.fieldTransforms]; } - (FSTFieldValue *)parsedQueryValue:(id)input { FSTParseContext *context = - [FSTParseContext contextWithSource:FSTUserDataSourceQueryValue + [FSTParseContext contextWithSource:FSTUserDataSourceArgument path:absl::make_unique<FieldPath>(FieldPath::EmptyPath())]; FSTFieldValue *_Nullable parsed = [self parseData:input context:context]; FSTAssert(parsed, @"Parsed data should not be nil."); - FSTAssert(context.fieldTransforms.count == 0, @"Field transforms should have been disallowed."); + FSTAssert(context.fieldTransforms->empty(), @"Field transforms should have been disallowed."); return parsed; } @@ -456,64 +514,128 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { */ - (nullable FSTFieldValue *)parseData:(id)input context:(FSTParseContext *)context { input = self.preConverter(input); - if ([input isKindOfClass:[NSArray class]]) { - // TODO(b/34871131): Include the path containing the array in the error message. - if (context.isArrayElement) { - FSTThrowInvalidArgument(@"Nested arrays are not supported"); - } - NSArray *array = input; - NSMutableArray<FSTFieldValue *> *result = [NSMutableArray arrayWithCapacity:array.count]; - [array enumerateObjectsUsingBlock:^(id entry, NSUInteger idx, BOOL *stop) { - FSTFieldValue *_Nullable parsedEntry = - [self parseData:entry context:[context contextForArrayIndex:idx]]; - if (!parsedEntry) { - // Just include nulls in the array for fields being replaced with a sentinel. - parsedEntry = [FSTNullValue nullValue]; - } - [result addObject:parsedEntry]; - }]; + if ([input isKindOfClass:[NSDictionary class]]) { + return [self parseDictionary:(NSDictionary *)input context:context]; + } else { // If context.path is nil we are already inside an array and we don't support field mask paths // more granular than the top-level array. if (context.path) { [context appendToFieldMaskWithFieldPath:*context.path]; } - return [[FSTArrayValue alloc] initWithValueNoCopy:result]; - } else if ([input isKindOfClass:[NSDictionary class]]) { - NSDictionary *dict = input; - NSMutableDictionary<NSString *, FSTFieldValue *> *result = - [NSMutableDictionary dictionaryWithCapacity:dict.count]; - [dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) { - FSTFieldValue *_Nullable parsedValue = - [self parseData:value context:[context contextForField:key]]; - if (parsedValue) { - result[key] = parsedValue; + if ([input isKindOfClass:[NSArray class]]) { + // TODO(b/34871131): Include the path containing the array in the error message. + if (context.isArrayElement) { + FSTThrowInvalidArgument(@"Nested arrays are not supported"); } - }]; - return [[FSTObjectValue alloc] initWithDictionary:result]; + return [self parseArray:(NSArray *)input context:context]; + } else if ([input isKindOfClass:[FIRFieldValue class]]) { + // parseSentinelFieldValue may add an FSTFieldTransform, but we return nil since nothing + // should be included in the actual parsed data. + [self parseSentinelFieldValue:(FIRFieldValue *)input context:context]; + return nil; + } else { + return [self parseScalarValue:input context:context]; + } + } +} - } else { - // If context.path is null, we are inside an array and we should have already added the root of - // the array to the field mask. - if (context.path) { - [context appendToFieldMaskWithFieldPath:*context.path]; +- (FSTFieldValue *)parseDictionary:(NSDictionary *)dict context:(FSTParseContext *)context { + NSMutableDictionary<NSString *, FSTFieldValue *> *result = + [NSMutableDictionary dictionaryWithCapacity:dict.count]; + [dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) { + FSTFieldValue *_Nullable parsedValue = + [self parseData:value context:[context contextForField:key]]; + if (parsedValue) { + result[key] = parsedValue; } - return [self parseScalarValue:input context:context]; + }]; + return [[FSTObjectValue alloc] initWithDictionary:result]; +} + +- (FSTFieldValue *)parseArray:(NSArray *)array context:(FSTParseContext *)context { + NSMutableArray<FSTFieldValue *> *result = [NSMutableArray arrayWithCapacity:array.count]; + [array enumerateObjectsUsingBlock:^(id entry, NSUInteger idx, BOOL *stop) { + FSTFieldValue *_Nullable parsedEntry = + [self parseData:entry context:[context contextForArrayIndex:idx]]; + if (!parsedEntry) { + // Just include nulls in the array for fields being replaced with a sentinel. + parsedEntry = [FSTNullValue nullValue]; + } + [result addObject:parsedEntry]; + }]; + return [[FSTArrayValue alloc] initWithValueNoCopy:result]; +} + +/** + * "Parses" the provided FIRFieldValue, adding any necessary transforms to + * context.fieldTransforms. + */ +- (void)parseSentinelFieldValue:(FIRFieldValue *)fieldValue context:(FSTParseContext *)context { + // Sentinels are only supported with writes, and not within arrays. + if (![context isWrite]) { + FSTThrowInvalidArgument(@"%@ can only be used with updateData() and setData()%@", + fieldValue.methodName, [context fieldDescription]); + } + if (!context.path) { + FSTThrowInvalidArgument(@"%@ is not currently supported inside arrays", fieldValue.methodName); + } + + if ([fieldValue isKindOfClass:[FSTDeleteFieldValue class]]) { + if (context.dataSource == FSTUserDataSourceMergeSet) { + // No transform to add for a delete, so we do nothing. + } else if (context.dataSource == FSTUserDataSourceUpdate) { + FSTAssert(context.path->size() > 0, + @"FieldValue.delete() at the top level should have already been handled."); + FSTThrowInvalidArgument( + @"FieldValue.delete() can only appear at the top level of your " + "update data%@", + [context fieldDescription]); + } else { + // We shouldn't encounter delete sentinels for queries or non-merge setData calls. + FSTThrowInvalidArgument( + @"FieldValue.delete() can only be used with updateData() and setData() with " + @"SetOptions.merge()%@", + [context fieldDescription]); + } + + } else if ([fieldValue isKindOfClass:[FSTServerTimestampFieldValue class]]) { + [context appendToFieldTransformsWithFieldPath:*context.path + transformOperation:absl::make_unique<ServerTimestampTransform>( + ServerTimestampTransform::Get())]; + + } else if ([fieldValue isKindOfClass:[FSTArrayUnionFieldValue class]]) { + std::vector<FSTFieldValue *> parsedElements = + [self parseArrayTransformElements:((FSTArrayUnionFieldValue *)fieldValue).elements]; + auto array_union = + absl::make_unique<ArrayTransform>(TransformOperation::Type::ArrayUnion, parsedElements); + [context appendToFieldTransformsWithFieldPath:*context.path + transformOperation:std::move(array_union)]; + + } else if ([fieldValue isKindOfClass:[FSTArrayRemoveFieldValue class]]) { + std::vector<FSTFieldValue *> parsedElements = + [self parseArrayTransformElements:((FSTArrayRemoveFieldValue *)fieldValue).elements]; + auto array_remove = + absl::make_unique<ArrayTransform>(TransformOperation::Type::ArrayRemove, parsedElements); + [context appendToFieldTransformsWithFieldPath:*context.path + transformOperation:std::move(array_remove)]; + + } else { + FSTFail(@"Unknown FIRFieldValue type: %@", NSStringFromClass([fieldValue class])); } } /** - * Helper to parse a scalar value (i.e. not an NSDictionary or NSArray). + * Helper to parse a scalar value (i.e. not an NSDictionary, NSArray, or FIRFieldValue). * * Note that it handles all NSNumber values that are encodable as int64_t or doubles * (depending on the underlying type of the NSNumber). Unsigned integer values are handled though * any value outside what is representable by int64_t (a signed 64-bit value) will throw an * exception. * - * @return The parsed value, or nil if the value was a FieldValue sentinel that should not be - * included in the resulting parsed data. + * @return The parsed value. */ -- (nullable FSTFieldValue *)parseScalarValue:(nullable id)input context:(FSTParseContext *)context { +- (FSTFieldValue *)parseScalarValue:(nullable id)input context:(FSTParseContext *)context { if (!input || [input isMemberOfClass:[NSNull class]]) { return [FSTNullValue nullValue]; @@ -620,8 +742,7 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { FSTAssert(context.path->size() > 0, @"FieldValue.delete() at the top level should have already been handled."); FSTThrowInvalidArgument( - @"FieldValue.delete() can only appear at the top level of your " - "update data%@", + @"FieldValue.delete() can only appear at the top level of your update data%@", [context fieldDescription]); } else { // We shouldn't encounter delete sentinels for queries or non-merge setData calls. @@ -639,10 +760,9 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { @"FieldValue.serverTimestamp() is not currently supported inside arrays%@", [context fieldDescription]); } - [context.fieldTransforms - addObject:[[FSTFieldTransform alloc] - initWithPath:*context.path - transform:[FSTServerTimestampTransform serverTimestampTransform]]]; + [context appendToFieldTransformsWithFieldPath:*context.path + transformOperation:absl::make_unique<ServerTimestampTransform>( + ServerTimestampTransform::Get())]; // Return nil so this value is omitted from the parsed result. return nil; @@ -656,6 +776,22 @@ typedef NS_ENUM(NSInteger, FSTUserDataSource) { } } +- (std::vector<FSTFieldValue *>)parseArrayTransformElements:(NSArray<id> *)elements { + std::vector<FSTFieldValue *> results; + for (id element in elements) { + // Although array transforms are used with writes, the actual elements being unioned or removed + // are not considered writes since they cannot contain any FieldValue sentinels, etc. + FSTParseContext *context = + [FSTParseContext contextWithSource:FSTUserDataSourceArgument + path:absl::make_unique<FieldPath>(FieldPath::EmptyPath())]; + FSTFieldValue *parsedElement = [self parseData:element context:context]; + FSTAssert(parsedElement && context.fieldTransforms->size() == 0, + @"Failed to properly parse array transform element: %@", element); + results.push_back(parsedElement); + } + return results; +} + @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Core/FSTFirestoreClient.mm b/Firestore/Source/Core/FSTFirestoreClient.mm index c0d38ca..33d1903 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.mm +++ b/Firestore/Source/Core/FSTFirestoreClient.mm @@ -16,7 +16,8 @@ #import "Firestore/Source/Core/FSTFirestoreClient.h" -#import <future> +#include <future> // NOLINT(build/c++11) +#include <memory> #import "Firestore/Source/Core/FSTEventManager.h" #import "Firestore/Source/Core/FSTSyncEngine.h" diff --git a/Firestore/Source/Core/FSTListenSequence.mm b/Firestore/Source/Core/FSTListenSequence.mm index 6f50d35..6a9f9ae 100644 --- a/Firestore/Source/Core/FSTListenSequence.mm +++ b/Firestore/Source/Core/FSTListenSequence.mm @@ -14,7 +14,7 @@ * limitations under the License. */ -#import "FSTListenSequence.h" +#import "Firestore/Source/Core/FSTListenSequence.h" NS_ASSUME_NONNULL_BEGIN diff --git a/Firestore/Source/Core/FSTQuery.mm b/Firestore/Source/Core/FSTQuery.mm index 811ad03..8f49c26 100644 --- a/Firestore/Source/Core/FSTQuery.mm +++ b/Firestore/Source/Core/FSTQuery.mm @@ -17,6 +17,7 @@ #import "Firestore/Source/Core/FSTQuery.h" #include <memory> +#include <string> #include <utility> #import "Firestore/Source/API/FIRFirestore+Internal.h" diff --git a/Firestore/Source/Core/FSTSyncEngine.mm b/Firestore/Source/Core/FSTSyncEngine.mm index 673991c..0a4fc94 100644 --- a/Firestore/Source/Core/FSTSyncEngine.mm +++ b/Firestore/Source/Core/FSTSyncEngine.mm @@ -16,11 +16,12 @@ #import "Firestore/Source/Core/FSTSyncEngine.h" +#import <GRPCClient/GRPCCall.h> + #include <map> +#include <set> #include <unordered_map> -#import <GRPCClient/GRPCCall.h> - #import "FIRFirestoreErrors.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" diff --git a/Firestore/Source/Core/FSTTransaction.h b/Firestore/Source/Core/FSTTransaction.h index 676ada9..581a5fa 100644 --- a/Firestore/Source/Core/FSTTransaction.h +++ b/Firestore/Source/Core/FSTTransaction.h @@ -24,8 +24,6 @@ @class FIRSetOptions; @class FSTDatastore; -@class FSTFieldMask; -@class FSTFieldTransform; @class FSTMaybeDocument; @class FSTObjectValue; @class FSTParsedSetData; diff --git a/Firestore/Source/Core/FSTTransaction.mm b/Firestore/Source/Core/FSTTransaction.mm index 9e67ed4..681f9ca 100644 --- a/Firestore/Source/Core/FSTTransaction.mm +++ b/Firestore/Source/Core/FSTTransaction.mm @@ -16,11 +16,11 @@ #import "Firestore/Source/Core/FSTTransaction.h" +#import <GRPCClient/GRPCCall.h> + #include <map> #include <vector> -#import <GRPCClient/GRPCCall.h> - #import "FIRFirestoreErrors.h" #import "FIRSetOptions.h" #import "Firestore/Source/API/FSTUserDataConverter.h" @@ -33,8 +33,10 @@ #import "Firestore/Source/Util/FSTUsageValidation.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/precondition.h" using firebase::firestore::model::DocumentKey; +using firebase::firestore::model::Precondition; NS_ASSUME_NONNULL_BEGIN @@ -136,25 +138,26 @@ NS_ASSUME_NONNULL_BEGIN * Returns version of this doc when it was read in this transaction as a precondition, or no * precondition if it was not read. */ -- (FSTPrecondition *)preconditionForDocumentKey:(const DocumentKey &)key { +- (Precondition)preconditionForDocumentKey:(const DocumentKey &)key { const auto iter = _readVersions.find(key); if (iter == _readVersions.end()) { - return [FSTPrecondition none]; + return Precondition::None(); } else { - return [FSTPrecondition preconditionWithUpdateTime:iter->second]; + return Precondition::UpdateTime(iter->second); } } /** * Returns the precondition for a document if the operation is an update, based on the provided - * UpdateOptions. Will return nil if an error occurred, in which case it sets the error parameter. + * UpdateOptions. Will return none precondition if an error occurred, in which case it sets the + * error parameter. */ -- (nullable FSTPrecondition *)preconditionForUpdateWithDocumentKey:(const DocumentKey &)key - error:(NSError **)error { +- (Precondition)preconditionForUpdateWithDocumentKey:(const DocumentKey &)key + error:(NSError **)error { const auto iter = _readVersions.find(key); if (iter == _readVersions.end()) { // Document was not read, so we just use the preconditions for an update. - return [FSTPrecondition preconditionWithExists:YES]; + return Precondition::Exists(true); } FSTSnapshotVersion *version = iter->second; @@ -169,10 +172,10 @@ NS_ASSUME_NONNULL_BEGIN NSLocalizedDescriptionKey : @"Can't update a document that doesn't exist." }]; } - return nil; + return Precondition::None(); } else { // Document exists, just base precondition on document update time. - return [FSTPrecondition preconditionWithUpdateTime:version]; + return Precondition::UpdateTime(version); } } @@ -183,13 +186,12 @@ NS_ASSUME_NONNULL_BEGIN - (void)updateData:(FSTParsedUpdateData *)data forDocument:(const DocumentKey &)key { NSError *error = nil; - FSTPrecondition *_Nullable precondition = - [self preconditionForUpdateWithDocumentKey:key error:&error]; - if (precondition) { - [self writeMutations:[data mutationsWithKey:key precondition:precondition]]; - } else { + const Precondition precondition = [self preconditionForUpdateWithDocumentKey:key error:&error]; + if (precondition.IsNone()) { FSTAssert(error, @"Got nil precondition, but error was not set"); self.lastWriteError = error; + } else { + [self writeMutations:[data mutationsWithKey:key precondition:precondition]]; } } @@ -198,7 +200,7 @@ NS_ASSUME_NONNULL_BEGIN initWithKey:key precondition:[self preconditionForDocumentKey:key]] ]]; // Since the delete will be applied before all following writes, we need to ensure that the - // precondition for the next write will be exists: false. + // precondition for the next write will be exists without timestamp. _readVersions[key] = [FSTSnapshotVersion noVersion]; } diff --git a/Firestore/Source/Local/FSTLevelDB.mm b/Firestore/Source/Local/FSTLevelDB.mm index 4812228..fae85e7 100644 --- a/Firestore/Source/Local/FSTLevelDB.mm +++ b/Firestore/Source/Local/FSTLevelDB.mm @@ -16,7 +16,7 @@ #import "Firestore/Source/Local/FSTLevelDB.h" -#include <leveldb/db.h> +#include <memory> #import "FIRFirestoreErrors.h" #import "Firestore/Source/Local/FSTLevelDBMigrations.h" @@ -26,13 +26,14 @@ #import "Firestore/Source/Remote/FSTSerializerBeta.h" #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTLogger.h" -#include "absl/memory/memory.h" #include "Firestore/core/src/firebase/firestore/auth/user.h" #include "Firestore/core/src/firebase/firestore/core/database_info.h" #include "Firestore/core/src/firebase/firestore/local/leveldb_transaction.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" +#include "absl/memory/memory.h" +#include "leveldb/db.h" namespace util = firebase::firestore::util; using firebase::firestore::auth::User; diff --git a/Firestore/Source/Local/FSTLevelDBKey.h b/Firestore/Source/Local/FSTLevelDBKey.h index c7a64ee..5b234ec 100644 --- a/Firestore/Source/Local/FSTLevelDBKey.h +++ b/Firestore/Source/Local/FSTLevelDBKey.h @@ -16,6 +16,8 @@ #import <Foundation/Foundation.h> +#include <string> + #import "Firestore/Source/Core/FSTTypes.h" #import "Firestore/Source/Local/StringView.h" diff --git a/Firestore/Source/Local/FSTLevelDBMigrations.mm b/Firestore/Source/Local/FSTLevelDBMigrations.mm index cf06c9f..fefd0f7 100644 --- a/Firestore/Source/Local/FSTLevelDBMigrations.mm +++ b/Firestore/Source/Local/FSTLevelDBMigrations.mm @@ -14,16 +14,18 @@ * limitations under the License. */ -#include "Firestore/Source/Local/FSTLevelDBMigrations.h" +#import "Firestore/Source/Local/FSTLevelDBMigrations.h" -#include <absl/strings/match.h> -#include "leveldb/write_batch.h" +#include <string> #import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h" #import "Firestore/Source/Local/FSTLevelDBKey.h" #import "Firestore/Source/Local/FSTLevelDBQueryCache.h" #import "Firestore/Source/Util/FSTAssert.h" +#include "absl/strings/match.h" +#include "leveldb/write_batch.h" + NS_ASSUME_NONNULL_BEGIN // Current version of the schema defined in this file. diff --git a/Firestore/Source/Local/FSTLevelDBMutationQueue.mm b/Firestore/Source/Local/FSTLevelDBMutationQueue.mm index 29acead..75c3cf6 100644 --- a/Firestore/Source/Local/FSTLevelDBMutationQueue.mm +++ b/Firestore/Source/Local/FSTLevelDBMutationQueue.mm @@ -16,13 +16,10 @@ #import "Firestore/Source/Local/FSTLevelDBMutationQueue.h" +#include <memory> #include <set> #include <string> -#include <absl/strings/match.h> -#include <leveldb/db.h> -#include <leveldb/write_batch.h> - #import "Firestore/Protos/objc/firestore/local/Mutation.pbobjc.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Local/FSTLevelDB.h" @@ -38,6 +35,9 @@ #include "Firestore/core/src/firebase/firestore/model/resource_path.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" #include "Firestore/core/src/firebase/firestore/util/string_util.h" +#include "absl/strings/match.h" +#include "leveldb/db.h" +#include "leveldb/write_batch.h" NS_ASSUME_NONNULL_BEGIN diff --git a/Firestore/Source/Local/FSTLevelDBQueryCache.mm b/Firestore/Source/Local/FSTLevelDBQueryCache.mm index aa1ab41..5fde7d7 100644 --- a/Firestore/Source/Local/FSTLevelDBQueryCache.mm +++ b/Firestore/Source/Local/FSTLevelDBQueryCache.mm @@ -16,6 +16,9 @@ #import "Firestore/Source/Local/FSTLevelDBQueryCache.h" +#include <memory> +#include <string> + #import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Local/FSTLevelDB.h" @@ -23,9 +26,9 @@ #import "Firestore/Source/Local/FSTLocalSerializer.h" #import "Firestore/Source/Local/FSTQueryData.h" #import "Firestore/Source/Util/FSTAssert.h" -#include "absl/strings/match.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "absl/strings/match.h" NS_ASSUME_NONNULL_BEGIN diff --git a/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.mm b/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.mm index 703fc69..f655e3a 100644 --- a/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.mm +++ b/Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.mm @@ -16,8 +16,6 @@ #import "Firestore/Source/Local/FSTLevelDBRemoteDocumentCache.h" -#include <leveldb/db.h> -#include <leveldb/write_batch.h> #include <string> #import "Firestore/Protos/objc/firestore/local/MaybeDocument.pbobjc.h" @@ -29,8 +27,11 @@ #import "Firestore/Source/Model/FSTDocumentDictionary.h" #import "Firestore/Source/Model/FSTDocumentSet.h" #import "Firestore/Source/Util/FSTAssert.h" + #include "Firestore/core/src/firebase/firestore/local/leveldb_transaction.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "leveldb/db.h" +#include "leveldb/write_batch.h" NS_ASSUME_NONNULL_BEGIN diff --git a/Firestore/Source/Local/FSTLocalSerializer.mm b/Firestore/Source/Local/FSTLocalSerializer.mm index 1d9455d..61e173a 100644 --- a/Firestore/Source/Local/FSTLocalSerializer.mm +++ b/Firestore/Source/Local/FSTLocalSerializer.mm @@ -16,7 +16,7 @@ #import "Firestore/Source/Local/FSTLocalSerializer.h" -#include <inttypes.h> +#include <cinttypes> #import "Firestore/Protos/objc/firestore/local/MaybeDocument.pbobjc.h" #import "Firestore/Protos/objc/firestore/local/Mutation.pbobjc.h" diff --git a/Firestore/Source/Local/FSTLocalStore.mm b/Firestore/Source/Local/FSTLocalStore.mm index 990e332..b5dfeec 100644 --- a/Firestore/Source/Local/FSTLocalStore.mm +++ b/Firestore/Source/Local/FSTLocalStore.mm @@ -16,6 +16,8 @@ #import "Firestore/Source/Local/FSTLocalStore.h" +#include <set> + #import "FIRTimestamp.h" #import "Firestore/Source/Core/FSTListenSequence.h" #import "Firestore/Source/Core/FSTQuery.h" diff --git a/Firestore/Source/Local/FSTNoOpGarbageCollector.mm b/Firestore/Source/Local/FSTNoOpGarbageCollector.mm index 451cde2..421d283 100644 --- a/Firestore/Source/Local/FSTNoOpGarbageCollector.mm +++ b/Firestore/Source/Local/FSTNoOpGarbageCollector.mm @@ -16,6 +16,8 @@ #import "Firestore/Source/Local/FSTNoOpGarbageCollector.h" +#include <set> + #include "Firestore/core/src/firebase/firestore/model/document_key.h" using firebase::firestore::model::DocumentKey; diff --git a/Firestore/Source/Local/StringView.h b/Firestore/Source/Local/StringView.h index 4e36cff..85afcaa 100644 --- a/Firestore/Source/Local/StringView.h +++ b/Firestore/Source/Local/StringView.h @@ -19,9 +19,10 @@ #import <Foundation/Foundation.h> -#include <leveldb/slice.h> #include <string> + #include "absl/strings/string_view.h" +#include "leveldb/slice.h" namespace Firestore { @@ -43,7 +44,7 @@ class StringView { // Creates a StringView from an NSString. When StringView is an argument type // into which an NSString* is passed, the caller should ensure that the // NSString is retained. - StringView(NSString *str) + StringView(NSString *str) // NOLINT(runtime/explicit) : data_([str UTF8String]), size_([str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]) { } @@ -54,20 +55,24 @@ class StringView { // Creates a StringView from the given char* pointer but computes the size // with strlen. This is really only suitable for passing C string literals. - StringView(const char *data) : data_(data), size_(strlen(data)) { + StringView(const char *data) // NOLINT(runtime/explicit) + : data_(data), size_(strlen(data)) { } // Creates a StringView from the given slice. - StringView(leveldb::Slice slice) : data_(slice.data()), size_(slice.size()) { + StringView(leveldb::Slice slice) // NOLINT(runtime/explicit) + : data_(slice.data()), size_(slice.size()) { } // Creates a StringView from the absl::string_view. - StringView(absl::string_view s) : data_(s.data()), size_(s.size()) { + StringView(absl::string_view s) // NOLINT(runtime/explicit) + : data_(s.data()), size_(s.size()) { } // Creates a StringView from the given std::string. The string must be an // lvalue for the lifetime requirements to be satisfied. - StringView(const std::string &str) : data_(str.data()), size_(str.size()) { + StringView(const std::string &str) // NOLINT(runtime/explicit) + : data_(str.data()), size_(str.size()) { } // Converts this StringView to a Slice, which is an equivalent (and more diff --git a/Firestore/Source/Model/FSTDocumentKey.h b/Firestore/Source/Model/FSTDocumentKey.h index dbcff2c..a403117 100644 --- a/Firestore/Source/Model/FSTDocumentKey.h +++ b/Firestore/Source/Model/FSTDocumentKey.h @@ -17,6 +17,7 @@ #import <Foundation/Foundation.h> #include <initializer_list> +#include <string> #include "Firestore/core/src/firebase/firestore/model/resource_path.h" diff --git a/Firestore/Source/Model/FSTDocumentKey.mm b/Firestore/Source/Model/FSTDocumentKey.mm index cbe9c89..679d7a6 100644 --- a/Firestore/Source/Model/FSTDocumentKey.mm +++ b/Firestore/Source/Model/FSTDocumentKey.mm @@ -16,6 +16,7 @@ #import "Firestore/Source/Model/FSTDocumentKey.h" +#include <string> #include <utility> #import "Firestore/Source/Core/FSTFirestoreClient.h" diff --git a/Firestore/Source/Model/FSTFieldValue.mm b/Firestore/Source/Model/FSTFieldValue.mm index 80bd11f..0d7c649 100644 --- a/Firestore/Source/Model/FSTFieldValue.mm +++ b/Firestore/Source/Model/FSTFieldValue.mm @@ -18,9 +18,6 @@ #import "FIRTimestamp.h" -#include "Firestore/core/src/firebase/firestore/util/comparison.h" -#include "Firestore/core/src/firebase/firestore/util/string_apple.h" - #import "Firestore/Source/API/FIRGeoPoint+Internal.h" #import "Firestore/Source/API/FIRSnapshotOptions+Internal.h" #import "Firestore/Source/Model/FSTDocumentKey.h" @@ -29,6 +26,7 @@ #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "Firestore/core/src/firebase/firestore/util/comparison.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace util = firebase::firestore::util; diff --git a/Firestore/Source/Model/FSTMutation.h b/Firestore/Source/Model/FSTMutation.h index 4e4357d..7261f30 100644 --- a/Firestore/Source/Model/FSTMutation.h +++ b/Firestore/Source/Model/FSTMutation.h @@ -16,10 +16,15 @@ #import <Foundation/Foundation.h> +#include <memory> #include <vector> #include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/field_mask.h" #include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "Firestore/core/src/firebase/firestore/model/field_transform.h" +#include "Firestore/core/src/firebase/firestore/model/precondition.h" +#include "Firestore/core/src/firebase/firestore/model/transform_operations.h" @class FSTDocument; @class FSTFieldValue; @@ -30,94 +35,6 @@ NS_ASSUME_NONNULL_BEGIN -#pragma mark - FSTFieldMask - -/** - * Provides a set of fields that can be used to partially patch a document. FieldMask is used in - * conjunction with ObjectValue. - * - * Examples: - * foo - Overwrites foo entirely with the provided value. If foo is not present in the companion - * ObjectValue, the field is deleted. - * foo.bar - Overwrites only the field bar of the object foo. If foo is not an object, foo is - * replaced with an object containing bar. - */ -@interface FSTFieldMask : NSObject -- (id)init __attribute__((unavailable("Use initWithFields:"))); - -/** - * Initializes the field mask with the given field paths. Caller is expected to either copy or - * or release the array of fields. - */ -- (instancetype)initWithFields:(std::vector<firebase::firestore::model::FieldPath>)fields - NS_DESIGNATED_INITIALIZER; - -- (const std::vector<firebase::firestore::model::FieldPath> &)fields; -@end - -#pragma mark - FSTFieldTransform - -/** Represents a transform within a TransformMutation. */ -@protocol FSTTransformOperation <NSObject> -@end - -/** Transforms a value into a server-generated timestamp. */ -@interface FSTServerTimestampTransform : NSObject <FSTTransformOperation> -+ (instancetype)serverTimestampTransform; -@end - -/** A field path and the FSTTransformOperation to perform upon it. */ -@interface FSTFieldTransform : NSObject -- (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithPath:(firebase::firestore::model::FieldPath)path - transform:(id<FSTTransformOperation>)transform NS_DESIGNATED_INITIALIZER; -- (const firebase::firestore::model::FieldPath &)path; -@property(nonatomic, strong, readonly) id<FSTTransformOperation> transform; -@end - -#pragma mark - FSTPrecondition - -typedef NS_ENUM(NSUInteger, FSTPreconditionExists) { - FSTPreconditionExistsNotSet, - FSTPreconditionExistsYes, - FSTPreconditionExistsNo, -}; - -/** - * Encodes a precondition for a mutation. This follows the model that the backend accepts with the - * special case of an explicit "empty" precondition (meaning no precondition). - */ -@interface FSTPrecondition : NSObject - -/** Creates a new FSTPrecondition with an exists flag. */ -+ (FSTPrecondition *)preconditionWithExists:(BOOL)exists; - -/** Creates a new FSTPrecondition based on a time the document exists at. */ -+ (FSTPrecondition *)preconditionWithUpdateTime:(FSTSnapshotVersion *)updateTime; - -/** Returns a precondition representing no precondition. */ -+ (FSTPrecondition *)none; - -/** - * Returns true if the preconditions is valid for the given document (or null if no document is - * available). - */ -- (BOOL)isValidForDocument:(FSTMaybeDocument *_Nullable)maybeDoc; - -/** Returns whether this Precondition represents no precondition. */ -- (BOOL)isNone; - -/** If set, preconditions a mutation based on the last updateTime. */ -@property(nonatomic, strong, readonly, nullable) FSTSnapshotVersion *updateTime; - -/** - * If set, preconditions a mutation based on whether the document exists. - * Uses FSTPreconditionExistsNotSet to mark as unset. - */ -@property(nonatomic, assign, readonly) FSTPreconditionExists exists; - -@end - #pragma mark - FSTMutationResult @interface FSTMutationResult : NSObject @@ -132,7 +49,7 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) { /** * The resulting fields returned from the backend after a FSTTransformMutation has been committed. - * Contains one FieldValue for each FSTFieldTransform that was in the mutation. + * Contains one FieldValue for each FieldTransform that was in the mutation. * * Will be nil if the mutation was not a FSTTransformMutation. */ @@ -156,7 +73,8 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) { - (id)init NS_UNAVAILABLE; - (instancetype)initWithKey:(firebase::firestore::model::DocumentKey)key - precondition:(FSTPrecondition *)precondition NS_DESIGNATED_INITIALIZER; + precondition:(firebase::firestore::model::Precondition)precondition + NS_DESIGNATED_INITIALIZER; /** * Applies this mutation to the given FSTDocument, FSTDeletedDocument or nil, if we don't have @@ -217,8 +135,7 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) { - (const firebase::firestore::model::DocumentKey &)key; -/** The precondition for this mutation. */ -@property(nonatomic, strong, readonly) FSTPrecondition *precondition; +- (const firebase::firestore::model::Precondition &)precondition; @end @@ -231,7 +148,7 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) { @interface FSTSetMutation : FSTMutation - (instancetype)initWithKey:(firebase::firestore::model::DocumentKey)key - precondition:(FSTPrecondition *)precondition NS_UNAVAILABLE; + precondition:(firebase::firestore::model::Precondition)precondition NS_UNAVAILABLE; /** * Initializes the set mutation. @@ -243,7 +160,8 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) { */ - (instancetype)initWithKey:(firebase::firestore::model::DocumentKey)key value:(FSTObjectValue *)value - precondition:(FSTPrecondition *)precondition NS_DESIGNATED_INITIALIZER; + precondition:(firebase::firestore::model::Precondition)precondition + NS_DESIGNATED_INITIALIZER; /** The object value to use when setting the document. */ @property(nonatomic, strong, readonly) FSTObjectValue *value; @@ -262,12 +180,12 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) { */ @interface FSTPatchMutation : FSTMutation -/** Returns the precondition for the given FSTPrecondition. */ +/** Returns the precondition for the given Precondition. */ - (instancetype)initWithKey:(firebase::firestore::model::DocumentKey)key - precondition:(FSTPrecondition *)precondition NS_UNAVAILABLE; + precondition:(firebase::firestore::model::Precondition)precondition NS_UNAVAILABLE; /** - * Initializes a new patch mutation with an explicit FSTFieldMask and FSTObjectValue representing + * Initializes a new patch mutation with an explicit FieldMask and FSTObjectValue representing * the updates to perform * * @param key Identifies the location of the document to mutate. @@ -278,18 +196,19 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) { * @param precondition The precondition for this mutation. */ - (instancetype)initWithKey:(firebase::firestore::model::DocumentKey)key - fieldMask:(FSTFieldMask *)fieldMask + fieldMask:(firebase::firestore::model::FieldMask)fieldMask value:(FSTObjectValue *)value - precondition:(FSTPrecondition *)precondition NS_DESIGNATED_INITIALIZER; - -/** The fields and associated values to use when patching the document. */ -@property(nonatomic, strong, readonly) FSTObjectValue *value; + precondition:(firebase::firestore::model::Precondition)precondition + NS_DESIGNATED_INITIALIZER; /** * A mask to apply to |value|, where only fields that are in both the fieldMask and the value * will be updated. */ -@property(nonatomic, strong, readonly) FSTFieldMask *fieldMask; +- (const firebase::firestore::model::FieldMask &)fieldMask; + +/** The fields and associated values to use when patching the document. */ +@property(nonatomic, strong, readonly) FSTObjectValue *value; @end @@ -307,20 +226,20 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) { @interface FSTTransformMutation : FSTMutation - (instancetype)initWithKey:(firebase::firestore::model::DocumentKey)key - precondition:(FSTPrecondition *)precondition NS_UNAVAILABLE; + precondition:(firebase::firestore::model::Precondition)precondition NS_UNAVAILABLE; /** * Initializes a new transform mutation with the specified field transforms. * * @param key Identifies the location of the document to mutate. - * @param fieldTransforms A list of FSTFieldTransform objects to perform to the document. + * @param fieldTransforms A list of FieldTransform objects to perform to the document. */ - (instancetype)initWithKey:(firebase::firestore::model::DocumentKey)key - fieldTransforms:(NSArray<FSTFieldTransform *> *)fieldTransforms + fieldTransforms:(std::vector<firebase::firestore::model::FieldTransform>)fieldTransforms NS_DESIGNATED_INITIALIZER; /** The field transforms to use when transforming the document. */ -@property(nonatomic, strong, readonly) NSArray<FSTFieldTransform *> *fieldTransforms; +- (const std::vector<firebase::firestore::model::FieldTransform> &)fieldTransforms; @end diff --git a/Firestore/Source/Model/FSTMutation.mm b/Firestore/Source/Model/FSTMutation.mm index 253a853..99d2e51 100644 --- a/Firestore/Source/Model/FSTMutation.mm +++ b/Firestore/Source/Model/FSTMutation.mm @@ -16,7 +16,10 @@ #import "Firestore/Source/Model/FSTMutation.h" +#include <memory> +#include <string> #include <utility> +#include <vector> #import "FIRTimestamp.h" @@ -27,211 +30,22 @@ #import "Firestore/Source/Util/FSTClasses.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/field_mask.h" #include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "Firestore/core/src/firebase/firestore/model/field_transform.h" +#include "Firestore/core/src/firebase/firestore/model/precondition.h" +#include "Firestore/core/src/firebase/firestore/model/transform_operations.h" using firebase::firestore::model::DocumentKey; +using firebase::firestore::model::FieldMask; using firebase::firestore::model::FieldPath; +using firebase::firestore::model::FieldTransform; +using firebase::firestore::model::Precondition; +using firebase::firestore::model::ServerTimestampTransform; +using firebase::firestore::model::TransformOperation; NS_ASSUME_NONNULL_BEGIN -#pragma mark - FSTFieldMask - -@implementation FSTFieldMask { - std::vector<FieldPath> _fields; -} - -- (instancetype)initWithFields:(std::vector<FieldPath>)fields { - if (self = [super init]) { - _fields = std::move(fields); - } - return self; -} - -- (BOOL)isEqual:(id)other { - if (other == self) { - return YES; - } - if (![other isKindOfClass:[FSTFieldMask class]]) { - return NO; - } - - FSTFieldMask *otherMask = (FSTFieldMask *)other; - return _fields == otherMask->_fields; -} - -- (NSUInteger)hash { - NSUInteger hashResult = 0; - for (const FieldPath &field : _fields) { - hashResult = hashResult * 31u + field.Hash(); - } - return hashResult; -} - -- (const std::vector<FieldPath> &)fields { - return _fields; -} -@end - -#pragma mark - FSTServerTimestampTransform - -@implementation FSTServerTimestampTransform - -+ (instancetype)serverTimestampTransform { - static FSTServerTimestampTransform *sharedInstance = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedInstance = [[FSTServerTimestampTransform alloc] init]; - }); - return sharedInstance; -} - -- (BOOL)isEqual:(id)other { - if (other == self) { - return YES; - } - return [other isKindOfClass:[FSTServerTimestampTransform class]]; -} - -- (NSUInteger)hash { - // arbitrary number since all instances are equal. - return 37; -} - -@end - -#pragma mark - FSTFieldTransform - -@implementation FSTFieldTransform { - FieldPath _path; -} - -- (instancetype)initWithPath:(FieldPath)path transform:(id<FSTTransformOperation>)transform { - self = [super init]; - if (self) { - _path = std::move(path); - _transform = transform; - } - return self; -} - -- (BOOL)isEqual:(id)other { - if (other == self) return YES; - if (![[other class] isEqual:[self class]]) return NO; - FSTFieldTransform *otherFieldTransform = other; - return self.path == otherFieldTransform.path && - [self.transform isEqual:otherFieldTransform.transform]; -} - -- (NSUInteger)hash { - NSUInteger hash = self.path.Hash(); - hash = hash * 31 + [self.transform hash]; - return hash; -} - -- (const firebase::firestore::model::FieldPath &)path { - return _path; -} - -@end - -#pragma mark - FSTPrecondition - -@implementation FSTPrecondition - -+ (FSTPrecondition *)preconditionWithExists:(BOOL)exists { - FSTPreconditionExists existsEnum = exists ? FSTPreconditionExistsYes : FSTPreconditionExistsNo; - return [[FSTPrecondition alloc] initWithUpdateTime:nil exists:existsEnum]; -} - -+ (FSTPrecondition *)preconditionWithUpdateTime:(FSTSnapshotVersion *)updateTime { - return [[FSTPrecondition alloc] initWithUpdateTime:updateTime exists:FSTPreconditionExistsNotSet]; -} - -+ (FSTPrecondition *)none { - static dispatch_once_t onceToken; - static FSTPrecondition *noPrecondition; - dispatch_once(&onceToken, ^{ - noPrecondition = - [[FSTPrecondition alloc] initWithUpdateTime:nil exists:FSTPreconditionExistsNotSet]; - }); - return noPrecondition; -} - -- (instancetype)initWithUpdateTime:(FSTSnapshotVersion *_Nullable)updateTime - exists:(FSTPreconditionExists)exists { - if (self = [super init]) { - _updateTime = updateTime; - _exists = exists; - } - return self; -} - -- (BOOL)isValidForDocument:(FSTMaybeDocument *_Nullable)maybeDoc { - if (self.updateTime) { - return - [maybeDoc isKindOfClass:[FSTDocument class]] && [maybeDoc.version isEqual:self.updateTime]; - } else if (self.exists != FSTPreconditionExistsNotSet) { - if (self.exists == FSTPreconditionExistsYes) { - return [maybeDoc isKindOfClass:[FSTDocument class]]; - } else { - FSTAssert(self.exists == FSTPreconditionExistsNo, @"Invalid precondition"); - return maybeDoc == nil || [maybeDoc isKindOfClass:[FSTDeletedDocument class]]; - } - } else { - FSTAssert(self.isNone, @"Precondition should be empty"); - return YES; - } -} - -- (BOOL)isNone { - return self.updateTime == nil && self.exists == FSTPreconditionExistsNotSet; -} - -- (BOOL)isEqual:(id)other { - if (self == other) { - return YES; - } - - if (![other isKindOfClass:[FSTPrecondition class]]) { - return NO; - } - - FSTPrecondition *otherPrecondition = (FSTPrecondition *)other; - // Compare references to cover nil equality - return (self.updateTime == otherPrecondition.updateTime || - [self.updateTime isEqual:otherPrecondition.updateTime]) && - self.exists == otherPrecondition.exists; -} - -- (NSUInteger)hash { - NSUInteger hash = [self.updateTime hash]; - hash = hash * 31 + self.exists; - return hash; -} - -- (NSString *)description { - if (self.isNone) { - return @"<FSTPrecondition <none>>"; - } else { - NSString *existsString; - switch (self.exists) { - case FSTPreconditionExistsYes: - existsString = @"yes"; - break; - case FSTPreconditionExistsNo: - existsString = @"no"; - break; - default: - existsString = @"<not-set>"; - break; - } - return [NSString stringWithFormat:@"<FSTPrecondition updateTime=%@ exists=%@>", self.updateTime, - existsString]; - } -} - -@end - #pragma mark - FSTMutationResult @implementation FSTMutationResult @@ -251,12 +65,13 @@ NS_ASSUME_NONNULL_BEGIN @implementation FSTMutation { DocumentKey _key; + Precondition _precondition; } -- (instancetype)initWithKey:(DocumentKey)key precondition:(FSTPrecondition *)precondition { +- (instancetype)initWithKey:(DocumentKey)key precondition:(Precondition)precondition { if (self = [super init]) { _key = std::move(key); - _precondition = precondition; + _precondition = std::move(precondition); } return self; } @@ -279,6 +94,10 @@ NS_ASSUME_NONNULL_BEGIN return _key; } +- (const firebase::firestore::model::Precondition &)precondition { + return _precondition; +} + @end #pragma mark - FSTSetMutation @@ -287,8 +106,8 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithKey:(DocumentKey)key value:(FSTObjectValue *)value - precondition:(FSTPrecondition *)precondition { - if (self = [super initWithKey:std::move(key) precondition:precondition]) { + precondition:(Precondition)precondition { + if (self = [super initWithKey:std::move(key) precondition:std::move(precondition)]) { _value = value; } return self; @@ -296,7 +115,8 @@ NS_ASSUME_NONNULL_BEGIN - (NSString *)description { return [NSString stringWithFormat:@"<FSTSetMutation key=%s value=%@ precondition=%@>", - self.key.ToString().c_str(), self.value, self.precondition]; + self.key.ToString().c_str(), self.value, + self.precondition.description()]; } - (BOOL)isEqual:(id)other { @@ -309,12 +129,12 @@ NS_ASSUME_NONNULL_BEGIN FSTSetMutation *otherMutation = (FSTSetMutation *)other; return [self.key isEqual:otherMutation.key] && [self.value isEqual:otherMutation.value] && - [self.precondition isEqual:otherMutation.precondition]; + self.precondition == otherMutation.precondition; } - (NSUInteger)hash { NSUInteger result = [self.key hash]; - result = 31 * result + [self.precondition hash]; + result = 31 * result + self.precondition.Hash(); result = 31 * result + [self.value hash]; return result; } @@ -327,7 +147,7 @@ NS_ASSUME_NONNULL_BEGIN FSTAssert(!mutationResult.transformResults, @"Transform results received by FSTSetMutation."); } - if (![self.precondition isValidForDocument:maybeDoc]) { + if (!self.precondition.IsValidFor(maybeDoc)) { return maybeDoc; } @@ -354,20 +174,26 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - FSTPatchMutation -@implementation FSTPatchMutation +@implementation FSTPatchMutation { + FieldMask _fieldMask; +} - (instancetype)initWithKey:(DocumentKey)key - fieldMask:(FSTFieldMask *)fieldMask + fieldMask:(FieldMask)fieldMask value:(FSTObjectValue *)value - precondition:(FSTPrecondition *)precondition { - self = [super initWithKey:std::move(key) precondition:precondition]; + precondition:(Precondition)precondition { + self = [super initWithKey:std::move(key) precondition:std::move(precondition)]; if (self) { - _fieldMask = fieldMask; + _fieldMask = std::move(fieldMask); _value = value; } return self; } +- (const firebase::firestore::model::FieldMask &)fieldMask { + return _fieldMask; +} + - (BOOL)isEqual:(id)other { if (other == self) { return YES; @@ -377,23 +203,23 @@ NS_ASSUME_NONNULL_BEGIN } FSTPatchMutation *otherMutation = (FSTPatchMutation *)other; - return [self.key isEqual:otherMutation.key] && [self.fieldMask isEqual:otherMutation.fieldMask] && + return [self.key isEqual:otherMutation.key] && self.fieldMask == otherMutation.fieldMask && [self.value isEqual:otherMutation.value] && - [self.precondition isEqual:otherMutation.precondition]; + self.precondition == otherMutation.precondition; } - (NSUInteger)hash { NSUInteger result = [self.key hash]; - result = 31 * result + [self.precondition hash]; - result = 31 * result + [self.fieldMask hash]; + result = 31 * result + self.precondition.Hash(); + result = 31 * result + self.fieldMask.Hash(); result = 31 * result + [self.value hash]; return result; } - (NSString *)description { - return [NSString stringWithFormat:@"<FSTPatchMutation key=%s mask=%@ value=%@ precondition=%@>", - self.key.ToString().c_str(), self.fieldMask, self.value, - self.precondition]; + return [NSString stringWithFormat:@"<FSTPatchMutation key=%s mask=%s value=%@ precondition=%@>", + self.key.ToString().c_str(), self.fieldMask.ToString().c_str(), + self.value, self.precondition.description()]; } - (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc @@ -404,7 +230,7 @@ NS_ASSUME_NONNULL_BEGIN FSTAssert(!mutationResult.transformResults, @"Transform results received by FSTPatchMutation."); } - if (![self.precondition isValidForDocument:maybeDoc]) { + if (!self.precondition.IsValidFor(maybeDoc)) { return maybeDoc; } @@ -434,7 +260,7 @@ NS_ASSUME_NONNULL_BEGIN - (FSTObjectValue *)patchObjectValue:(FSTObjectValue *)objectValue { FSTObjectValue *result = objectValue; - for (const FieldPath &fieldPath : self.fieldMask.fields) { + for (const FieldPath &fieldPath : self.fieldMask) { FSTFieldValue *newValue = [self.value valueForPath:fieldPath]; if (newValue) { result = [result objectBySettingValue:newValue forPath:fieldPath]; @@ -447,20 +273,26 @@ NS_ASSUME_NONNULL_BEGIN @end -@implementation FSTTransformMutation +@implementation FSTTransformMutation { + /** The field transforms to use when transforming the document. */ + std::vector<FieldTransform> _fieldTransforms; +} - (instancetype)initWithKey:(DocumentKey)key - fieldTransforms:(NSArray<FSTFieldTransform *> *)fieldTransforms { + fieldTransforms:(std::vector<FieldTransform>)fieldTransforms { // NOTE: We set a precondition of exists: true as a safety-check, since we always combine // FSTTransformMutations with a FSTSetMutation or FSTPatchMutation which (if successful) should // end up with an existing document. - if (self = [super initWithKey:std::move(key) - precondition:[FSTPrecondition preconditionWithExists:YES]]) { - _fieldTransforms = fieldTransforms; + if (self = [super initWithKey:std::move(key) precondition:Precondition::Exists(true)]) { + _fieldTransforms = std::move(fieldTransforms); } return self; } +- (const std::vector<FieldTransform> &)fieldTransforms { + return _fieldTransforms; +} + - (BOOL)isEqual:(id)other { if (other == self) { return YES; @@ -471,21 +303,27 @@ NS_ASSUME_NONNULL_BEGIN FSTTransformMutation *otherMutation = (FSTTransformMutation *)other; return [self.key isEqual:otherMutation.key] && - [self.fieldTransforms isEqual:otherMutation.fieldTransforms] && - [self.precondition isEqual:otherMutation.precondition]; + self.fieldTransforms == otherMutation.fieldTransforms && + self.precondition == otherMutation.precondition; } - (NSUInteger)hash { NSUInteger result = [self.key hash]; - result = 31 * result + [self.precondition hash]; - result = 31 * result + [self.fieldTransforms hash]; + result = 31 * result + self.precondition.Hash(); + for (const auto &transform : self.fieldTransforms) { + result = 31 * result + transform.Hash(); + } return result; } - (NSString *)description { - return [NSString stringWithFormat:@"<FSTTransformMutation key=%s transforms=%@ precondition=%@>", - self.key.ToString().c_str(), self.fieldTransforms, - self.precondition]; + std::string fieldTransforms; + for (const auto &transform : self.fieldTransforms) { + fieldTransforms += " " + transform.path().CanonicalString(); + } + return [NSString stringWithFormat:@"<FSTTransformMutation key=%s transforms=%s precondition=%@>", + self.key.ToString().c_str(), fieldTransforms.c_str(), + self.precondition.description()]; } - (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc @@ -497,7 +335,7 @@ NS_ASSUME_NONNULL_BEGIN @"Transform results missing for FSTTransformMutation."); } - if (![self.precondition isValidForDocument:maybeDoc]) { + if (!self.precondition.IsValidFor(maybeDoc)) { return maybeDoc; } @@ -534,19 +372,19 @@ NS_ASSUME_NONNULL_BEGIN (FSTMaybeDocument *_Nullable)baseDocument writeTime:(FIRTimestamp *)localWriteTime { NSMutableArray<FSTFieldValue *> *transformResults = [NSMutableArray array]; - for (FSTFieldTransform *fieldTransform in self.fieldTransforms) { - if ([fieldTransform.transform isKindOfClass:[FSTServerTimestampTransform class]]) { + for (const FieldTransform &fieldTransform : self.fieldTransforms) { + if (fieldTransform.transformation().type() == TransformOperation::Type::ServerTimestamp) { FSTFieldValue *previousValue = nil; if ([baseDocument isMemberOfClass:[FSTDocument class]]) { - previousValue = [((FSTDocument *)baseDocument) fieldForPath:fieldTransform.path]; + previousValue = [((FSTDocument *)baseDocument) fieldForPath:fieldTransform.path()]; } [transformResults addObject:[FSTServerTimestampValue serverTimestampValueWithLocalWriteTime:localWriteTime previousValue:previousValue]]; } else { - FSTFail(@"Encountered unknown transform: %@", fieldTransform); + FSTFail(@"Encountered unknown transform: %d type", fieldTransform.transformation().type()); } } return transformResults; @@ -554,17 +392,17 @@ NS_ASSUME_NONNULL_BEGIN - (FSTObjectValue *)transformObject:(FSTObjectValue *)objectValue transformResults:(NSArray<FSTFieldValue *> *)transformResults { - FSTAssert(transformResults.count == self.fieldTransforms.count, + FSTAssert(transformResults.count == self.fieldTransforms.size(), @"Transform results length mismatch."); - for (NSUInteger i = 0; i < self.fieldTransforms.count; i++) { - FSTFieldTransform *fieldTransform = self.fieldTransforms[i]; - id<FSTTransformOperation> transform = fieldTransform.transform; - FieldPath fieldPath = fieldTransform.path; - if ([transform isKindOfClass:[FSTServerTimestampTransform class]]) { + for (size_t i = 0; i < self.fieldTransforms.size(); i++) { + const FieldTransform &fieldTransform = self.fieldTransforms[i]; + const TransformOperation &transform = fieldTransform.transformation(); + const FieldPath &fieldPath = fieldTransform.path(); + if (transform.type() == TransformOperation::Type::ServerTimestamp) { objectValue = [objectValue objectBySettingValue:transformResults[i] forPath:fieldPath]; } else { - FSTFail(@"Encountered unknown transform: %@", transform); + FSTFail(@"Encountered unknown transform: %d type", transform.type()); } } return objectValue; @@ -585,19 +423,18 @@ NS_ASSUME_NONNULL_BEGIN } FSTDeleteMutation *otherMutation = (FSTDeleteMutation *)other; - return [self.key isEqual:otherMutation.key] && - [self.precondition isEqual:otherMutation.precondition]; + return [self.key isEqual:otherMutation.key] && self.precondition == otherMutation.precondition; } - (NSUInteger)hash { NSUInteger result = [self.key hash]; - result = 31 * result + [self.precondition hash]; + result = 31 * result + self.precondition.Hash(); return result; } - (NSString *)description { return [NSString stringWithFormat:@"<FSTDeleteMutation key=%s precondition=%@>", - self.key.ToString().c_str(), self.precondition]; + self.key.ToString().c_str(), self.precondition.description()]; } - (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc @@ -609,7 +446,7 @@ NS_ASSUME_NONNULL_BEGIN @"Transform results received by FSTDeleteMutation."); } - if (![self.precondition isValidForDocument:maybeDoc]) { + if (!self.precondition.IsValidFor(maybeDoc)) { return maybeDoc; } diff --git a/Firestore/Source/Public/FIRFieldValue.h b/Firestore/Source/Public/FIRFieldValue.h index 11a0da0..d896587 100644 --- a/Firestore/Source/Public/FIRFieldValue.h +++ b/Firestore/Source/Public/FIRFieldValue.h @@ -38,6 +38,29 @@ NS_SWIFT_NAME(FieldValue) */ + (instancetype)fieldValueForServerTimestamp NS_SWIFT_NAME(serverTimestamp()); +/** + * Returns a special value that can be used with setData() or updateData() that tells the server to + * union the given elements with any array value that already exists on the server. Each + * specified element that doesn't already exist in the array will be added to the end. If the + * field being modified is not already an array it will be overwritten with an array containing + * exactly the specified elements. + * + * @param elements The elements to union into the array. + * @return The FieldValue sentinel for use in a call to setData() or updateData(). + */ ++ (instancetype)fieldValueForArrayUnion:(NSArray<id> *)elements NS_SWIFT_NAME(arrayUnion(_:)); + +/** + * Returns a special value that can be used with setData() or updateData() that tells the server to + * remove the given elements from any array value that already exists on the server. All + * instances of each element specified will be removed from the array. If the field being + * modified is not already an array it will be overwritten with an empty array. + * + * @param elements The elements to remove from the array. + * @return The FieldValue sentinel for use in a call to setData() or updateData(). + */ ++ (instancetype)fieldValueForArrayRemove:(NSArray<id> *)elements NS_SWIFT_NAME(arrayRemove(_:)); + @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Remote/FSTDatastore.mm b/Firestore/Source/Remote/FSTDatastore.mm index 501237f..c7ee30f 100644 --- a/Firestore/Source/Remote/FSTDatastore.mm +++ b/Firestore/Source/Remote/FSTDatastore.mm @@ -16,12 +16,13 @@ #import "Firestore/Source/Remote/FSTDatastore.h" -#include <memory> -#include <vector> - #import <GRPCClient/GRPCCall+OAuth2.h> #import <ProtoRPC/ProtoRPC.h> +#include <map> +#include <memory> +#include <vector> + #import "FIRFirestoreErrors.h" #import "Firestore/Source/API/FIRFirestore+Internal.h" #import "Firestore/Source/API/FIRFirestoreVersion.h" @@ -258,12 +259,10 @@ typedef GRPCProtoCall * (^RPCFactory)(void); } struct Closure { - std::vector<DocumentKey> keys; - FSTMaybeDocumentDictionary *results; + std::map<DocumentKey, FSTMaybeDocument *> results; }; - __block std::shared_ptr<Closure> closure = std::make_shared<Closure>( - Closure{keys, [FSTMaybeDocumentDictionary maybeDocumentDictionary]}); + __block std::shared_ptr<Closure> closure = std::make_shared<Closure>(Closure{}); RPCFactory rpcFactory = ^GRPCProtoCall * { __block GRPCProtoCall *rpc = [self.service RPCToBatchGetDocumentsWithRequest:request @@ -283,19 +282,17 @@ typedef GRPCProtoCall * (^RPCFactory)(void); // Streaming response, accumulate result FSTMaybeDocument *doc = [self.serializer decodedMaybeDocumentFromBatch:response]; - closure->results = - [closure->results dictionaryBySettingObject:doc - forKey:doc.key]; + closure->results.insert({doc.key, doc}); } else { // Streaming response is done, call completion FSTLog(@"RPC BatchGetDocuments completed successfully."); [FSTDatastore logHeadersForRPC:rpc RPCName:@"BatchGetDocuments"]; FSTAssert(!response, @"Got response after done."); NSMutableArray<FSTMaybeDocument *> *docs = - [NSMutableArray arrayWithCapacity:closure->keys.size()]; - for (const DocumentKey &key : closure->keys) { - [docs addObject:closure->results[static_cast<FSTDocumentKey *>( - key)]]; + [NSMutableArray arrayWithCapacity:closure->results.size()]; + for (auto &&entry : closure->results) { + FSTMaybeDocument *doc = entry.second; + [docs addObject:doc]; } completion(docs, nil); } diff --git a/Firestore/Source/Remote/FSTRemoteStore.mm b/Firestore/Source/Remote/FSTRemoteStore.mm index 8892ffb..39d285a 100644 --- a/Firestore/Source/Remote/FSTRemoteStore.mm +++ b/Firestore/Source/Remote/FSTRemoteStore.mm @@ -16,7 +16,7 @@ #import "Firestore/Source/Remote/FSTRemoteStore.h" -#include <inttypes.h> +#include <cinttypes> #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" diff --git a/Firestore/Source/Remote/FSTSerializerBeta.mm b/Firestore/Source/Remote/FSTSerializerBeta.mm index 3a22a3f..c8b0fa4 100644 --- a/Firestore/Source/Remote/FSTSerializerBeta.mm +++ b/Firestore/Source/Remote/FSTSerializerBeta.mm @@ -16,10 +16,12 @@ #import "Firestore/Source/Remote/FSTSerializerBeta.h" -#include <inttypes.h> - #import <GRPCClient/GRPCCall.h> -#import "FIRTimestamp.h" + +#include <cinttypes> +#include <string> +#include <utility> +#include <vector> #import "Firestore/Protos/objc/google/firestore/v1beta1/Common.pbobjc.h" #import "Firestore/Protos/objc/google/firestore/v1beta1/Document.pbobjc.h" @@ -31,6 +33,7 @@ #import "FIRFirestoreErrors.h" #import "FIRGeoPoint.h" +#import "FIRTimestamp.h" #import "Firestore/Source/Core/FSTQuery.h" #import "Firestore/Source/Core/FSTSnapshotVersion.h" #import "Firestore/Source/Local/FSTQueryData.h" @@ -44,15 +47,26 @@ #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/field_mask.h" #include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "Firestore/core/src/firebase/firestore/model/field_transform.h" +#include "Firestore/core/src/firebase/firestore/model/precondition.h" #include "Firestore/core/src/firebase/firestore/model/resource_path.h" +#include "Firestore/core/src/firebase/firestore/model/transform_operations.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" +#include "absl/memory/memory.h" namespace util = firebase::firestore::util; using firebase::firestore::model::DatabaseId; using firebase::firestore::model::DocumentKey; +using firebase::firestore::model::FieldMask; using firebase::firestore::model::FieldPath; +using firebase::firestore::model::FieldTransform; +using firebase::firestore::model::Precondition; using firebase::firestore::model::ResourcePath; +using firebase::firestore::model::ServerTimestampTransform; +using firebase::firestore::model::SnapshotVersion; +using firebase::firestore::model::TransformOperation; NS_ASSUME_NONNULL_BEGIN @@ -464,7 +478,7 @@ NS_ASSUME_NONNULL_BEGIN FSTFail(@"Unknown mutation type %@", NSStringFromClass(mutationClass)); } - if (!mutation.precondition.isNone) { + if (!mutation.precondition.IsNone()) { proto.currentDocument = [self encodedPrecondition:mutation.precondition]; } @@ -472,9 +486,9 @@ NS_ASSUME_NONNULL_BEGIN } - (FSTMutation *)decodedMutation:(GCFSWrite *)mutation { - FSTPrecondition *precondition = [mutation hasCurrentDocument] - ? [self decodedPrecondition:mutation.currentDocument] - : [FSTPrecondition none]; + Precondition precondition = [mutation hasCurrentDocument] + ? [self decodedPrecondition:mutation.currentDocument] + : Precondition::None(); switch (mutation.operationOneOfCase) { case GCFSWrite_Operation_OneOfCase_Update: @@ -494,8 +508,7 @@ NS_ASSUME_NONNULL_BEGIN precondition:precondition]; case GCFSWrite_Operation_OneOfCase_Transform: { - FSTPreconditionExists exists = precondition.exists; - FSTAssert(exists == FSTPreconditionExistsYes, + FSTAssert(precondition == Precondition::Exists(true), @"Transforms must have precondition \"exists == true\""); return [[FSTTransformMutation alloc] @@ -509,79 +522,77 @@ NS_ASSUME_NONNULL_BEGIN } } -- (GCFSPrecondition *)encodedPrecondition:(FSTPrecondition *)precondition { - FSTAssert(!precondition.isNone, @"Can't serialize an empty precondition"); +- (GCFSPrecondition *)encodedPrecondition:(const Precondition &)precondition { + FSTAssert(!precondition.IsNone(), @"Can't serialize an empty precondition"); GCFSPrecondition *message = [GCFSPrecondition message]; - if (precondition.updateTime) { - message.updateTime = [self encodedVersion:precondition.updateTime]; - } else if (precondition.exists != FSTPreconditionExistsNotSet) { - message.exists = precondition.exists == FSTPreconditionExistsYes; + if (precondition.type() == Precondition::Type::UpdateTime) { + message.updateTime = [self encodedVersion:precondition.update_time()]; + } else if (precondition.type() == Precondition::Type::Exists) { + message.exists = precondition == Precondition::Exists(true); } else { - FSTFail(@"Unknown precondition: %@", precondition); + FSTFail(@"Unknown precondition: %@", precondition.description()); } return message; } -- (FSTPrecondition *)decodedPrecondition:(GCFSPrecondition *)precondition { +- (Precondition)decodedPrecondition:(GCFSPrecondition *)precondition { switch (precondition.conditionTypeOneOfCase) { case GCFSPrecondition_ConditionType_OneOfCase_GPBUnsetOneOfCase: - return [FSTPrecondition none]; + return Precondition::None(); case GCFSPrecondition_ConditionType_OneOfCase_Exists: - return [FSTPrecondition preconditionWithExists:precondition.exists]; + return Precondition::Exists(precondition.exists); case GCFSPrecondition_ConditionType_OneOfCase_UpdateTime: - return [FSTPrecondition - preconditionWithUpdateTime:[self decodedVersion:precondition.updateTime]]; + return Precondition::UpdateTime([self decodedVersion:precondition.updateTime]); default: FSTFail(@"Unrecognized Precondition one-of case %@", precondition); } } -- (GCFSDocumentMask *)encodedFieldMask:(FSTFieldMask *)fieldMask { +- (GCFSDocumentMask *)encodedFieldMask:(const FieldMask &)fieldMask { GCFSDocumentMask *mask = [GCFSDocumentMask message]; - for (const FieldPath &field : fieldMask.fields) { + for (const FieldPath &field : fieldMask) { [mask.fieldPathsArray addObject:util::WrapNSString(field.CanonicalString())]; } return mask; } -- (FSTFieldMask *)decodedFieldMask:(GCFSDocumentMask *)fieldMask { - std::vector<FieldPath> fields{}; +- (FieldMask)decodedFieldMask:(GCFSDocumentMask *)fieldMask { + std::vector<FieldPath> fields; fields.reserve(fieldMask.fieldPathsArray_Count); for (NSString *path in fieldMask.fieldPathsArray) { fields.push_back(FieldPath::FromServerFormat(util::MakeStringView(path))); } - return [[FSTFieldMask alloc] initWithFields:std::move(fields)]; + return FieldMask(std::move(fields)); } - (NSMutableArray<GCFSDocumentTransform_FieldTransform *> *)encodedFieldTransforms: - (NSArray<FSTFieldTransform *> *)fieldTransforms { + (const std::vector<FieldTransform> &)fieldTransforms { NSMutableArray *protos = [NSMutableArray array]; - for (FSTFieldTransform *fieldTransform in fieldTransforms) { - FSTAssert([fieldTransform.transform isKindOfClass:[FSTServerTimestampTransform class]], - @"Unknown transform: %@", fieldTransform.transform); + for (const FieldTransform &fieldTransform : fieldTransforms) { + FSTAssert(fieldTransform.transformation().type() == TransformOperation::Type::ServerTimestamp, + @"Unknown transform: %d type", fieldTransform.transformation().type()); GCFSDocumentTransform_FieldTransform *proto = [GCFSDocumentTransform_FieldTransform message]; - proto.fieldPath = util::WrapNSString(fieldTransform.path.CanonicalString()); + proto.fieldPath = util::WrapNSString(fieldTransform.path().CanonicalString()); proto.setToServerValue = GCFSDocumentTransform_FieldTransform_ServerValue_RequestTime; [protos addObject:proto]; } return protos; } -- (NSArray<FSTFieldTransform *> *)decodedFieldTransforms: +- (std::vector<FieldTransform>)decodedFieldTransforms: (NSArray<GCFSDocumentTransform_FieldTransform *> *)protos { - NSMutableArray<FSTFieldTransform *> *fieldTransforms = [NSMutableArray array]; + std::vector<FieldTransform> fieldTransforms; + fieldTransforms.reserve(protos.count); for (GCFSDocumentTransform_FieldTransform *proto in protos) { FSTAssert( proto.setToServerValue == GCFSDocumentTransform_FieldTransform_ServerValue_RequestTime, @"Unknown transform setToServerValue: %d", proto.setToServerValue); - [fieldTransforms - addObject:[[FSTFieldTransform alloc] - initWithPath:FieldPath::FromServerFormat( - util::MakeStringView(proto.fieldPath)) - transform:[FSTServerTimestampTransform serverTimestampTransform]]]; + fieldTransforms.emplace_back( + FieldPath::FromServerFormat(util::MakeStringView(proto.fieldPath)), + absl::make_unique<ServerTimestampTransform>(ServerTimestampTransform::Get())); } return fieldTransforms; } diff --git a/Firestore/Source/Remote/FSTStream.mm b/Firestore/Source/Remote/FSTStream.mm index f65230b..a96feae 100644 --- a/Firestore/Source/Remote/FSTStream.mm +++ b/Firestore/Source/Remote/FSTStream.mm @@ -14,8 +14,6 @@ * limitations under the License. */ -#import "Firestore/Source/Remote/FSTDatastore.h" - #import <GRPCClient/GRPCCall+OAuth2.h> #import <GRPCClient/GRPCCall.h> @@ -24,6 +22,7 @@ #import "Firestore/Source/Local/FSTQueryData.h" #import "Firestore/Source/Model/FSTMutation.h" #import "Firestore/Source/Remote/FSTBufferedWriter.h" +#import "Firestore/Source/Remote/FSTDatastore.h" #import "Firestore/Source/Remote/FSTExponentialBackoff.h" #import "Firestore/Source/Remote/FSTSerializerBeta.h" #import "Firestore/Source/Remote/FSTStream.h" diff --git a/Firestore/Source/Util/FSTAssert.h b/Firestore/Source/Util/FSTAssert.h index 77bbb1d..610d306 100644 --- a/Firestore/Source/Util/FSTAssert.h +++ b/Firestore/Source/Util/FSTAssert.h @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <Foundation/Foundation.h> +#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN diff --git a/Firestore/Source/Util/FSTDispatchQueue.mm b/Firestore/Source/Util/FSTDispatchQueue.mm index 15d6e7b..0974359 100644 --- a/Firestore/Source/Util/FSTDispatchQueue.mm +++ b/Firestore/Source/Util/FSTDispatchQueue.mm @@ -16,6 +16,8 @@ #import <Foundation/Foundation.h> +#include <atomic> + #import "Firestore/Source/Util/FSTAssert.h" #import "Firestore/Source/Util/FSTDispatchQueue.h" @@ -146,24 +148,23 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - FSTDispatchQueue @interface FSTDispatchQueue () - /** * Callbacks scheduled to be queued in the future. Callbacks are automatically removed after they * are run or canceled. */ @property(nonatomic, strong, readonly) NSMutableArray<FSTDelayedCallback *> *delayedCallbacks; -/** - * Flag set while an FSTDispatchQueue operation is currently executing. Used for assertion - * sanity-checks. - */ -@property(nonatomic, assign) BOOL operationInProgress; - - (instancetype)initWithQueue:(dispatch_queue_t)queue NS_DESIGNATED_INITIALIZER; @end -@implementation FSTDispatchQueue +@implementation FSTDispatchQueue { + /** + * Flag set while an FSTDispatchQueue operation is currently executing. Used for assertion + * sanity-checks. + */ + std::atomic<bool> _operationInProgress; +} + (instancetype)queueWith:(dispatch_queue_t)dispatchQueue { return [[FSTDispatchQueue alloc] initWithQueue:dispatchQueue]; @@ -171,9 +172,9 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithQueue:(dispatch_queue_t)queue { if (self = [super init]) { + _operationInProgress = false; _queue = queue; _delayedCallbacks = [NSMutableArray array]; - _operationInProgress = NO; } return self; } @@ -191,16 +192,16 @@ NS_ASSUME_NONNULL_BEGIN FSTAssert(!_operationInProgress, @"enterCheckedOperation may not be called when an operation is in progress"); @try { - _operationInProgress = YES; + _operationInProgress = true; [self verifyIsCurrentQueue]; block(); } @finally { - _operationInProgress = NO; + _operationInProgress = false; } } - (void)dispatchAsync:(void (^)(void))block { - FSTAssert(!_operationInProgress || ![self onTargetQueue], + FSTAssert(![self onTargetQueue] || !_operationInProgress, @"dispatchAsync called when we are already running on target dispatch queue '%@'", [self targetQueueLabel]); @@ -216,7 +217,7 @@ NS_ASSUME_NONNULL_BEGIN } - (void)dispatchSync:(void (^)(void))block { - FSTAssert(!_operationInProgress || ![self onTargetQueue], + FSTAssert(![self onTargetQueue] || !_operationInProgress, @"dispatchSync called when we are already running on target dispatch queue '%@'", [self targetQueueLabel]); diff --git a/Firestore/Source/Util/FSTUsageValidation.h b/Firestore/Source/Util/FSTUsageValidation.h index a80dafa..05933ea 100644 --- a/Firestore/Source/Util/FSTUsageValidation.h +++ b/Firestore/Source/Util/FSTUsageValidation.h @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <Foundation/Foundation.h> +#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN diff --git a/Firestore/Source/Util/FSTUsageValidation.mm b/Firestore/Source/Util/FSTUsageValidation.mm index 82128f4..93abf87 100644 --- a/Firestore/Source/Util/FSTUsageValidation.mm +++ b/Firestore/Source/Util/FSTUsageValidation.mm @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <Foundation/Foundation.h> +#import "Firestore/Source/Util/FSTUsageValidation.h" NS_ASSUME_NONNULL_BEGIN diff --git a/Firestore/core/.clang-tidy b/Firestore/core/.clang-tidy new file mode 100644 index 0000000..a071105 --- /dev/null +++ b/Firestore/core/.clang-tidy @@ -0,0 +1,34 @@ +--- +# cert-* +# -cert-dcl50-cpp +# We use variadic functions +# -cert-err58-cpp +# GoogleTest creates instances in static scope in a way that trips this +# warning for every test. +# readability-* +# -readability-else-after-return +# -readability-implicit-bool-conversion +# These checks generate a bunch of noise that we're just not religious +# about. +# modernize-* +# -modernize-use-equals-default +# VS 2015 and Xcode <= 8.2 don't fully support this so we don't use +# `= default`. +# -modernize-use-equals-delete +# GoogleTest generates test classes that use the old idiom of making +# default constructors and operator= private. +Checks: 'bugprone-*,cert-*,-cert-dcl50-cpp,-cert-err58-cpp,google-*,objc-*,readability-*,-readability-else-after-return,-readability-implicit-bool-conversion,misc-*,modernize-*,-modernize-use-equals-default,-modernize-use-equals-delete,clang-diagnostic-*,clang-analyzer-*' +WarningsAsErrors: '' +HeaderFilterRegex: '' +CheckOptions: + - key: readability-braces-around-statements.ShortStatementLines + value: '1' + - key: google-readability-braces-around-statements.ShortStatementLines + value: '1' + - key: google-readability-function-size.StatementThreshold + value: '800' + - key: google-readability-namespace-comments.ShortNamespaceLines + value: '10' + - key: google-readability-namespace-comments.SpacesBeforeComments + value: '2' +... diff --git a/Firestore/core/include/firebase/firestore/document_reference.h b/Firestore/core/include/firebase/firestore/document_reference.h index 58310b5..d295188 100644 --- a/Firestore/core/include/firebase/firestore/document_reference.h +++ b/Firestore/core/include/firebase/firestore/document_reference.h @@ -16,7 +16,7 @@ // TODO(rsgowman): This file isn't intended to be used just yet. It's just an // outline of what the API might eventually look like. Most of this was -// shamelessly stolen and modified from rtdb's header file, melded with the +// shamelessly stolen and modified from RTDB's header file, melded with the // (java) firestore api. #ifndef FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_DOCUMENT_REFERENCE_H_ @@ -36,7 +36,7 @@ // here so we don't forget to mention this during the API review, and should be // removed once this note has migrated to the API review doc. -// TODO(rsgowman): replace these forward decl's with appropriate includes (once +// TODO(rsgowman): replace these forward decls with appropriate includes (once // they exist) namespace firebase { class App; @@ -47,7 +47,7 @@ class Future; namespace firebase { namespace firestore { -// TODO(rsgowman): replace these forward decl's with appropriate includes (once +// TODO(rsgowman): replace these forward decls with appropriate includes (once // they exist) class FieldValue; class DocumentSnapshot; @@ -361,7 +361,7 @@ namespace std { // C++ style guide. But we think this is probably ok in this case since: // a) It's the standard way of doing this outside of Google (as the style guide // itself points out), and -// b) This has a straightfoward hash function anyway (just hash the path) so I +// b) This has a straightforward hash function anyway (just hash the path) so I // don't think the concerns in the style guide are going to bite us. // // Raise this concern during the API review. diff --git a/Firestore/core/include/firebase/firestore/timestamp.h b/Firestore/core/include/firebase/firestore/timestamp.h index 2ee1c46..1736981 100644 --- a/Firestore/core/include/firebase/firestore/timestamp.h +++ b/Firestore/core/include/firebase/firestore/timestamp.h @@ -17,12 +17,13 @@ #ifndef FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_TIMESTAMP_H_ #define FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_TIMESTAMP_H_ -#include <stdint.h> -#include <time.h> +#include <cstdint> +#include <ctime> +#include <string> + #if !defined(_STLPORT_VERSION) #include <chrono> // NOLINT(build/c++11) #endif // !defined(_STLPORT_VERSION) -#include <string> namespace firebase { diff --git a/Firestore/core/src/firebase/firestore/auth/credentials_provider.h b/Firestore/core/src/firebase/firestore/auth/credentials_provider.h index 1aa76df..0a1930a 100644 --- a/Firestore/core/src/firebase/firestore/auth/credentials_provider.h +++ b/Firestore/core/src/firebase/firestore/auth/credentials_provider.h @@ -31,10 +31,10 @@ namespace firestore { namespace auth { // `TokenErrorListener` is a listener that gets a token or an error. -typedef std::function<void(util::StatusOr<Token>)> TokenListener; +using TokenListener = std::function<void(util::StatusOr<Token>)>; // Listener notified with a User change. -typedef std::function<void(User user)> UserChangeListener; +using UserChangeListener = std::function<void(User user)>; /** * Provides methods for getting the uid and token for the current user and diff --git a/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h b/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h index 66c3c87..0e1da31 100644 --- a/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h +++ b/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.h @@ -78,7 +78,7 @@ class FirebaseCredentialsProvider : public CredentialsProvider { */ struct Contents { Contents(FIRApp* app, User&& user) - : app(app), current_user(std::move(user)), mutex() { + : app(app), current_user(std::move(user)) { } const FIRApp* app; diff --git a/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm b/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm index 2bd3acc..ff2d5f7 100644 --- a/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm +++ b/Firestore/core/src/firebase/firestore/auth/firebase_credentials_provider_apple.mm @@ -24,6 +24,7 @@ #include "Firestore/core/src/firebase/firestore/util/string_apple.h" // NB: This is also defined in Firestore/Source/Public/FIRFirestoreErrors.h +// NOLINTNEXTLINE: public constant NSString* const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain"; namespace firebase { @@ -47,7 +48,7 @@ FirebaseCredentialsProvider::FirebaseCredentialsProvider(FIRApp* app) std::unique_lock<std::mutex> lock(contents->mutex); NSDictionary<NSString*, id>* user_info = notification.userInfo; - // ensure we're only notifiying for the current app. + // ensure we're only notifying for the current app. FIRApp* notified_app = user_info[FIRAuthStateDidChangeInternalNotificationAppKey]; if (![contents->app isEqual:notified_app]) { diff --git a/Firestore/core/src/firebase/firestore/auth/token.h b/Firestore/core/src/firebase/firestore/auth/token.h index 4b2f3aa..56084f8 100644 --- a/Firestore/core/src/firebase/firestore/auth/token.h +++ b/Firestore/core/src/firebase/firestore/auth/token.h @@ -42,7 +42,7 @@ namespace auth { // TODO(zxu123): Make this support token-type for desktop workflow. class Token { public: - Token(const absl::string_view token, const User& user); + Token(absl::string_view token, const User& user); /** The actual raw token. */ const std::string& token() const { diff --git a/Firestore/core/src/firebase/firestore/auth/user.cc b/Firestore/core/src/firebase/firestore/auth/user.cc index f442d7b..4793fed 100644 --- a/Firestore/core/src/firebase/firestore/auth/user.cc +++ b/Firestore/core/src/firebase/firestore/auth/user.cc @@ -22,7 +22,7 @@ namespace firebase { namespace firestore { namespace auth { -User::User() : uid_(), is_authenticated_(false) { +User::User() : is_authenticated_(false) { } User::User(const absl::string_view uid) : uid_(uid), is_authenticated_(true) { diff --git a/Firestore/core/src/firebase/firestore/auth/user.h b/Firestore/core/src/firebase/firestore/auth/user.h index 3918c61..cc030cc 100644 --- a/Firestore/core/src/firebase/firestore/auth/user.h +++ b/Firestore/core/src/firebase/firestore/auth/user.h @@ -41,7 +41,7 @@ class User { User(); /** Construct an authenticated user with the given UID. */ - explicit User(const absl::string_view uid); + explicit User(absl::string_view uid); const std::string& uid() const { return uid_; diff --git a/Firestore/core/src/firebase/firestore/core/database_info.h b/Firestore/core/src/firebase/firestore/core/database_info.h index 2e1303e..0f97b1f 100644 --- a/Firestore/core/src/firebase/firestore/core/database_info.h +++ b/Firestore/core/src/firebase/firestore/core/database_info.h @@ -41,12 +41,12 @@ class DatabaseInfo { * @param database_id The project/database to use. * @param persistence_key A unique identifier for this Firestore's local * storage. Usually derived from -[FIRApp appName]. - * @param host The hostname of the datastore backend. + * @param host The hostname of the Firestore backend. * @param ssl_enabled Whether to use SSL when connecting. */ DatabaseInfo(const firebase::firestore::model::DatabaseId& database_id, - const absl::string_view persistence_key, - const absl::string_view host, + absl::string_view persistence_key, + absl::string_view host, bool ssl_enabled); const firebase::firestore::model::DatabaseId& database_id() const { diff --git a/Firestore/core/src/firebase/firestore/geo_point.cc b/Firestore/core/src/firebase/firestore/geo_point.cc index fb01023..1ed5126 100644 --- a/Firestore/core/src/firebase/firestore/geo_point.cc +++ b/Firestore/core/src/firebase/firestore/geo_point.cc @@ -16,10 +16,12 @@ #include "Firestore/core/include/firebase/firestore/geo_point.h" -#include <math.h> +#include <cmath> #include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +using std::isnan; + namespace firebase { namespace firestore { diff --git a/Firestore/core/src/firebase/firestore/immutable/array_sorted_map.h b/Firestore/core/src/firebase/firestore/immutable/array_sorted_map.h index 56da9e9..92fd823 100644 --- a/Firestore/core/src/firebase/firestore/immutable/array_sorted_map.h +++ b/Firestore/core/src/firebase/firestore/immutable/array_sorted_map.h @@ -70,8 +70,8 @@ class FixedArray { */ template <typename SourceIterator> void append(SourceIterator src_begin, SourceIterator src_end) { - size_type appending = static_cast<size_type>(src_end - src_begin); - size_type new_size = size_ + appending; + auto appending = static_cast<size_type>(src_end - src_begin); + auto new_size = size_ + appending; FIREBASE_ASSERT(new_size <= fixed_size); std::copy(src_begin, src_end, end()); @@ -229,8 +229,6 @@ class ArraySortedMap : public SortedMapBase { } } - // TODO(wilhuff): indexof - /** Returns true if the map contains no elements. */ bool empty() const { return size() == 0; diff --git a/Firestore/core/src/firebase/firestore/immutable/map_entry.h b/Firestore/core/src/firebase/firestore/immutable/map_entry.h index 2130b5b..1022b06 100644 --- a/Firestore/core/src/firebase/firestore/immutable/map_entry.h +++ b/Firestore/core/src/firebase/firestore/immutable/map_entry.h @@ -33,7 +33,7 @@ namespace immutable { */ template <typename K, typename V, typename C = std::less<K>> struct KeyComparator { - typedef std::pair<K, V> pair_type; + using pair_type = std::pair<K, V>; explicit KeyComparator(const C& comparator = C()) : key_comparator_(comparator) { diff --git a/Firestore/core/src/firebase/firestore/immutable/sorted_map.h b/Firestore/core/src/firebase/firestore/immutable/sorted_map.h index 5ed16b3..ef6f54e 100644 --- a/Firestore/core/src/firebase/firestore/immutable/sorted_map.h +++ b/Firestore/core/src/firebase/firestore/immutable/sorted_map.h @@ -144,6 +144,7 @@ class SortedMap : public impl::SortedMapBase { case Tag::Tree: return SortedMap{tree_.insert(key, value)}; } + FIREBASE_UNREACHABLE(); } /** @@ -159,6 +160,7 @@ class SortedMap : public impl::SortedMapBase { case Tag::Tree: return SortedMap{tree_.erase(key)}; } + FIREBASE_UNREACHABLE(); } /** Returns true if the map contains no elements. */ @@ -169,6 +171,7 @@ class SortedMap : public impl::SortedMapBase { case Tag::Tree: return tree_.empty(); } + FIREBASE_UNREACHABLE(); } /** Returns the number of items in this map. */ @@ -179,6 +182,7 @@ class SortedMap : public impl::SortedMapBase { case Tag::Tree: return tree_.size(); } + FIREBASE_UNREACHABLE(); } private: diff --git a/Firestore/core/src/firebase/firestore/immutable/sorted_map_base.h b/Firestore/core/src/firebase/firestore/immutable/sorted_map_base.h index accb5ef..cfb19c1 100644 --- a/Firestore/core/src/firebase/firestore/immutable/sorted_map_base.h +++ b/Firestore/core/src/firebase/firestore/immutable/sorted_map_base.h @@ -17,7 +17,7 @@ #ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_IMMUTABLE_SORTED_MAP_BASE_H_ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_IMMUTABLE_SORTED_MAP_BASE_H_ -#include <stdint.h> +#include <cstdint> namespace firebase { namespace firestore { diff --git a/Firestore/core/src/firebase/firestore/immutable/tree_sorted_map.h b/Firestore/core/src/firebase/firestore/immutable/tree_sorted_map.h index e3102e7..dfe270d 100644 --- a/Firestore/core/src/firebase/firestore/immutable/tree_sorted_map.h +++ b/Firestore/core/src/firebase/firestore/immutable/tree_sorted_map.h @@ -17,9 +17,8 @@ #ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_IMMUTABLE_TREE_SORTED_MAP_H_ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_IMMUTABLE_TREE_SORTED_MAP_H_ -#include <assert.h> - #include <algorithm> +#include <cassert> #include <functional> #include <memory> #include <utility> diff --git a/Firestore/core/src/firebase/firestore/local/leveldb_key.cc b/Firestore/core/src/firebase/firestore/local/leveldb_key.cc index 03dbeae..7febe71 100644 --- a/Firestore/core/src/firebase/firestore/local/leveldb_key.cc +++ b/Firestore/core/src/firebase/firestore/local/leveldb_key.cc @@ -373,7 +373,7 @@ DocumentKey Reader::ReadDocumentKey() { } ResourcePath path{std::move(path_segments)}; - if (ok_ && path.size() > 0 && DocumentKey::IsDocumentKey(path)) { + if (ok_ && !path.empty() && DocumentKey::IsDocumentKey(path)) { return DocumentKey{std::move(path)}; } diff --git a/Firestore/core/src/firebase/firestore/local/leveldb_transaction.cc b/Firestore/core/src/firebase/firestore/local/leveldb_transaction.cc index d84d441..f998550 100644 --- a/Firestore/core/src/firebase/firestore/local/leveldb_transaction.cc +++ b/Firestore/core/src/firebase/firestore/local/leveldb_transaction.cc @@ -16,12 +16,11 @@ #include "Firestore/core/src/firebase/firestore/local/leveldb_transaction.h" -#include <leveldb/write_batch.h> - #include "Firestore/core/src/firebase/firestore/local/leveldb_key.h" #include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" #include "Firestore/core/src/firebase/firestore/util/log.h" #include "absl/memory/memory.h" +#include "leveldb/write_batch.h" using leveldb::DB; using leveldb::ReadOptions; diff --git a/Firestore/core/src/firebase/firestore/local/leveldb_transaction.h b/Firestore/core/src/firebase/firestore/local/leveldb_transaction.h index 56a9a77..a6ddce2 100644 --- a/Firestore/core/src/firebase/firestore/local/leveldb_transaction.h +++ b/Firestore/core/src/firebase/firestore/local/leveldb_transaction.h @@ -17,16 +17,16 @@ #ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_LEVELDB_TRANSACTION_H_ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_LOCAL_LEVELDB_TRANSACTION_H_ -#include <absl/strings/string_view.h> -#include <leveldb/db.h> - -#include <stdint.h> +#include <cstdint> #include <map> #include <memory> #include <set> #include <string> #include <utility> +#include "absl/strings/string_view.h" +#include "leveldb/db.h" + #if __OBJC__ #import <Protobuf/GPBProtocolBuffers.h> #endif diff --git a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt index 78f5cd6..02affdb 100644 --- a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt @@ -22,18 +22,23 @@ cc_library( document.h document_key.cc document_key.h + field_mask.h field_path.cc field_path.h + field_transform.h field_value.cc field_value.h maybe_document.cc maybe_document.h no_document.cc no_document.h + precondition.cc + precondition.h resource_path.cc resource_path.h snapshot_version.cc snapshot_version.h + transform_operations.h types.h DEPENDS absl_strings diff --git a/Firestore/core/src/firebase/firestore/model/database_id.h b/Firestore/core/src/firebase/firestore/model/database_id.h index 2ad1332..0c0e0ec 100644 --- a/Firestore/core/src/firebase/firestore/model/database_id.h +++ b/Firestore/core/src/firebase/firestore/model/database_id.h @@ -17,8 +17,7 @@ #ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DATABASE_ID_H_ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DATABASE_ID_H_ -#include <stdint.h> - +#include <cstdint> #include <string> #include "absl/strings/string_view.h" @@ -45,8 +44,7 @@ class DatabaseId { * @param project_id The project for the database. * @param database_id The database in the project to use. */ - DatabaseId(const absl::string_view project_id, - const absl::string_view database_id); + DatabaseId(absl::string_view project_id, absl::string_view database_id); const std::string& project_id() const { return project_id_; diff --git a/Firestore/core/src/firebase/firestore/model/document.cc b/Firestore/core/src/firebase/firestore/model/document.cc index 16548cd..ae59d15 100644 --- a/Firestore/core/src/firebase/firestore/model/document.cc +++ b/Firestore/core/src/firebase/firestore/model/document.cc @@ -39,7 +39,7 @@ bool Document::Equals(const MaybeDocument& other) const { if (other.type() != Type::Document) { return false; } - const Document& other_doc = static_cast<const Document&>(other); + auto& other_doc = static_cast<const Document&>(other); return MaybeDocument::Equals(other) && has_local_mutations_ == other_doc.has_local_mutations_ && data_ == other_doc.data_; diff --git a/Firestore/core/src/firebase/firestore/model/field_mask.h b/Firestore/core/src/firebase/firestore/model/field_mask.h new file mode 100644 index 0000000..b895ab3 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/field_mask.h @@ -0,0 +1,95 @@ +/* + * Copyright 2018 Google + * + * Licensed 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_MASK_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_MASK_H_ + +#include <initializer_list> +#include <string> +#include <utility> +#include <vector> + +#include "Firestore/core/src/firebase/firestore/model/field_path.h" + +namespace firebase { +namespace firestore { +namespace model { + +/** + * Provides a set of fields that can be used to partially patch a document. + * FieldMask is used in conjunction with FieldValue of Object type. + * + * Examples: + * foo - Overwrites foo entirely with the provided value. If foo is not + * present in the companion FieldValue, the field is deleted. + * foo.bar - Overwrites only the field bar of the object foo. If foo is not an + * object, foo is replaced with an object containing bar. + */ +class FieldMask { + public: + using const_iterator = std::vector<FieldPath>::const_iterator; + + FieldMask(std::initializer_list<FieldPath> list) : fields_{list} { + } + explicit FieldMask(std::vector<FieldPath> fields) + : fields_{std::move(fields)} { + } + + const_iterator begin() const { + return fields_.begin(); + } + const_iterator end() const { + return fields_.end(); + } + + std::string ToString() const { + // Ideally, one should use a string builder. Since this is only non-critical + // code for logging and debugging, the logic is kept simple here. + std::string result("{ "); + for (const FieldPath& field : fields_) { + result += field.CanonicalString() + " "; + } + return result + "}"; + } + +#if defined(__OBJC__) + FieldMask() { + } + + NSUInteger Hash() const { + NSUInteger hashResult = 0; + for (const FieldPath& field : fields_) { + hashResult = hashResult * 31u + field.Hash(); + } + return hashResult; + } +#endif + + friend bool operator==(const FieldMask& lhs, const FieldMask& rhs); + + private: + std::vector<FieldPath> fields_; +}; + +inline bool operator==(const FieldMask& lhs, const FieldMask& rhs) { + return lhs.fields_ == rhs.fields_; +} + +} // namespace model +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_MASK_H_ diff --git a/Firestore/core/src/firebase/firestore/model/field_transform.h b/Firestore/core/src/firebase/firestore/model/field_transform.h new file mode 100644 index 0000000..a1dd96c --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/field_transform.h @@ -0,0 +1,70 @@ +/* + * Copyright 2018 Google + * + * Licensed 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_TRANSFORM_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_TRANSFORM_H_ + +#include <memory> +#include <utility> + +#include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "Firestore/core/src/firebase/firestore/model/transform_operations.h" + +namespace firebase { +namespace firestore { +namespace model { + +/** A field path and the TransformOperation to perform upon it. */ +class FieldTransform { + public: + FieldTransform(FieldPath path, + std::unique_ptr<TransformOperation> transformation) noexcept + : path_{std::move(path)}, transformation_{std::move(transformation)} { + } + + const FieldPath& path() const { + return path_; + } + + const TransformOperation& transformation() const { + return *transformation_.get(); + } + + bool operator==(const FieldTransform& other) const { + return path_ == other.path_ && *transformation_ == *other.transformation_; + } + +#if defined(__OBJC__) + // For Objective-C++ hash; to be removed after migration. + // Do NOT use in C++ code. + NSUInteger Hash() const { + NSUInteger hash = path_.Hash(); + hash = hash * 31 + transformation_->Hash(); + return hash; + } +#endif // defined(__OBJC__) + + private: + FieldPath path_; + // Shared by copies of the same FieldTransform. + std::shared_ptr<const TransformOperation> transformation_; +}; + +} // namespace model +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_TRANSFORM_H_ diff --git a/Firestore/core/src/firebase/firestore/model/field_value.cc b/Firestore/core/src/firebase/firestore/model/field_value.cc index 2c1af53..b0519f7 100644 --- a/Firestore/core/src/firebase/firestore/model/field_value.cc +++ b/Firestore/core/src/firebase/firestore/model/field_value.cc @@ -16,9 +16,8 @@ #include "Firestore/core/src/firebase/firestore/model/field_value.h" -#include <math.h> - #include <algorithm> +#include <cmath> #include <memory> #include <utility> #include <vector> @@ -39,7 +38,7 @@ namespace { /** * This deviates from the other platforms that define TypeOrder. Since * we already define Type for union types, we use it together with this - * function to achive the equivalent order of types i.e. + * function to achieve the equivalent order of types i.e. * i) if two types are comparable, then they are of equal order; * ii) otherwise, their order is the same as the order of their Type. */ @@ -149,7 +148,8 @@ FieldValue& FieldValue::operator=(FieldValue&& value) { return *this; default: // We just copy over POD union types. - return *this = value; + *this = value; + return *this; } } diff --git a/Firestore/core/src/firebase/firestore/model/field_value.h b/Firestore/core/src/firebase/firestore/model/field_value.h index c42d533..3c5af9c 100644 --- a/Firestore/core/src/firebase/firestore/model/field_value.h +++ b/Firestore/core/src/firebase/firestore/model/field_value.h @@ -17,8 +17,7 @@ #ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_VALUE_H_ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_VALUE_H_ -#include <stdint.h> - +#include <cstdint> #include <map> #include <memory> #include <string> @@ -164,7 +163,7 @@ class FieldValue { /** * Switch to the specified type, if different from the current type. */ - void SwitchTo(const Type type); + void SwitchTo(Type type); Type tag_ = Type::Null; union { diff --git a/Firestore/core/src/firebase/firestore/model/precondition.cc b/Firestore/core/src/firebase/firestore/model/precondition.cc new file mode 100644 index 0000000..423d5a2 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/precondition.cc @@ -0,0 +1,67 @@ +/* + * Copyright 2018 Google + * + * Licensed 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/precondition.h" + +#include "Firestore/core/src/firebase/firestore/model/maybe_document.h" +#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" + +namespace firebase { +namespace firestore { +namespace model { + +Precondition::Precondition(Type type, SnapshotVersion update_time, bool exists) + : type_(type), update_time_(std::move(update_time)), exists_(exists) { +} + +/* static */ +Precondition Precondition::Exists(bool exists) { + return Precondition{Type::Exists, SnapshotVersion::None(), exists}; +} + +/* static */ +Precondition Precondition::UpdateTime(SnapshotVersion update_time) { + // update_time could be SnapshotVersion::None() in particular for locally + // deleted documents. + return Precondition{Type::UpdateTime, std::move(update_time), false}; +} + +/* static */ +Precondition Precondition::None() { + return Precondition{Type::None, SnapshotVersion::None(), false}; +} + +bool Precondition::IsValidFor(const MaybeDocument& maybe_doc) const { + switch (type_) { + case Type::UpdateTime: + return maybe_doc.type() == MaybeDocument::Type::Document && + maybe_doc.version() == update_time_; + case Type::Exists: + if (exists_) { + return maybe_doc.type() == MaybeDocument::Type::Document; + } else { + return maybe_doc.type() == MaybeDocument::Type::NoDocument; + } + case Type::None: + return true; + } + FIREBASE_UNREACHABLE(); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/model/precondition.h b/Firestore/core/src/firebase/firestore/model/precondition.h new file mode 100644 index 0000000..4ab03c2 --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/precondition.h @@ -0,0 +1,160 @@ +/* + * Copyright 2018 Google + * + * Licensed 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_PRECONDITION_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_PRECONDITION_H_ + +#include <utility> + +#if defined(__OBJC__) +#import "FIRTimestamp.h" +#import "Firestore/Source/Core/FSTSnapshotVersion.h" +#import "Firestore/Source/Model/FSTDocument.h" +#include "Firestore/core/include/firebase/firestore/timestamp.h" +#endif // defined(__OBJC__) + +#include "Firestore/core/src/firebase/firestore/model/maybe_document.h" +#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" + +namespace firebase { +namespace firestore { +namespace model { + +/** + * Encodes a precondition for a mutation. This follows the model that the + * backend accepts with the special case of an explicit "empty" precondition + * (meaning no precondition). + */ +class Precondition { + public: + enum class Type { + None, + Exists, + UpdateTime, + }; + + /** Creates a new Precondition with an exists flag. */ + static Precondition Exists(bool exists); + + /** Creates a new Precondition based on a time the document exists at. */ + static Precondition UpdateTime(SnapshotVersion update_time); + + /** Returns a precondition representing no precondition. */ + static Precondition None(); + + /** + * Returns true if the precondition is valid for the given document (and the + * document is available). + */ + bool IsValidFor(const MaybeDocument& maybe_doc) const; + + /** Returns whether this Precondition represents no precondition. */ + bool IsNone() const { + return type_ == Type::None; + } + + Type type() const { + return type_; + } + + const SnapshotVersion& update_time() const { + return update_time_; + } + + bool operator==(const Precondition& other) const { + return type_ == other.type_ && update_time_ == other.update_time_ && + exists_ == other.exists_; + } + +#if defined(__OBJC__) + // Objective-C requires a default constructor. + Precondition() + : type_(Type::None), + update_time_(SnapshotVersion::None()), + exists_(false) { + } + + // MaybeDocument is not fully ported yet. So we suppose this addition helper. + bool IsValidFor(FSTMaybeDocument* maybe_doc) const { + switch (type_) { + case Type::UpdateTime: + return [maybe_doc isKindOfClass:[FSTDocument class]] && + firebase::firestore::model::SnapshotVersion(maybe_doc.version) == + update_time_; + case Type::Exists: + if (exists_) { + return [maybe_doc isKindOfClass:[FSTDocument class]]; + } else { + return maybe_doc == nil || + [maybe_doc isKindOfClass:[FSTDeletedDocument class]]; + } + case Type::None: + return true; + } + FIREBASE_UNREACHABLE(); + } + + // For Objective-C++ hash; to be removed after migration. + // Do NOT use in C++ code. + NSUInteger Hash() const { + NSUInteger hash = std::hash<Timestamp>()(update_time_.timestamp()); + hash = hash * 31 + exists_; + hash = hash * 31 + static_cast<NSUInteger>(type_); + return hash; + } + + NSString* description() const { + switch (type_) { + case Type::None: + return @"<Precondition <none>>"; + case Type::Exists: + if (exists_) { + return @"<Precondition exists=yes>"; + } else { + return @"<Precondition exists=no>"; + } + case Type::UpdateTime: + return [NSString + stringWithFormat:@"<Precondition update_time=%s>", + update_time_.timestamp().ToString().c_str()]; + } + // We only raise dev assertion here. This function is mainly used in + // logging. + FIREBASE_DEV_ASSERT_MESSAGE(false, "precondition invalid"); + return @"<Precondition invalid>"; + } +#endif // defined(__OBJC__) + + private: + Precondition(Type type, SnapshotVersion update_time, bool exists); + + // The actual time of this precondition. + Type type_ = Type::None; + + // For UpdateTime type, preconditions a mutation based on the last updateTime. + SnapshotVersion update_time_; + + // For Exists type, preconditions a mutation based on whether the document + // exists. + bool exists_; +}; + +} // namespace model +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_PRECONDITION_H_ diff --git a/Firestore/core/src/firebase/firestore/model/snapshot_version.h b/Firestore/core/src/firebase/firestore/model/snapshot_version.h index 56e8c50..1fbba1c 100644 --- a/Firestore/core/src/firebase/firestore/model/snapshot_version.h +++ b/Firestore/core/src/firebase/firestore/model/snapshot_version.h @@ -19,6 +19,11 @@ #include "Firestore/core/include/firebase/firestore/timestamp.h" +#if defined(__OBJC__) +#import "FIRTimestamp.h" +#import "Firestore/Source/Core/FSTSnapshotVersion.h" +#endif // defined(__OBJC__) + namespace firebase { namespace firestore { namespace model { @@ -38,6 +43,24 @@ class SnapshotVersion { /** Creates a new version that is smaller than all other versions. */ static const SnapshotVersion& None(); +#if defined(__OBJC__) + SnapshotVersion(FSTSnapshotVersion* version) // NOLINT(runtime/explicit) + : timestamp_{version.timestamp.seconds, version.timestamp.nanoseconds} { + } + + operator FSTSnapshotVersion*() const { + if (timestamp_ == Timestamp{}) { + return [FSTSnapshotVersion noVersion]; + } else { + return [FSTSnapshotVersion + versionWithTimestamp:[FIRTimestamp + timestampWithSeconds:timestamp_.seconds() + nanoseconds:timestamp_ + .nanoseconds()]]; + } + } +#endif // defined(__OBJC__) + private: Timestamp timestamp_; }; diff --git a/Firestore/core/src/firebase/firestore/model/transform_operations.h b/Firestore/core/src/firebase/firestore/model/transform_operations.h new file mode 100644 index 0000000..aad5a9b --- /dev/null +++ b/Firestore/core/src/firebase/firestore/model/transform_operations.h @@ -0,0 +1,164 @@ +/* + * Copyright 2018 Google + * + * Licensed 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. + */ + +#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_TRANSFORM_OPERATIONS_H_ +#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_TRANSFORM_OPERATIONS_H_ + +#include <vector> + +#if defined(__OBJC__) +#import "Firestore/Source/Model/FSTFieldValue.h" +#endif + +namespace firebase { +namespace firestore { +namespace model { + +// TODO(zxu123): We might want to refactor transform_operations.h into several +// files when the number of different types of operations grows gigantically. +// For now, put the interface and the only operation here. + +/** Represents a transform within a TransformMutation. */ +class TransformOperation { + public: + /** All the different kinds to TransformOperation. */ + enum class Type { + ServerTimestamp, + ArrayUnion, + ArrayRemove, + Test, // Purely for test purpose. + }; + + virtual ~TransformOperation() { + } + + /** Returns the actual type. */ + virtual Type type() const = 0; + + /** Returns whether the two are equal. */ + virtual bool operator==(const TransformOperation& other) const = 0; + + /** Returns whether the two are not equal. */ + bool operator!=(const TransformOperation& other) const { + return !operator==(other); + } + +#if defined(__OBJC__) + // For Objective-C++ hash; to be removed after migration. + // Do NOT use in C++ code. + virtual NSUInteger Hash() const = 0; +#endif // defined(__OBJC__) +}; + +/** Transforms a value into a server-generated timestamp. */ +class ServerTimestampTransform : public TransformOperation { + public: + ~ServerTimestampTransform() override { + } + + Type type() const override { + return Type::ServerTimestamp; + } + + bool operator==(const TransformOperation& other) const override { + // All ServerTimestampTransform objects are equal. + return other.type() == Type::ServerTimestamp; + } + + static const ServerTimestampTransform& Get() { + static ServerTimestampTransform shared_instance; + return shared_instance; + } + +#if defined(__OBJC__) + // For Objective-C++ hash; to be removed after migration. + // Do NOT use in C++ code. + NSUInteger Hash() const override { + // arbitrary number, the same as used in ObjC implementation, since all + // instances are equal. + return 37; + } +#endif // defined(__OBJC__) + + private: + ServerTimestampTransform() { + } +}; + +// TODO(mikelehen): ArrayTransform can only be used from Obj-C until we switch +// to using FieldValue instead of FSTFieldValue. +#if defined(__OBJC__) +/** + * Transforms an array via a union or remove operation (for convenience, we use + * this class for both Type::ArrayUnion and Type::ArrayRemove). + */ +class ArrayTransform : public TransformOperation { + public: + ArrayTransform(const Type& type, const std::vector<FSTFieldValue*> elements) + : type_(type), elements_(elements) { + } + + ~ArrayTransform() override { + } + + Type type() const override { + return type_; + } + + const std::vector<FSTFieldValue*>& elements() const { + return elements_; + } + + bool operator==(const TransformOperation& other) const override { + if (other.type() != type()) { + return false; + } + auto array_transform = static_cast<const ArrayTransform&>(other); + if (array_transform.elements_.size() != elements_.size()) { + return false; + } + for (int i = 0; i < elements_.size(); i++) { + if (![array_transform.elements_[i] isEqual:elements_[i]]) { + return false; + } + } + return true; + } + +#if defined(__OBJC__) + // For Objective-C++ hash; to be removed after migration. + // Do NOT use in C++ code. + NSUInteger Hash() const override { + NSUInteger result = 37; + result = 31 * result + (type() == Type::ArrayUnion ? 1231 : 1237); + for (FSTFieldValue* element : elements_) { + result = 31 * result + [element hash]; + } + return result; + } +#endif // defined(__OBJC__) + + private: + Type type_; + std::vector<FSTFieldValue*> elements_; +}; +#endif + +} // namespace model +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_TRANSFORM_OPERATIONS_H_ diff --git a/Firestore/core/src/firebase/firestore/model/types.h b/Firestore/core/src/firebase/firestore/model/types.h index fc1b196..3f813be 100644 --- a/Firestore/core/src/firebase/firestore/model/types.h +++ b/Firestore/core/src/firebase/firestore/model/types.h @@ -17,7 +17,7 @@ #ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_TYPES_H_ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_TYPES_H_ -#include <stdint.h> +#include <cstdint> namespace firebase { namespace firestore { diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.cc b/Firestore/core/src/firebase/firestore/remote/serializer.cc index 5483e1f..befe032 100644 --- a/Firestore/core/src/firebase/firestore/remote/serializer.cc +++ b/Firestore/core/src/firebase/firestore/remote/serializer.cc @@ -32,18 +32,34 @@ namespace remote { using firebase::firestore::model::FieldValue; using firebase::firestore::model::ObjectValue; +using firebase::firestore::util::Status; namespace { class Writer; +class Reader; + void EncodeObject(Writer* writer, const ObjectValue& object_value); -ObjectValue DecodeObject(pb_istream_t* stream); +ObjectValue DecodeObject(Reader* reader); + +/** + * Represents a nanopb tag. + * + * field_number is one of the field tags that nanopb generates based off of + * the proto messages. They're typically named in the format: + * <parentNameSpace>_<childNameSpace>_<message>_<field>_tag, e.g. + * google_firestore_v1beta1_Document_name_tag. + */ +struct Tag { + pb_wire_type_t wire_type; + uint32_t field_number; +}; /** * Docs TODO(rsgowman). But currently, this just wraps the underlying nanopb - * pb_ostream_t. + * pb_ostream_t. Also doc how to check status. */ class Writer { public: @@ -70,13 +86,8 @@ class Writer { * Writes a message type to the output stream. * * This essentially wraps calls to nanopb's pb_encode_tag() method. - * - * @param field_number is one of the field tags that nanopb generates based - * off of the proto messages. They're typically named in the format: - * <parentNameSpace>_<childNameSpace>_<message>_<field>_tag, e.g. - * google_firestore_v1beta1_Document_name_tag. */ - void WriteTag(pb_wire_type_t wiretype, uint32_t field_number); + void WriteTag(Tag tag); void WriteSize(size_t size); void WriteNull(); @@ -105,7 +116,13 @@ class Writer { return stream_.bytes_written; } + Status status() const { + return status_; + } + private: + Status status_ = Status::OK(); + /** * Creates a new Writer, based on the given nanopb pb_ostream_t. Note that * a shallow copy will be taken. (Non-null pointers within this struct must @@ -132,6 +149,79 @@ class Writer { pb_ostream_t stream_; }; +/** + * Docs TODO(rsgowman). But currently, this just wraps the underlying nanopb + * pb_istream_t. + */ +class Reader { + public: + /** + * Creates an input stream that reads from the specified bytes. Note that + * this reference must remain valid for the lifetime of this Reader. + * + * (This is roughly equivalent to the nanopb function + * pb_istream_from_buffer()) + * + * @param bytes where the input should be deserialized from. + */ + static Reader Wrap(const uint8_t* bytes, size_t length); + + /** + * Reads a message type from the input stream. + * + * This essentially wraps calls to nanopb's pb_decode_tag() method. + */ + Tag ReadTag(); + + void ReadNull(); + bool ReadBool(); + int64_t ReadInteger(); + + std::string ReadString(); + + /** + * Reads a message and its length. + * + * Analog to Writer::WriteNestedMessage(). See that methods docs for further + * details. + * + * Call this method when reading a nested message. Provide a function to read + * the message itself. + */ + template <typename T> + T ReadNestedMessage(const std::function<T(Reader*)>& read_message_fn); + + size_t bytes_left() const { + return stream_.bytes_left; + } + + private: + /** + * Creates a new Reader, based on the given nanopb pb_istream_t. Note that + * a shallow copy will be taken. (Non-null pointers within this struct must + * remain valid for the lifetime of this Reader.) + */ + explicit Reader(pb_istream_t stream) : stream_(stream) { + } + + /** + * Reads a "varint" from the input stream. + * + * This essentially wraps calls to nanopb's pb_decode_varint() method. + * + * Note that (despite the return type) this works for bool, enum, int32, + * int64, uint32 and uint64 proto field types. + * + * Note: This is not expected to be called direclty, but rather only via the + * other Decode* methods (i.e. DecodeBool, DecodeLong, etc) + * + * @return The decoded varint as a uint64_t. + */ + uint64_t ReadVarint(); + + pb_istream_t stream_; +}; + Writer Writer::Wrap(std::vector<uint8_t>* out_bytes) { // TODO(rsgowman): find a better home for this constant. // A document is defined to have a max size of 1MiB - 4 bytes. @@ -159,18 +249,33 @@ Writer Writer::Wrap(std::vector<uint8_t>* out_bytes) { return Writer(raw_stream); } +Reader Reader::Wrap(const uint8_t* bytes, size_t length) { + return Reader{pb_istream_from_buffer(bytes, length)}; +} + // TODO(rsgowman): I've left the methods as near as possible to where they were // before, which implies that the Writer methods are interspersed with the -// PbIstream methods (or what will become the PbIstream methods). This should -// make it a bit easier to review. Refactor these to group the related methods -// together (probably within their own file rather than here). +// Reader methods. This should make it a bit easier to review. Refactor these to +// group the related methods together (probably within their own file rather +// than here). + +void Writer::WriteTag(Tag tag) { + if (!status_.ok()) return; -void Writer::WriteTag(pb_wire_type_t wiretype, uint32_t field_number) { - bool status = pb_encode_tag(&stream_, wiretype, field_number); - if (!status) { + if (!pb_encode_tag(&stream_, tag.wire_type, tag.field_number)) { + FIREBASE_ASSERT_MESSAGE(false, PB_GET_ERROR(&stream_)); + } +} + +Tag Reader::ReadTag() { + Tag tag; + bool eof; + bool ok = pb_decode_tag(&stream_, &tag.wire_type, &tag.field_number, &eof); + if (!ok || eof) { // TODO(rsgowman): figure out error handling abort(); } + return tag; } void Writer::WriteSize(size_t size) { @@ -178,10 +283,10 @@ void Writer::WriteSize(size_t size) { } void Writer::WriteVarint(uint64_t value) { - bool status = pb_encode_varint(&stream_, value); - if (!status) { - // TODO(rsgowman): figure out error handling - abort(); + if (!status_.ok()) return; + + if (!pb_encode_varint(&stream_, value)) { + FIREBASE_ASSERT_MESSAGE(false, PB_GET_ERROR(&stream_)); } } @@ -189,15 +294,14 @@ void Writer::WriteVarint(uint64_t value) { * Note that (despite the return type) this works for bool, enum, int32, int64, * uint32 and uint64 proto field types. * - * Note: This is not expected to be called direclty, but rather only via the + * Note: This is not expected to be called directly, but rather only via the * other Decode* methods (i.e. DecodeBool, DecodeLong, etc) * * @return The decoded varint as a uint64_t. */ -uint64_t DecodeVarint(pb_istream_t* stream) { +uint64_t Reader::ReadVarint() { uint64_t varint_value; - bool status = pb_decode_varint(stream, &varint_value); - if (!status) { + if (!pb_decode_varint(&stream_, &varint_value)) { // TODO(rsgowman): figure out error handling abort(); } @@ -208,8 +312,8 @@ void Writer::WriteNull() { return WriteVarint(google_protobuf_NullValue_NULL_VALUE); } -void DecodeNull(pb_istream_t* stream) { - uint64_t varint = DecodeVarint(stream); +void Reader::ReadNull() { + uint64_t varint = ReadVarint(); if (varint != google_protobuf_NullValue_NULL_VALUE) { // TODO(rsgowman): figure out error handling abort(); @@ -220,8 +324,8 @@ void Writer::WriteBool(bool bool_value) { return WriteVarint(bool_value); } -bool DecodeBool(pb_istream_t* stream) { - uint64_t varint = DecodeVarint(stream); +bool Reader::ReadBool() { + uint64_t varint = ReadVarint(); switch (varint) { case 0: return false; @@ -237,32 +341,30 @@ void Writer::WriteInteger(int64_t integer_value) { return WriteVarint(integer_value); } -int64_t DecodeInteger(pb_istream_t* stream) { - return DecodeVarint(stream); +int64_t Reader::ReadInteger() { + return ReadVarint(); } void Writer::WriteString(const std::string& string_value) { - bool status = pb_encode_string( - &stream_, reinterpret_cast<const pb_byte_t*>(string_value.c_str()), - string_value.length()); - if (!status) { - // TODO(rsgowman): figure out error handling - abort(); + if (!status_.ok()) return; + + if (!pb_encode_string( + &stream_, reinterpret_cast<const pb_byte_t*>(string_value.c_str()), + string_value.length())) { + FIREBASE_ASSERT_MESSAGE(false, PB_GET_ERROR(&stream_)); } } -std::string DecodeString(pb_istream_t* stream) { +std::string Reader::ReadString() { pb_istream_t substream; - bool status = pb_make_string_substream(stream, &substream); - if (!status) { + if (!pb_make_string_substream(&stream_, &substream)) { // TODO(rsgowman): figure out error handling abort(); } std::string result(substream.bytes_left, '\0'); - status = pb_read(&substream, reinterpret_cast<pb_byte_t*>(&result[0]), - substream.bytes_left); - if (!status) { + if (!pb_read(&substream, reinterpret_cast<pb_byte_t*>(&result[0]), + substream.bytes_left)) { // TODO(rsgowman): figure out error handling abort(); } @@ -277,7 +379,7 @@ std::string DecodeString(pb_istream_t* stream) { abort(); } - pb_close_string_substream(stream, &substream); + pb_close_string_substream(&stream_, &substream); return result; } @@ -291,32 +393,32 @@ void EncodeFieldValueImpl(Writer* writer, const FieldValue& field_value) { // non-varint, non-fixed-size (i.e. string) type is present before doing so. switch (field_value.type()) { case FieldValue::Type::Null: - writer->WriteTag(PB_WT_VARINT, - google_firestore_v1beta1_Value_null_value_tag); + writer->WriteTag( + {PB_WT_VARINT, google_firestore_v1beta1_Value_null_value_tag}); writer->WriteNull(); break; case FieldValue::Type::Boolean: - writer->WriteTag(PB_WT_VARINT, - google_firestore_v1beta1_Value_boolean_value_tag); + writer->WriteTag( + {PB_WT_VARINT, google_firestore_v1beta1_Value_boolean_value_tag}); writer->WriteBool(field_value.boolean_value()); break; case FieldValue::Type::Integer: - writer->WriteTag(PB_WT_VARINT, - google_firestore_v1beta1_Value_integer_value_tag); + writer->WriteTag( + {PB_WT_VARINT, google_firestore_v1beta1_Value_integer_value_tag}); writer->WriteInteger(field_value.integer_value()); break; case FieldValue::Type::String: - writer->WriteTag(PB_WT_STRING, - google_firestore_v1beta1_Value_string_value_tag); + writer->WriteTag( + {PB_WT_STRING, google_firestore_v1beta1_Value_string_value_tag}); writer->WriteString(field_value.string_value()); break; case FieldValue::Type::Object: - writer->WriteTag(PB_WT_STRING, - google_firestore_v1beta1_Value_map_value_tag); + writer->WriteTag( + {PB_WT_STRING, google_firestore_v1beta1_Value_map_value_tag}); EncodeObject(writer, field_value.object_value()); break; @@ -326,30 +428,23 @@ void EncodeFieldValueImpl(Writer* writer, const FieldValue& field_value) { } } -FieldValue DecodeFieldValueImpl(pb_istream_t* stream) { - pb_wire_type_t wire_type; - uint32_t tag; - bool eof; - bool status = pb_decode_tag(stream, &wire_type, &tag, &eof); - if (!status) { - // TODO(rsgowman): figure out error handling - abort(); - } +FieldValue DecodeFieldValueImpl(Reader* reader) { + Tag tag = reader->ReadTag(); // Ensure the tag matches the wire type // TODO(rsgowman): figure out error handling - switch (tag) { + switch (tag.field_number) { case google_firestore_v1beta1_Value_null_value_tag: case google_firestore_v1beta1_Value_boolean_value_tag: case google_firestore_v1beta1_Value_integer_value_tag: - if (wire_type != PB_WT_VARINT) { + if (tag.wire_type != PB_WT_VARINT) { abort(); } break; case google_firestore_v1beta1_Value_string_value_tag: case google_firestore_v1beta1_Value_map_value_tag: - if (wire_type != PB_WT_STRING) { + if (tag.wire_type != PB_WT_STRING) { abort(); } break; @@ -358,19 +453,19 @@ FieldValue DecodeFieldValueImpl(pb_istream_t* stream) { abort(); } - switch (tag) { + switch (tag.field_number) { case google_firestore_v1beta1_Value_null_value_tag: - DecodeNull(stream); + reader->ReadNull(); return FieldValue::NullValue(); case google_firestore_v1beta1_Value_boolean_value_tag: - return FieldValue::BooleanValue(DecodeBool(stream)); + return FieldValue::BooleanValue(reader->ReadBool()); case google_firestore_v1beta1_Value_integer_value_tag: - return FieldValue::IntegerValue(DecodeInteger(stream)); + return FieldValue::IntegerValue(reader->ReadInteger()); case google_firestore_v1beta1_Value_string_value_tag: - return FieldValue::StringValue(DecodeString(stream)); + return FieldValue::StringValue(reader->ReadString()); case google_firestore_v1beta1_Value_map_value_tag: return FieldValue::ObjectValueFromMap( - DecodeObject(stream).internal_value); + DecodeObject(reader).internal_value); default: // TODO(rsgowman): figure out error handling @@ -380,31 +475,35 @@ FieldValue DecodeFieldValueImpl(pb_istream_t* stream) { void Writer::WriteNestedMessage( const std::function<void(Writer*)>& write_message_fn) { + if (!status_.ok()) return; + // First calculate the message size using a non-writing substream. Writer sizer = Writer::Sizing(); write_message_fn(&sizer); + status_ = sizer.status(); + if (!status_.ok()) return; size_t size = sizer.bytes_written(); // Write out the size to the output writer. WriteSize(size); + if (!status_.ok()) return; // If this stream is itself a sizing stream, then we don't need to actually // parse field_value a second time; just update the bytes_written via a call // to pb_write. (If we try to write the contents into a sizing stream, it'll // fail since sizing streams don't actually have any buffer space.) if (stream_.callback == nullptr) { - bool status = pb_write(&stream_, nullptr, size); - if (!status) { - // TODO(rsgowman): figure out error handling - abort(); + if (!pb_write(&stream_, nullptr, size)) { + FIREBASE_ASSERT_MESSAGE(false, PB_GET_ERROR(&stream_)); } return; } // Ensure the output stream has enough space if (stream_.bytes_written + size > stream_.max_size) { - // TODO(rsgowman): figure out error handling - abort(); + FIREBASE_ASSERT_MESSAGE( + false, + "Insufficient space in the output stream to write the given message"); } // Use a substream to verify that a callback doesn't write more than what it @@ -415,6 +514,8 @@ void Writer::WriteNestedMessage( /*max_size=*/size, /*bytes_written=*/0, /*errmsg=*/nullptr}); write_message_fn(&writer); + status_ = writer.status(); + if (!status_.ok()) return; stream_.bytes_written += writer.stream_.bytes_written; stream_.state = writer.stream_.state; @@ -422,35 +523,36 @@ void Writer::WriteNestedMessage( if (writer.bytes_written() != size) { // submsg size changed - // TODO(rsgowman): figure out error handling - abort(); + FIREBASE_ASSERT_MESSAGE( + false, "Parsing the nested message twice yielded different sizes"); } } -FieldValue DecodeNestedFieldValue(pb_istream_t* stream) { +template <typename T> +T Reader::ReadNestedMessage(const std::function<T(Reader*)>& read_message_fn) { // Implementation note: This is roughly modeled on pb_decode_delimited, // adjusted to account for the oneof in FieldValue. - pb_istream_t substream; - bool status = pb_make_string_substream(stream, &substream); - if (!status) { + pb_istream_t raw_substream; + if (!pb_make_string_substream(&stream_, &raw_substream)) { // TODO(rsgowman): figure out error handling abort(); } + Reader substream(raw_substream); - FieldValue fv = DecodeFieldValueImpl(&substream); + T message = read_message_fn(&substream); // NB: future versions of nanopb read the remaining characters out of the // substream (and return false if that fails) as an additional safety // check within pb_close_string_substream. Unfortunately, that's not present // in the current version (0.38). We'll make a stronger assertion and check // to make sure there *are* no remaining characters in the substream. - if (substream.bytes_left != 0) { + if (substream.bytes_left() != 0) { // TODO(rsgowman): figure out error handling abort(); } - pb_close_string_substream(stream, &substream); + pb_close_string_substream(&stream_, &substream.stream_); - return fv; + return message; } /** @@ -475,102 +577,90 @@ FieldValue DecodeNestedFieldValue(pb_istream_t* stream) { */ void EncodeFieldsEntry(Writer* writer, const ObjectValue::Map::value_type& kv) { // Write the key (string) - writer->WriteTag(PB_WT_STRING, - google_firestore_v1beta1_MapValue_FieldsEntry_key_tag); + writer->WriteTag( + {PB_WT_STRING, google_firestore_v1beta1_MapValue_FieldsEntry_key_tag}); writer->WriteString(kv.first); // Write the value (FieldValue) - writer->WriteTag(PB_WT_STRING, - google_firestore_v1beta1_MapValue_FieldsEntry_value_tag); + writer->WriteTag( + {PB_WT_STRING, google_firestore_v1beta1_MapValue_FieldsEntry_value_tag}); writer->WriteNestedMessage( [&kv](Writer* writer) { EncodeFieldValueImpl(writer, kv.second); }); } -ObjectValue::Map::value_type DecodeFieldsEntry(pb_istream_t* stream) { - pb_wire_type_t wire_type; - uint32_t tag; - bool eof; - bool status = pb_decode_tag(stream, &wire_type, &tag, &eof); +ObjectValue::Map::value_type DecodeFieldsEntry(Reader* reader) { + Tag tag = reader->ReadTag(); + // TODO(rsgowman): figure out error handling: We can do better than a failed // assertion. - FIREBASE_ASSERT(tag == google_firestore_v1beta1_MapValue_FieldsEntry_key_tag); - FIREBASE_ASSERT(wire_type == PB_WT_STRING); - FIREBASE_ASSERT(!eof); - FIREBASE_ASSERT(status); - std::string key = DecodeString(stream); - - status = pb_decode_tag(stream, &wire_type, &tag, &eof); - FIREBASE_ASSERT(tag == + FIREBASE_ASSERT(tag.field_number == + google_firestore_v1beta1_MapValue_FieldsEntry_key_tag); + FIREBASE_ASSERT(tag.wire_type == PB_WT_STRING); + std::string key = reader->ReadString(); + + tag = reader->ReadTag(); + FIREBASE_ASSERT(tag.field_number == google_firestore_v1beta1_MapValue_FieldsEntry_value_tag); - FIREBASE_ASSERT(wire_type == PB_WT_STRING); - FIREBASE_ASSERT(!eof); - FIREBASE_ASSERT(status); + FIREBASE_ASSERT(tag.wire_type == PB_WT_STRING); - FieldValue value = DecodeNestedFieldValue(stream); + FieldValue value = + reader->ReadNestedMessage<FieldValue>(DecodeFieldValueImpl); return {key, value}; } void EncodeObject(Writer* writer, const ObjectValue& object_value) { - writer->WriteNestedMessage([&object_value](Writer* writer) { + return writer->WriteNestedMessage([&object_value](Writer* writer) { // Write each FieldsEntry (i.e. key-value pair.) for (const auto& kv : object_value.internal_value) { - writer->WriteTag(PB_WT_STRING, - google_firestore_v1beta1_MapValue_FieldsEntry_key_tag); + writer->WriteTag({PB_WT_STRING, + google_firestore_v1beta1_MapValue_FieldsEntry_key_tag}); writer->WriteNestedMessage( - [&kv](Writer* writer) { EncodeFieldsEntry(writer, kv); }); + [&kv](Writer* writer) { return EncodeFieldsEntry(writer, kv); }); } - - return true; }); } -ObjectValue DecodeObject(pb_istream_t* stream) { - google_firestore_v1beta1_MapValue map_value = - google_firestore_v1beta1_MapValue_init_zero; - ObjectValue::Map result; - // NB: c-style callbacks can't use *capturing* lambdas, so we'll pass in the - // object_value via the arg field (and therefore need to do a bunch of - // casting). - map_value.fields.funcs.decode = [](pb_istream_t* stream, const pb_field_t*, - void** arg) -> bool { - auto& result = *static_cast<ObjectValue::Map*>(*arg); - - ObjectValue::Map::value_type fv = DecodeFieldsEntry(stream); - - // Sanity check: ensure that this key doesn't already exist in the map. - // TODO(rsgowman): figure out error handling: We can do better than a failed - // assertion. - FIREBASE_ASSERT(result.find(fv.first) == result.end()); - - // Add this key,fieldvalue to the results map. - result.emplace(std::move(fv)); - - return true; - }; - map_value.fields.arg = &result; - - bool status = pb_decode_delimited( - stream, google_firestore_v1beta1_MapValue_fields, &map_value); - if (!status) { - // TODO(rsgowman): figure out error handling - abort(); - } - - return ObjectValue{result}; +ObjectValue DecodeObject(Reader* reader) { + ObjectValue::Map internal_value = reader->ReadNestedMessage<ObjectValue::Map>( + [](Reader* reader) -> ObjectValue::Map { + ObjectValue::Map result; + while (reader->bytes_left()) { + Tag tag = reader->ReadTag(); + FIREBASE_ASSERT(tag.field_number == + google_firestore_v1beta1_MapValue_fields_tag); + FIREBASE_ASSERT(tag.wire_type == PB_WT_STRING); + + ObjectValue::Map::value_type fv = + reader->ReadNestedMessage<ObjectValue::Map::value_type>( + DecodeFieldsEntry); + + // Sanity check: ensure that this key doesn't already exist in the + // map. + // TODO(rsgowman): figure out error handling: We can do better than a + // failed assertion. + FIREBASE_ASSERT(result.find(fv.first) == result.end()); + + // Add this key,fieldvalue to the results map. + result.emplace(std::move(fv)); + } + return result; + }); + return ObjectValue{internal_value}; } } // namespace -void Serializer::EncodeFieldValue(const FieldValue& field_value, - std::vector<uint8_t>* out_bytes) { +Status Serializer::EncodeFieldValue(const FieldValue& field_value, + std::vector<uint8_t>* out_bytes) { Writer writer = Writer::Wrap(out_bytes); EncodeFieldValueImpl(&writer, field_value); + return writer.status(); } FieldValue Serializer::DecodeFieldValue(const uint8_t* bytes, size_t length) { - pb_istream_t stream = pb_istream_from_buffer(bytes, length); - return DecodeFieldValueImpl(&stream); + Reader reader = Reader::Wrap(bytes, length); + return DecodeFieldValueImpl(&reader); } } // namespace remote diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.h b/Firestore/core/src/firebase/firestore/remote/serializer.h index 10cacbc..7541ef5 100644 --- a/Firestore/core/src/firebase/firestore/remote/serializer.h +++ b/Firestore/core/src/firebase/firestore/remote/serializer.h @@ -17,13 +17,15 @@ #ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_SERIALIZER_H_ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_SERIALIZER_H_ -#include <stdint.h> -#include <stdlib.h> +#include <cstdint> +#include <cstdlib> #include <vector> #include "Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.h" #include "Firestore/core/src/firebase/firestore/model/field_value.h" #include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "Firestore/core/src/firebase/firestore/util/status.h" +#include "absl/base/attributes.h" namespace firebase { namespace firestore { @@ -70,7 +72,7 @@ class Serializer { // TODO(rsgowman): If we never support any output except to a vector, it may // make sense to have Serializer own the vector and provide an accessor rather // than asking the user to create it first. - static void EncodeFieldValue( + util::Status EncodeFieldValue( const firebase::firestore::model::FieldValue& field_value, std::vector<uint8_t>* out_bytes); @@ -82,8 +84,7 @@ class Serializer { * @return The model equivalent of the bytes. */ // TODO(rsgowman): error handling. - static firebase::firestore::model::FieldValue DecodeFieldValue( - const uint8_t* bytes, size_t length); + model::FieldValue DecodeFieldValue(const uint8_t* bytes, size_t length); /** * @brief Converts from bytes to the model FieldValue format. @@ -93,14 +94,13 @@ class Serializer { * @return The model equivalent of the bytes. */ // TODO(rsgowman): error handling. - static firebase::firestore::model::FieldValue DecodeFieldValue( - const std::vector<uint8_t>& bytes) { + model::FieldValue DecodeFieldValue(const std::vector<uint8_t>& bytes) { return DecodeFieldValue(bytes.data(), bytes.size()); } private: // TODO(rsgowman): We don't need the database_id_ yet (but will eventually). - // const firebase::firestore::model::DatabaseId& database_id_; + // model::DatabaseId* database_id_; }; } // namespace remote diff --git a/Firestore/core/src/firebase/firestore/timestamp.cc b/Firestore/core/src/firebase/firestore/timestamp.cc index 7d947c5..a5f0121 100644 --- a/Firestore/core/src/firebase/firestore/timestamp.cc +++ b/Firestore/core/src/firebase/firestore/timestamp.cc @@ -48,7 +48,7 @@ Timestamp Timestamp::Now() { } Timestamp Timestamp::FromTimeT(const time_t seconds_since_unix_epoch) { - return Timestamp(seconds_since_unix_epoch, 0); + return {seconds_since_unix_epoch, 0}; } #if !defined(_STLPORT_VERSION) diff --git a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt index eac7053..701d288 100644 --- a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt @@ -31,9 +31,11 @@ cc_library( ## assert and log cc_library( - firebase_firestore_util_stdio + firebase_firestore_util_log_stdio SOURCES + firebase_assert.h assert_stdio.cc + log.h log_stdio.cc DEPENDS firebase_firestore_util_base @@ -42,9 +44,11 @@ cc_library( ) cc_library( - firebase_firestore_util_apple + firebase_firestore_util_log_apple SOURCES + firebase_assert.h assert_apple.mm + log.h log_apple.mm string_apple.h DEPENDS @@ -53,12 +57,16 @@ cc_library( EXCLUDE_FROM_ALL ) -# Export a dependency on the correct logging library for this platform. All -# buildable libraries are built and tested but only the best fit is exported. if(APPLE) - list(APPEND UTIL_DEPENDS firebase_firestore_util_apple) + set( + FIREBASE_FIRESTORE_UTIL_LOG + firebase_firestore_util_log_apple + ) else() - list(APPEND UTIL_DEPENDS firebase_firestore_util_stdio) + set( + FIREBASE_FIRESTORE_UTIL_LOG + firebase_firestore_util_log_stdio + ) endif() @@ -66,7 +74,7 @@ endif() check_symbol_exists(arc4random stdlib.h HAVE_ARC4RANDOM) cc_library( - firebase_firestore_util_arc4random + firebase_firestore_util_random_arc4random SOURCES secure_random_arc4random.cc ) @@ -77,7 +85,7 @@ get_target_property( ) check_include_files(openssl/rand.h HAVE_OPENSSL_RAND_H) cc_library( - firebase_firestore_util_openssl + firebase_firestore_util_random_openssl SOURCES secure_random_openssl.cc DEPENDS @@ -85,10 +93,16 @@ cc_library( ) if(HAVE_ARC4RANDOM) - list(APPEND UTIL_DEPENDS firebase_firestore_util_arc4random) + set( + FIREBASE_FIRESTORE_UTIL_RANDOM + firebase_firestore_util_random_arc4random + ) elseif(HAVE_OPENSSL_RAND_H) - list(APPEND UTIL_DEPENDS firebase_firestore_util_openssl) + set( + FIREBASE_FIRESTORE_UTIL_RANDOM + firebase_firestore_util_random_openssl + ) else() message(FATAL_ERROR "No implementation for SecureRandom available.") @@ -97,6 +111,7 @@ endif() ## main library + configure_file( config.h.in config.h @@ -113,9 +128,7 @@ cc_library( comparison.cc comparison.h config.h - firebase_assert.h iterator_adaptors.h - log.h ordered_code.cc ordered_code.h secure_random.h @@ -127,7 +140,8 @@ cc_library( string_util.cc string_util.h DEPENDS - ${UTIL_DEPENDS} - firebase_firestore_util_base absl_base + firebase_firestore_util_base + ${FIREBASE_FIRESTORE_UTIL_LOG} + ${FIREBASE_FIRESTORE_UTIL_RANDOM} ) diff --git a/Firestore/core/src/firebase/firestore/util/assert_apple.mm b/Firestore/core/src/firebase/firestore/util/assert_apple.mm index 83b76e1..9b6a651 100644 --- a/Firestore/core/src/firebase/firestore/util/assert_apple.mm +++ b/Firestore/core/src/firebase/firestore/util/assert_apple.mm @@ -14,12 +14,10 @@ * limitations under the License. */ -#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" - #import <Foundation/Foundation.h> -#include <string.h> - +// TODO(wilhuff): match basenames so this can move up top +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" #include "Firestore/core/src/firebase/firestore/util/string_apple.h" namespace firebase { diff --git a/Firestore/core/src/firebase/firestore/util/assert_stdio.cc b/Firestore/core/src/firebase/firestore/util/assert_stdio.cc index 1d2e333..e01e564 100644 --- a/Firestore/core/src/firebase/firestore/util/assert_stdio.cc +++ b/Firestore/core/src/firebase/firestore/util/assert_stdio.cc @@ -14,8 +14,7 @@ * limitations under the License. */ -#include <stdarg.h> - +#include <cstdarg> #include <stdexcept> #include <string> diff --git a/Firestore/core/src/firebase/firestore/util/bits.h b/Firestore/core/src/firebase/firestore/util/bits.h index 185273f..94a018f 100644 --- a/Firestore/core/src/firebase/firestore/util/bits.h +++ b/Firestore/core/src/firebase/firestore/util/bits.h @@ -22,7 +22,7 @@ // Munging bits in _signed_ integers is fraught with peril! For example, // -5 << n has undefined behavior (for some values of n). -#include <stdint.h> +#include <cstdint> class Bits_Port32_Test; class Bits_Port64_Test; @@ -139,23 +139,23 @@ inline int Bits::Log2FloorNonZero_Portable(uint32_t n) { // Log2Floor64() is defined in terms of Log2Floor32(), Log2FloorNonZero32() inline int Bits::Log2Floor64_Portable(uint64_t n) { - const uint32_t topbits = static_cast<uint32_t>(n >> 32); - if (topbits == 0) { + const auto top_bits = static_cast<uint32_t>(n >> 32); + if (top_bits == 0) { // Top bits are zero, so scan in bottom bits return Log2Floor(static_cast<uint32_t>(n)); } else { - return 32 + Log2FloorNonZero(topbits); + return 32 + Log2FloorNonZero(top_bits); } } // Log2FloorNonZero64() is defined in terms of Log2FloorNonZero32() inline int Bits::Log2FloorNonZero64_Portable(uint64_t n) { - const uint32_t topbits = static_cast<uint32_t>(n >> 32); - if (topbits == 0) { + const auto top_bits = static_cast<uint32_t>(n >> 32); + if (top_bits == 0) { // Top bits are zero, so scan in bottom bits return Log2FloorNonZero(static_cast<uint32_t>(n)); } else { - return 32 + Log2FloorNonZero(topbits); + return 32 + Log2FloorNonZero(top_bits); } } diff --git a/Firestore/core/src/firebase/firestore/util/comparator_holder.h b/Firestore/core/src/firebase/firestore/util/comparator_holder.h index 8641b0f..c7f6144 100644 --- a/Firestore/core/src/firebase/firestore/util/comparator_holder.h +++ b/Firestore/core/src/firebase/firestore/util/comparator_holder.h @@ -47,7 +47,7 @@ class ComparatorHolder { template <typename C> class ComparatorHolder<C, true> : private C { protected: - explicit ComparatorHolder(const C&) noexcept { + explicit ComparatorHolder(const C& /* comparator */) noexcept { } const C& comparator() const noexcept { diff --git a/Firestore/core/src/firebase/firestore/util/comparison.cc b/Firestore/core/src/firebase/firestore/util/comparison.cc index 4bef843..5ac4c27 100644 --- a/Firestore/core/src/firebase/firestore/util/comparison.cc +++ b/Firestore/core/src/firebase/firestore/util/comparison.cc @@ -16,10 +16,11 @@ #include "Firestore/core/src/firebase/firestore/util/comparison.h" -#include <math.h> - +#include <cmath> #include <limits> +using std::isnan; + namespace firebase { namespace firestore { namespace util { @@ -77,7 +78,7 @@ ComparisonResult CompareMixedNumber(double double_value, int64_t int64_value) { // At this point the long representations are equal but this could be due to // rounding. - double int64_as_double = static_cast<double>(int64_value); + auto int64_as_double = static_cast<double>(int64_value); return Compare<double>(double_value, int64_as_double); } diff --git a/Firestore/core/src/firebase/firestore/util/comparison.h b/Firestore/core/src/firebase/firestore/util/comparison.h index 6fd1e2b..23207f5 100644 --- a/Firestore/core/src/firebase/firestore/util/comparison.h +++ b/Firestore/core/src/firebase/firestore/util/comparison.h @@ -21,9 +21,9 @@ #import <Foundation/Foundation.h> #endif -#include <stdint.h> #include <sys/types.h> +#include <cstdint> #include <functional> #include <string> #include <vector> diff --git a/Firestore/core/src/firebase/firestore/util/firebase_assert.h b/Firestore/core/src/firebase/firestore/util/firebase_assert.h index 20c8429..6a9c2eb 100644 --- a/Firestore/core/src/firebase/firestore/util/firebase_assert.h +++ b/Firestore/core/src/firebase/firestore/util/firebase_assert.h @@ -20,7 +20,7 @@ #ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_FIREBASE_ASSERT_H_ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_FIREBASE_ASSERT_H_ -#include <stdlib.h> +#include <cstdlib> #include "Firestore/core/src/firebase/firestore/util/log.h" #include "absl/base/attributes.h" @@ -76,10 +76,6 @@ } \ } while (0) -// Assert with custom message that is not compiled out in release builds. -#define FIREBASE_ASSERT_MESSAGE(expression, ...) \ - FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(expression, expression, __VA_ARGS__) - // Assert condition is true otherwise display the specified expression, // message and abort. Compiled out of release builds. #if defined(NDEBUG) @@ -103,16 +99,18 @@ FIREBASE_DEV_ASSERT_MESSAGE_WITH_EXPRESSION(expression, expression, \ __VA_ARGS__) +// Indicates an area of the code that cannot be reached (except possibly due to +// undefined behaviour or other similar badness). The only reasonable thing to +// do in these cases is to immediately abort. +#define FIREBASE_UNREACHABLE() abort() + namespace firebase { namespace firestore { namespace util { // A no-return helper function. To raise an assertion, use Macro instead. -ABSL_ATTRIBUTE_NORETURN void FailAssert(const char* file, - const char* func, - const int line, - const char* format, - ...); +ABSL_ATTRIBUTE_NORETURN void FailAssert( + const char* file, const char* func, int line, const char* format, ...); } // namespace util } // namespace firestore diff --git a/Firestore/core/src/firebase/firestore/util/log.h b/Firestore/core/src/firebase/firestore/util/log.h index d0cff4d..1944596 100644 --- a/Firestore/core/src/firebase/firestore/util/log.h +++ b/Firestore/core/src/firebase/firestore/util/log.h @@ -17,7 +17,7 @@ #ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_LOG_H_ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_LOG_H_ -#include <stdarg.h> +#include <cstdarg> namespace firebase { namespace firestore { diff --git a/Firestore/core/src/firebase/firestore/util/log_stdio.cc b/Firestore/core/src/firebase/firestore/util/log_stdio.cc index bca2dc9..b277406 100644 --- a/Firestore/core/src/firebase/firestore/util/log_stdio.cc +++ b/Firestore/core/src/firebase/firestore/util/log_stdio.cc @@ -16,7 +16,7 @@ #include "Firestore/core/src/firebase/firestore/util/log.h" -#include <stdio.h> +#include <cstdio> #include <string> namespace firebase { diff --git a/Firestore/core/src/firebase/firestore/util/ordered_code.cc b/Firestore/core/src/firebase/firestore/util/ordered_code.cc index 0eadf18..cb53b09 100644 --- a/Firestore/core/src/firebase/firestore/util/ordered_code.cc +++ b/Firestore/core/src/firebase/firestore/util/ordered_code.cc @@ -16,12 +16,11 @@ #include "Firestore/core/src/firebase/firestore/util/ordered_code.h" -#include <absl/base/internal/endian.h> -#include <absl/base/internal/unaligned_access.h> -#include <absl/base/port.h> - #include "Firestore/core/src/firebase/firestore/util/bits.h" #include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "absl/base/internal/endian.h" +#include "absl/base/internal/unaligned_access.h" +#include "absl/base/port.h" #define UNALIGNED_LOAD32 ABSL_INTERNAL_UNALIGNED_LOAD32 #define UNALIGNED_LOAD64 ABSL_INTERNAL_UNALIGNED_LOAD64 diff --git a/Firestore/core/src/firebase/firestore/util/secure_random.h b/Firestore/core/src/firebase/firestore/util/secure_random.h index 95b41e1..f030b5e 100644 --- a/Firestore/core/src/firebase/firestore/util/secure_random.h +++ b/Firestore/core/src/firebase/firestore/util/secure_random.h @@ -17,7 +17,7 @@ #ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_SECURE_RANDOM_H_ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_SECURE_RANDOM_H_ -#include <stdint.h> +#include <cstdint> #include <limits> diff --git a/Firestore/core/src/firebase/firestore/util/secure_random_arc4random.cc b/Firestore/core/src/firebase/firestore/util/secure_random_arc4random.cc index 83f72b5..d7e9be3 100644 --- a/Firestore/core/src/firebase/firestore/util/secure_random_arc4random.cc +++ b/Firestore/core/src/firebase/firestore/util/secure_random_arc4random.cc @@ -20,7 +20,7 @@ #if HAVE_ARC4RANDOM -#include <stdlib.h> +#include <cstdlib> namespace firebase { namespace firestore { diff --git a/Firestore/core/src/firebase/firestore/util/secure_random_openssl.cc b/Firestore/core/src/firebase/firestore/util/secure_random_openssl.cc index d3f6e63..e024846 100644 --- a/Firestore/core/src/firebase/firestore/util/secure_random_openssl.cc +++ b/Firestore/core/src/firebase/firestore/util/secure_random_openssl.cc @@ -20,8 +20,8 @@ #if HAVE_OPENSSL_RAND_H -#include <openssl/err.h> -#include <openssl/rand.h> +#include "openssl/err.h" +#include "openssl/rand.h" namespace firebase { namespace firestore { diff --git a/Firestore/core/src/firebase/firestore/util/string_printf.cc b/Firestore/core/src/firebase/firestore/util/string_printf.cc index 9c4e31c..c5483f4 100644 --- a/Firestore/core/src/firebase/firestore/util/string_printf.cc +++ b/Firestore/core/src/firebase/firestore/util/string_printf.cc @@ -16,7 +16,7 @@ #include "Firestore/core/src/firebase/firestore/util/string_printf.h" -#include <stdio.h> +#include <cstdio> namespace firebase { namespace firestore { diff --git a/Firestore/core/src/firebase/firestore/util/string_printf.h b/Firestore/core/src/firebase/firestore/util/string_printf.h index 10dfae9..553af66 100644 --- a/Firestore/core/src/firebase/firestore/util/string_printf.h +++ b/Firestore/core/src/firebase/firestore/util/string_printf.h @@ -17,8 +17,7 @@ #ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_PRINTF_H_ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_STRING_PRINTF_H_ -#include <stdarg.h> - +#include <cstdarg> #include <string> #include "absl/base/attributes.h" diff --git a/Firestore/core/test/firebase/firestore/immutable/array_sorted_map_test.cc b/Firestore/core/test/firebase/firestore/immutable/array_sorted_map_test.cc index fceab7d..6758dd5 100644 --- a/Firestore/core/test/firebase/firestore/immutable/array_sorted_map_test.cc +++ b/Firestore/core/test/firebase/firestore/immutable/array_sorted_map_test.cc @@ -29,7 +29,7 @@ namespace firestore { namespace immutable { namespace impl { -typedef ArraySortedMap<int, int> IntMap; +using IntMap = ArraySortedMap<int, int>; constexpr IntMap::size_type kFixedSize = IntMap::kFixedSize; // TODO(wilhuff): ReverseTraversal @@ -153,7 +153,7 @@ TEST(ArraySortedMap, EmptyRemoval) { TEST(ArraySortedMap, InsertionAndRemovalOfMaxItems) { auto expected_size = kFixedSize; - int n = static_cast<int>(expected_size); + auto n = static_cast<int>(expected_size); std::vector<int> to_insert = Shuffled(Sequence(n)); std::vector<int> to_remove = Shuffled(to_insert); diff --git a/Firestore/core/test/firebase/firestore/immutable/sorted_map_test.cc b/Firestore/core/test/firebase/firestore/immutable/sorted_map_test.cc index 44dca50..747c66b 100644 --- a/Firestore/core/test/firebase/firestore/immutable/sorted_map_test.cc +++ b/Firestore/core/test/firebase/firestore/immutable/sorted_map_test.cc @@ -49,6 +49,7 @@ class SortedMapTest : public ::testing::Test { } }; +// NOLINTNEXTLINE: must be a typedef for the gtest macros typedef ::testing::Types<SortedMap<int, int>, impl::ArraySortedMap<int, int>, impl::TreeSortedMap<int, int>> diff --git a/Firestore/core/test/firebase/firestore/immutable/tree_sorted_map_test.cc b/Firestore/core/test/firebase/firestore/immutable/tree_sorted_map_test.cc index 7a96b67..c03dc6c 100644 --- a/Firestore/core/test/firebase/firestore/immutable/tree_sorted_map_test.cc +++ b/Firestore/core/test/firebase/firestore/immutable/tree_sorted_map_test.cc @@ -25,7 +25,7 @@ namespace firestore { namespace immutable { namespace impl { -typedef TreeSortedMap<int, int> IntMap; +using IntMap = TreeSortedMap<int, int>; TEST(TreeSortedMap, EmptySize) { IntMap map; diff --git a/Firestore/core/test/firebase/firestore/model/CMakeLists.txt b/Firestore/core/test/firebase/firestore/model/CMakeLists.txt index 2c2281f..9c94677 100644 --- a/Firestore/core/test/firebase/firestore/model/CMakeLists.txt +++ b/Firestore/core/test/firebase/firestore/model/CMakeLists.txt @@ -18,12 +18,16 @@ cc_test( database_id_test.cc document_key_test.cc document_test.cc + field_mask_test.cc field_path_test.cc + field_transform_test.cc field_value_test.cc maybe_document_test.cc no_document_test.cc + precondition_test.cc resource_path_test.cc snapshot_version_test.cc + transform_operations_test.cc DEPENDS firebase_firestore_model ) diff --git a/Firestore/core/test/firebase/firestore/model/document_key_test.cc b/Firestore/core/test/firebase/firestore/model/document_key_test.cc index 0e0df2d..619ee7f 100644 --- a/Firestore/core/test/firebase/firestore/model/document_key_test.cc +++ b/Firestore/core/test/firebase/firestore/model/document_key_test.cc @@ -46,7 +46,7 @@ TEST(DocumentKey, Constructor_FromPath) { EXPECT_EQ(key_from_path_copy.path(), path); const DocumentKey key_from_moved_path{std::move(path)}; - EXPECT_TRUE(path.empty()); + EXPECT_TRUE(path.empty()); // NOLINT: use after move intended EXPECT_FALSE(key_from_moved_path.path().empty()); EXPECT_EQ(key_from_path_copy.path(), key_from_moved_path.path()); } @@ -62,7 +62,7 @@ TEST(DocumentKey, CopyAndMove) { const DocumentKey moved = std::move(key); EXPECT_EQ(path_string, moved.path().CanonicalString()); - EXPECT_NE(key, moved); + EXPECT_NE(key, moved); // NOLINT: use after move intended EXPECT_TRUE(key.path().empty()); // Reassignment. @@ -74,7 +74,7 @@ TEST(DocumentKey, CopyAndMove) { key = {}; EXPECT_TRUE(key.path().empty()); key = std::move(copied); - EXPECT_NE(copied, key); + EXPECT_NE(copied, key); // NOLINT: use after move intended EXPECT_TRUE(copied.path().empty()); EXPECT_EQ(path_string, key.path().CanonicalString()); } diff --git a/Firestore/core/test/firebase/firestore/model/field_mask_test.cc b/Firestore/core/test/firebase/firestore/model/field_mask_test.cc new file mode 100644 index 0000000..52d5951 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/model/field_mask_test.cc @@ -0,0 +1,56 @@ +/* + * Copyright 2018 Google + * + * Licensed 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/field_mask.h" + +#include <vector> + +#include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace model { + +TEST(FieldMask, ConstructorAndEqual) { + FieldMask mask_a{FieldPath::FromServerFormat("foo"), + FieldPath::FromServerFormat("bar")}; + std::vector<FieldPath> field_path_vector{FieldPath::FromServerFormat("foo"), + FieldPath::FromServerFormat("bar")}; + FieldMask mask_b{field_path_vector}; + FieldMask mask_c{std::vector<FieldPath>{FieldPath::FromServerFormat("foo"), + FieldPath::FromServerFormat("bar")}}; + EXPECT_EQ(mask_a, mask_b); + EXPECT_EQ(mask_b, mask_c); +} + +TEST(FieldMask, Getter) { + FieldMask mask{FieldPath::FromServerFormat("foo"), + FieldPath::FromServerFormat("bar")}; + EXPECT_EQ(std::vector<FieldPath>({FieldPath::FromServerFormat("foo"), + FieldPath::FromServerFormat("bar")}), + std::vector<FieldPath>(mask.begin(), mask.end())); +} + +TEST(FieldMask, ToString) { + FieldMask mask{FieldPath::FromServerFormat("foo"), + FieldPath::FromServerFormat("bar")}; + EXPECT_EQ("{ foo bar }", mask.ToString()); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/model/field_path_test.cc b/Firestore/core/test/firebase/firestore/model/field_path_test.cc index a5ae3b2..a215a96 100644 --- a/Firestore/core/test/firebase/firestore/model/field_path_test.cc +++ b/Firestore/core/test/firebase/firestore/model/field_path_test.cc @@ -47,7 +47,7 @@ TEST(FieldPath, Constructors) { EXPECT_EQ(path_from_list, copied); const FieldPath moved = std::move(copied); EXPECT_EQ(path_from_list, moved); - EXPECT_NE(copied, moved); + EXPECT_NE(copied, moved); // NOLINT: use after move intended EXPECT_EQ(empty_path, copied); } @@ -68,7 +68,7 @@ TEST(FieldPath, PopFirst) { const FieldPath bc{"Eros", "messages"}; const FieldPath c{"messages"}; const FieldPath empty; - const FieldPath abc_dupl{"rooms", "Eros", "messages"}; + const FieldPath abc_dup{"rooms", "Eros", "messages"}; EXPECT_NE(empty, c); EXPECT_NE(c, bc); @@ -77,7 +77,7 @@ TEST(FieldPath, PopFirst) { EXPECT_EQ(bc, abc.PopFirst()); EXPECT_EQ(c, abc.PopFirst(2)); EXPECT_EQ(empty, abc.PopFirst(3)); - EXPECT_EQ(abc_dupl, abc); + EXPECT_EQ(abc_dup, abc); } TEST(FieldPath, PopLast) { @@ -85,7 +85,7 @@ TEST(FieldPath, PopLast) { const FieldPath ab{"rooms", "Eros"}; const FieldPath a{"rooms"}; const FieldPath empty; - const FieldPath abc_dupl{"rooms", "Eros", "messages"}; + const FieldPath abc_dup{"rooms", "Eros", "messages"}; EXPECT_EQ(ab, abc.PopLast()); EXPECT_EQ(a, abc.PopLast().PopLast()); diff --git a/Firestore/core/test/firebase/firestore/model/field_transform_test.cc b/Firestore/core/test/firebase/firestore/model/field_transform_test.cc new file mode 100644 index 0000000..b66aeef --- /dev/null +++ b/Firestore/core/test/firebase/firestore/model/field_transform_test.cc @@ -0,0 +1,39 @@ +/* + * Copyright 2018 Google + * + * Licensed 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/field_transform.h" +#include "Firestore/core/src/firebase/firestore/model/transform_operations.h" +#include "Firestore/core/test/firebase/firestore/testutil/testutil.h" + +#include "absl/memory/memory.h" +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace model { + +TEST(FieldTransform, Getter) { + FieldTransform transform(testutil::Field("foo"), + absl::make_unique<ServerTimestampTransform>( + ServerTimestampTransform::Get())); + + EXPECT_EQ(testutil::Field("foo"), transform.path()); + EXPECT_EQ(ServerTimestampTransform::Get(), transform.transformation()); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/model/field_value_test.cc b/Firestore/core/test/firebase/firestore/model/field_value_test.cc index 40be2d5..c5645a4 100644 --- a/Firestore/core/test/firebase/firestore/model/field_value_test.cc +++ b/Firestore/core/test/firebase/firestore/model/field_value_test.cc @@ -16,8 +16,7 @@ #include "Firestore/core/src/firebase/firestore/model/field_value.h" -#include <limits.h> - +#include <climits> #include <vector> #include "gtest/gtest.h" @@ -412,7 +411,7 @@ TEST(FieldValue, Move) { EXPECT_EQ(FieldValue::ReferenceValue(DocumentKey::FromPathString("root/abc"), &database_id), clone); - clone = null_value; + clone = null_value; // NOLINT: use after move intended EXPECT_EQ(FieldValue::NullValue(), clone); FieldValue geo_point_value = FieldValue::GeoPointValue({1, 2}); diff --git a/Firestore/core/test/firebase/firestore/model/precondition_test.cc b/Firestore/core/test/firebase/firestore/model/precondition_test.cc new file mode 100644 index 0000000..3ddb2ba --- /dev/null +++ b/Firestore/core/test/firebase/firestore/model/precondition_test.cc @@ -0,0 +1,77 @@ +/* + * Copyright 2018 Google + * + * Licensed 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/precondition.h" + +#include "Firestore/core/src/firebase/firestore/model/document.h" +#include "Firestore/core/src/firebase/firestore/model/no_document.h" +#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" +#include "Firestore/core/test/firebase/firestore/testutil/testutil.h" + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace model { + +TEST(Precondition, None) { + const Precondition none = Precondition::None(); + EXPECT_EQ(Precondition::Type::None, none.type()); + EXPECT_TRUE(none.IsNone()); + EXPECT_EQ(SnapshotVersion::None(), none.update_time()); + + const NoDocument deleted_doc = testutil::DeletedDoc("foo/doc", 1234567); + const Document doc = testutil::Doc("bar/doc", 7654321); + EXPECT_TRUE(none.IsValidFor(deleted_doc)); + EXPECT_TRUE(none.IsValidFor(doc)); +} + +TEST(Precondition, Exists) { + const Precondition exists = Precondition::Exists(true); + const Precondition no_exists = Precondition::Exists(false); + EXPECT_EQ(Precondition::Type::Exists, exists.type()); + EXPECT_EQ(Precondition::Type::Exists, no_exists.type()); + EXPECT_FALSE(exists.IsNone()); + EXPECT_FALSE(no_exists.IsNone()); + EXPECT_EQ(SnapshotVersion::None(), exists.update_time()); + EXPECT_EQ(SnapshotVersion::None(), no_exists.update_time()); + + const NoDocument deleted_doc = testutil::DeletedDoc("foo/doc", 1234567); + const Document doc = testutil::Doc("bar/doc", 7654321); + EXPECT_FALSE(exists.IsValidFor(deleted_doc)); + EXPECT_TRUE(exists.IsValidFor(doc)); + EXPECT_TRUE(no_exists.IsValidFor(deleted_doc)); + EXPECT_FALSE(no_exists.IsValidFor(doc)); +} + +TEST(Precondition, UpdateTime) { + const Precondition update_time = + Precondition::UpdateTime(testutil::Version(1234567)); + EXPECT_EQ(Precondition::Type::UpdateTime, update_time.type()); + EXPECT_FALSE(update_time.IsNone()); + EXPECT_EQ(testutil::Version(1234567), update_time.update_time()); + + const NoDocument deleted_doc = testutil::DeletedDoc("foo/doc", 1234567); + const Document not_match = testutil::Doc("bar/doc", 7654321); + const Document match = testutil::Doc("baz/doc", 1234567); + EXPECT_FALSE(update_time.IsValidFor(deleted_doc)); + EXPECT_FALSE(update_time.IsValidFor(not_match)); + EXPECT_TRUE(update_time.IsValidFor(match)); +} + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/model/resource_path_test.cc b/Firestore/core/test/firebase/firestore/model/resource_path_test.cc index 637e78e..8545884 100644 --- a/Firestore/core/test/firebase/firestore/model/resource_path_test.cc +++ b/Firestore/core/test/firebase/firestore/model/resource_path_test.cc @@ -47,7 +47,7 @@ TEST(ResourcePath, Constructor) { EXPECT_EQ(path_from_list, copied); const ResourcePath moved = std::move(copied); EXPECT_EQ(path_from_list, moved); - EXPECT_NE(copied, moved); + EXPECT_NE(copied, moved); // NOLINT: use after move intended EXPECT_EQ(empty_path, copied); } diff --git a/Firestore/core/test/firebase/firestore/model/transform_operations_test.cc b/Firestore/core/test/firebase/firestore/model/transform_operations_test.cc new file mode 100644 index 0000000..ec0882a --- /dev/null +++ b/Firestore/core/test/firebase/firestore/model/transform_operations_test.cc @@ -0,0 +1,54 @@ +/* + * Copyright 2018 Google + * + * Licensed 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. + */ + +#include "Firestore/core/src/firebase/firestore/model/transform_operations.h" + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace model { + +class DummyOperation : public TransformOperation { + public: + DummyOperation() { + } + + Type type() const override { + return Type::Test; + } + + bool operator==(const TransformOperation& other) const override { + return this == &other; + } +}; + +TEST(TransformOperations, ServerTimestamp) { + ServerTimestampTransform transform = ServerTimestampTransform::Get(); + EXPECT_EQ(TransformOperation::Type::ServerTimestamp, transform.type()); + + ServerTimestampTransform another = ServerTimestampTransform::Get(); + DummyOperation dummy; + EXPECT_EQ(transform, another); + EXPECT_NE(transform, dummy); +} + +// TODO(mikelehen): Add ArrayTransform test once it no longer depends on +// FSTFieldValue and can be exposed to C++ code. + +} // namespace model +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/remote/datastore_test.cc b/Firestore/core/test/firebase/firestore/remote/datastore_test.cc index 42a9a8c..53e95a9 100644 --- a/Firestore/core/test/firebase/firestore/remote/datastore_test.cc +++ b/Firestore/core/test/firebase/firestore/remote/datastore_test.cc @@ -16,8 +16,9 @@ #include "Firestore/core/src/firebase/firestore/remote/datastore.h" -#include <grpc/grpc.h> -#include <gtest/gtest.h> +#include "grpc/grpc.h" + +#include "gtest/gtest.h" TEST(Datastore, CanLinkToGrpc) { // This test doesn't actually do anything interesting as far as actually diff --git a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc index 862ac97..addc830 100644 --- a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc +++ b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc @@ -42,17 +42,19 @@ #include <vector> #include "Firestore/core/src/firebase/firestore/model/field_value.h" +#include "Firestore/core/src/firebase/firestore/util/status.h" #include "gtest/gtest.h" using firebase::firestore::model::FieldValue; using firebase::firestore::remote::Serializer; +using firebase::firestore::util::Status; TEST(Serializer, CanLinkToNanopb) { // This test doesn't actually do anything interesting as far as actually using // nanopb is concerned but that it can run at all is proof that all the // libraries required for nanopb to work are actually linked correctly into // the test. - pb_ostream_from_buffer(NULL, 0); + pb_ostream_from_buffer(nullptr, 0); } // Fixture for running serializer tests. @@ -67,7 +69,8 @@ class SerializerTest : public ::testing::Test { FieldValue::Type type) { EXPECT_EQ(type, model.type()); std::vector<uint8_t> actual_bytes; - serializer.EncodeFieldValue(model, &actual_bytes); + Status status = serializer.EncodeFieldValue(model, &actual_bytes); + EXPECT_TRUE(status.ok()); EXPECT_EQ(bytes, actual_bytes); FieldValue actual_model = serializer.DecodeFieldValue(bytes); EXPECT_EQ(type, actual_model.type()); diff --git a/Firestore/core/test/firebase/firestore/testutil/testutil.h b/Firestore/core/test/firebase/firestore/testutil/testutil.h index 9c69784..9a875f4 100644 --- a/Firestore/core/test/firebase/firestore/testutil/testutil.h +++ b/Firestore/core/test/firebase/firestore/testutil/testutil.h @@ -17,9 +17,16 @@ #ifndef FIRESTORE_CORE_TEST_FIREBASE_FIRESTORE_TESTUTIL_TESTUTIL_H_ #define FIRESTORE_CORE_TEST_FIREBASE_FIRESTORE_TESTUTIL_TESTUTIL_H_ +#include <chrono> // NOLINT(build/c++11) +#include <cstdint> + +#include "Firestore/core/include/firebase/firestore/timestamp.h" +#include "Firestore/core/src/firebase/firestore/model/document.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/field_path.h" +#include "Firestore/core/src/firebase/firestore/model/no_document.h" #include "Firestore/core/src/firebase/firestore/model/resource_path.h" +#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" #include "absl/strings/string_view.h" namespace firebase { @@ -40,6 +47,28 @@ inline model::ResourcePath Resource(absl::string_view field) { return model::ResourcePath::FromString(field); } +/** + * Creates a snapshot version from the given version timestamp. + * + * @param version a timestamp in microseconds since the epoch. + */ +inline model::SnapshotVersion Version(int64_t version) { + namespace chr = std::chrono; + auto timepoint = + chr::time_point<chr::system_clock>(chr::microseconds(version)); + return model::SnapshotVersion{Timestamp::FromTimePoint(timepoint)}; +} + +inline model::Document Doc(absl::string_view key, int64_t version) { + return model::Document{model::FieldValue::ObjectValueFromMap({}), Key(key), + Version(version), + /* has_local_mutations= */ false}; +} + +inline model::NoDocument DeletedDoc(absl::string_view key, int64_t version) { + return model::NoDocument{Key(key), Version(version)}; +} + // Add a non-inline function to make this library buildable. // TODO(zxu123): remove once there is non-inline function. void dummy(); diff --git a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt index 13482b0..e5dbec5 100644 --- a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt @@ -17,26 +17,52 @@ set(CMAKE_CXX_EXTENSIONS ON) # Required to allow 0 length printf style strings for testing purposes. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format-zero-length") +## assert and log + +if(APPLE) + cc_test( + firebase_firestore_util_log_apple_test + SOURCES + assert_test.cc + log_test.cc + DEPENDS + firebase_firestore_util_log_apple + ) +endif(APPLE) + +cc_test( + firebase_firestore_util_log_stdio_test + SOURCES + assert_test.cc + log_test.cc + DEPENDS + firebase_firestore_util_log_stdio +) + +## secure random + if(HAVE_ARC4RANDOM) cc_test( - firebase_firestore_util_arc4random_test + firebase_firestore_util_random_arc4random_test SOURCES secure_random_test.cc DEPENDS - firebase_firestore_util_arc4random + firebase_firestore_util_random_arc4random ) endif() if(HAVE_OPENSSL_RAND_H) cc_test( - firebase_firestore_util_openssl_test + firebase_firestore_util_random_openssl_test SOURCES secure_random_test.cc DEPENDS - firebase_firestore_util_openssl + firebase_firestore_util_random_openssl ) endif() +## main library + cc_test( firebase_firestore_util_test SOURCES @@ -56,23 +82,3 @@ cc_test( firebase_firestore_util gmock ) - -if(APPLE) - cc_test( - firebase_firestore_util_apple_test - SOURCES - assert_test.cc - log_test.cc - DEPENDS - firebase_firestore_util_apple - ) -endif(APPLE) - -cc_test( - firebase_firestore_util_stdio_test - SOURCES - assert_test.cc - log_test.cc - DEPENDS - firebase_firestore_util_stdio -) diff --git a/Firestore/core/test/firebase/firestore/util/autoid_test.cc b/Firestore/core/test/firebase/firestore/util/autoid_test.cc index 079b990..808850b 100644 --- a/Firestore/core/test/firebase/firestore/util/autoid_test.cc +++ b/Firestore/core/test/firebase/firestore/util/autoid_test.cc @@ -16,9 +16,9 @@ #include "Firestore/core/src/firebase/firestore/util/autoid.h" -#include <ctype.h> +#include <cctype> -#include <gtest/gtest.h> +#include "gtest/gtest.h" using firebase::firestore::util::CreateAutoId; diff --git a/Firestore/core/test/firebase/firestore/util/bits_test.cc b/Firestore/core/test/firebase/firestore/util/bits_test.cc index cb0976b..572721f 100644 --- a/Firestore/core/test/firebase/firestore/util/bits_test.cc +++ b/Firestore/core/test/firebase/firestore/util/bits_test.cc @@ -69,16 +69,16 @@ TEST_F(BitsTest, Log2Random) { std::cout << "TestLog2Random" << std::endl; for (int i = 0; i < kNumIterations; i++) { - int maxbit = -1; + int max_bit = -1; uint32_t n = 0; while (!random_.OneIn(32u)) { int bit = static_cast<int>(random_.Uniform(32u)); n |= (1U << bit); - maxbit = std::max(bit, maxbit); + max_bit = std::max(bit, max_bit); } - EXPECT_EQ(maxbit, Bits::Log2Floor(n)); + EXPECT_EQ(max_bit, Bits::Log2Floor(n)); if (n != 0) { - EXPECT_EQ(maxbit, Bits::Log2FloorNonZero(n)); + EXPECT_EQ(max_bit, Bits::Log2FloorNonZero(n)); } } } @@ -87,16 +87,16 @@ TEST_F(BitsTest, Log2Random64) { std::cout << "TestLog2Random64" << std::endl; for (int i = 0; i < kNumIterations; i++) { - int maxbit = -1; + int max_bit = -1; uint64_t n = 0; while (!random_.OneIn(64u)) { int bit = static_cast<int>(random_.Uniform(64u)); n |= (1ULL << bit); - maxbit = std::max(bit, maxbit); + max_bit = std::max(bit, max_bit); } - EXPECT_EQ(maxbit, Bits::Log2Floor64(n)); + EXPECT_EQ(max_bit, Bits::Log2Floor64(n)); if (n != 0) { - EXPECT_EQ(maxbit, Bits::Log2FloorNonZero64(n)); + EXPECT_EQ(max_bit, Bits::Log2FloorNonZero64(n)); } } } diff --git a/Firestore/core/test/firebase/firestore/util/comparison_test.cc b/Firestore/core/test/firebase/firestore/util/comparison_test.cc index ccb3011..a03aec8 100644 --- a/Firestore/core/test/firebase/firestore/util/comparison_test.cc +++ b/Firestore/core/test/firebase/firestore/util/comparison_test.cc @@ -16,9 +16,8 @@ #include "Firestore/core/src/firebase/firestore/util/comparison.h" -#include <inttypes.h> -#include <math.h> - +#include <cinttypes> +#include <cmath> #include <limits> #include "Firestore/core/src/firebase/firestore/util/string_printf.h" diff --git a/Firestore/core/test/firebase/firestore/util/secure_random_test.cc b/Firestore/core/test/firebase/firestore/util/secure_random_test.cc index 0b7a51b..b0dfdd6 100644 --- a/Firestore/core/test/firebase/firestore/util/secure_random_test.cc +++ b/Firestore/core/test/firebase/firestore/util/secure_random_test.cc @@ -16,7 +16,7 @@ #include "Firestore/core/src/firebase/firestore/util/secure_random.h" -#include <gtest/gtest.h> +#include "gtest/gtest.h" using firebase::firestore::util::SecureRandom; diff --git a/Firestore/core/test/firebase/firestore/util/string_printf_test.cc b/Firestore/core/test/firebase/firestore/util/string_printf_test.cc index 085be84..14cc9c8 100644 --- a/Firestore/core/test/firebase/firestore/util/string_printf_test.cc +++ b/Firestore/core/test/firebase/firestore/util/string_printf_test.cc @@ -16,7 +16,7 @@ #include "Firestore/core/src/firebase/firestore/util/string_printf.h" -#include <gtest/gtest.h> +#include "gtest/gtest.h" namespace firebase { namespace firestore { diff --git a/Firestore/core/test/firebase/firestore/util/string_util_test.cc b/Firestore/core/test/firebase/firestore/util/string_util_test.cc index f94596f..a85c849 100644 --- a/Firestore/core/test/firebase/firestore/util/string_util_test.cc +++ b/Firestore/core/test/firebase/firestore/util/string_util_test.cc @@ -16,7 +16,7 @@ #include "Firestore/core/src/firebase/firestore/util/string_util.h" -#include <gtest/gtest.h> +#include "gtest/gtest.h" namespace firebase { namespace firestore { diff --git a/Functions/FirebaseFunctions/Public/FIRError.h b/Functions/FirebaseFunctions/Public/FIRError.h index 3048dee..5037f7b 100644 --- a/Functions/FirebaseFunctions/Public/FIRError.h +++ b/Functions/FirebaseFunctions/Public/FIRError.h @@ -86,6 +86,6 @@ typedef NS_ENUM(NSInteger, FIRFunctionsErrorCode) { FIRFunctionsErrorCodeDataLoss = 15, /** The request does not have valid authentication credentials for the operation. */ FIRFunctionsErrorCodeUnauthenticated = 16, -}; +} NS_SWIFT_NAME(FunctionsErrorCode); NS_ASSUME_NONNULL_END diff --git a/scripts/build.sh b/scripts/build.sh index 8841dc9..827fc14 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -119,6 +119,33 @@ case "$product-$method-$platform" in "${xcb_flags[@]}" \ build \ test + + # Test iOS Objective-C static library build + cd Example + sed -i -e 's/use_frameworks/\#use_frameworks/' Podfile + pod update --no-repo-update + # Workarounds for https://github.com/CocoaPods/CocoaPods/issues/7592. + # Remove when updating to CocoaPods 1.5.1 + sed -i -e 's/-l"FirebaseMessaging"//' "Pods/Target Support Files/Pods-Messaging_Tests_iOS/Pods-Messaging_Tests_iOS.debug.xcconfig" + sed -i -e 's/-l"FirebaseAuth-iOS" -l"FirebaseCore-iOS"//' "Pods/Target Support Files/Pods-Auth_Tests_iOS/Pods-Auth_Tests_iOS.debug.xcconfig" + cd .. + RunXcodebuild \ + -workspace 'Example/Firebase.xcworkspace' \ + -scheme "AllUnitTests_$platform" \ + "${xcb_flags[@]}" \ + build \ + test + + cd Functions/Example + sed -i -e 's/use_frameworks/\#use_frameworks/' Podfile + pod update --no-repo-update + cd ../.. + RunXcodebuild \ + -workspace 'Functions/Example/FirebaseFunctions.xcworkspace' \ + -scheme "FirebaseFunctions_Tests" \ + "${xcb_flags[@]}" \ + build \ + test fi ;; diff --git a/scripts/cpplint.py b/scripts/cpplint.py index d1c80b3..f09695c 100644 --- a/scripts/cpplint.py +++ b/scripts/cpplint.py @@ -428,6 +428,11 @@ _CPP_HEADERS = frozenset([ 'cwctype', ]) +_C_SYSTEM_DIRECTORIES = frozenset([ + 'libkern', + 'sys', +]) + # Type names _TYPES = re.compile( r'^(?:' @@ -685,7 +690,7 @@ def Search(pattern, s): def _IsSourceExtension(s): """File extension (excluding dot) matches a source file extension.""" - return s in ('c', 'cc', 'cpp', 'cxx') + return s in ('c', 'cc', 'cpp', 'cxx', 'm', 'mm') class _IncludeState(object): @@ -4411,7 +4416,7 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error) -_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') +_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*(?:include|import)\s*([<"])([^>"]*)[>"].*$') # Matches the first component of a filename delimited by -s and _s. That is: # _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' # _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' @@ -4444,6 +4449,11 @@ def _DropCommonSuffixes(filename): if (filename.endswith(suffix) and len(filename) > len(suffix) and filename[-len(suffix) - 1] in ('-', '_')): return filename[:-len(suffix) - 1] + + for suffix in ['Tests.h', 'Test.m', 'Test.mm', 'Tests.m', 'Tests.mm']: + if (filename.endswith(suffix) and len(filename) > len(suffix)): + return filename[:-len(suffix)] + return os.path.splitext(filename)[0] @@ -4524,6 +4534,30 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): fileinfo = FileInfo(filename) line = clean_lines.lines[linenum] + # system-style includes should not be used for project includes + match = Match(r'#include\s*<(([^/>]+)/[^>]+)', line) + if match: + if match.group(2) not in _C_SYSTEM_DIRECTORIES: + error(filename, linenum, 'build/include', 4, + '<%s> should be #include "%s" or #import <%s>' % + (match.group(1), match.group(1), match.group(1))) + + # C++ system files should not be #imported + match = Match(r'#import\s*<([^/>.]+)>', line) + if match: + error(filename, linenum, 'build/include', 4, + 'C++ header <%s> was #imported. Should be #include <%s>' % + (match.group(1), match.group(1))) + + # Prefer C++ wrappers for C headers + match = Match(r'#include\s*<(([^>]+).h)>', line) + if match: + wrapper = 'c' + match.group(2) + if wrapper in _CPP_HEADERS: + error(filename, linenum, 'build/include', 4, + 'Prefer C++ header <%s> for C system header %s' % + (wrapper, match.group(1))) + # "include" should use the new style "foo/bar.h" instead of just "bar.h" # Only do this check if the included header follows google naming # conventions. If not, assume that it's a 3rd party API that diff --git a/scripts/lint.sh b/scripts/lint.sh index d0f82b1..9e33c87 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -14,21 +14,74 @@ # Lints C++ files for conformance with the Google C++ style guide -options=( +# Joins the given arguments with the separator given as the first argument. +function join() { + local IFS="$1" + shift + echo "$*" +} + +git_options=( -z # \0 terminate output ) +objc_lint_filters=( + # Objective-C uses #import and does not use header guards + -build/header_guard + + # Inline definitions of Objective-C blocks confuse + -readability/braces + + # C-style casts are acceptable in Objective-C++ + -readability/casting + + # Objective-C needs use type 'long' for interop between types like NSInteger + # and printf-style functions. + -runtime/int + + # cpplint is generally confused by Objective-C mixing with C++. + # * Objective-C method invocations in a for loop make it think its a + # range-for + # * Objective-C dictionary literals confuse brace spacing + # * Empty category declarations ("@interface Foo ()") look like function + # invocations + -whitespace +) + +objc_lint_options=( + # cpplint normally excludes Objective-C++ + --extensions=h,m,mm + + # Objective-C style allows longer lines + --linelength=100 + + --filter=$(join , "${objc_lint_filters[@]}") +) + if [[ $# -gt 0 ]]; then # Interpret any command-line argument as a revision range command=(git diff --name-only) - options+=("$@") + git_options+=("$@") else # Default to operating on all files that match the pattern command=(git ls-files) fi - -"${command[@]}" "${options[@]}" \ +# Straight C++ files get regular cpplint +"${command[@]}" "${git_options[@]}" \ -- 'Firestore/core/**/*.'{h,cc} \ - | xargs -0 python scripts/cpplint.py --quiet + | xargs -0 python scripts/cpplint.py --quiet 2>&1 +CPP_STATUS=$? + +# Objective-C++ files get a looser cpplint +"${command[@]}" "${git_options[@]}" \ + -- 'Firestore/Source/**/*.'{h,m,mm} \ + 'Firestore/Example/Tests/**/*.'{h,m,mm} \ + 'Firestore/core/**/*.mm' \ + | xargs -0 python scripts/cpplint.py "${objc_lint_options[@]}" --quiet 2>&1 +OBJC_STATUS=$? + +if [[ $CPP_STATUS != 0 || $OBJC_STATUS != 0 ]]; then + exit 1 +fi |